148 Commits
3.0.3 ... 3.0.5

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

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

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

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

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

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

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

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

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

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

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

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

By unhardcoding:

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

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

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

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

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

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

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

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

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

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

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

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

Resolves: https://bugs.gentoo.org/show_bug.cgi?id=690840
2020-12-07 15:33:16 +01:00
4f88d38256 Correct the version of htop development repo 2020-12-07 19:57:44 +11:00
f03f48a0fb Change version string to note development repo build 2020-12-07 12:16:06 +11:00
116 changed files with 2041 additions and 1667 deletions

View File

@ -2,6 +2,11 @@ name: CI
on: [ push, pull_request ] 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: jobs:
build-ubuntu-latest-minimal-gcc: build-ubuntu-latest-minimal-gcc:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@ -47,6 +52,10 @@ jobs:
build-ubuntu-latest-full-featured-gcc: build-ubuntu-latest-full-featured-gcc:
runs-on: ubuntu-latest runs-on: ubuntu-latest
# Enable LTO, might trigger additional warnings on advanced inlining
env:
CFLAGS: -O3 -g -flto
LDFLAGS: -O3 -g -flto
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Install Dependencies - name: Install Dependencies
@ -102,6 +111,23 @@ jobs:
- name: Build - name: Build
run: scan-build-11 -analyze-headers --status-bugs make -j"$(nproc)" 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: whitespace_check:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:

View File

@ -6,17 +6,6 @@ compiler:
os: os:
- freebsd - freebsd
- linux
- osx
arch:
- amd64
- s390x
before_script:
if [[ ${TRAVIS_CPU_ARCH} == 's390x' ]]; then
sudo apt-get update && sudo apt-get install -y libncursesw5-dev ;
fi
script: script:
- ./autogen.sh - ./autogen.sh

158
Action.c
View File

@ -104,7 +104,7 @@ static bool changePriority(MainPanel* panel, int delta) {
return anyTagged; return anyTagged;
} }
static void addUserToVector(hkey_t key, void* userCast, void* panelCast) { static void addUserToVector(ht_key_t key, void* userCast, void* panelCast) {
const char* user = userCast; const char* user = userCast;
Panel* panel = panelCast; Panel* panel = panelCast;
Panel_add(panel, (Object*) ListItem_new(user, key)); Panel_add(panel, (Object*) ListItem_new(user, key));
@ -158,20 +158,21 @@ static bool collapseIntoParent(Panel* panel) {
} }
Htop_Reaction Action_setSortKey(Settings* settings, ProcessField sortKey) { Htop_Reaction Action_setSortKey(Settings* settings, ProcessField sortKey) {
settings->sortKey = sortKey; Settings_setSortKey(settings, sortKey);
settings->direction = 1;
return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_UPDATE_PANELHDR | HTOP_KEEP_FOLLOWING; return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_UPDATE_PANELHDR | HTOP_KEEP_FOLLOWING;
} }
static Htop_Reaction sortBy(State* st) { // ----------------------------------------
static Htop_Reaction actionSetSortColumn(State* st) {
Htop_Reaction reaction = HTOP_OK; Htop_Reaction reaction = HTOP_OK;
Panel* sortPanel = Panel_new(0, 0, 0, 0, true, Class(ListItem), FunctionBar_newEnterEsc("Sort ", "Cancel ")); Panel* sortPanel = Panel_new(0, 0, 0, 0, Class(ListItem), true, FunctionBar_newEnterEsc("Sort ", "Cancel "));
Panel_setHeader(sortPanel, "Sort by"); Panel_setHeader(sortPanel, "Sort by");
ProcessField* fields = st->settings->fields; ProcessField* fields = st->settings->fields;
for (int i = 0; fields[i]; i++) { for (int i = 0; fields[i]; i++) {
char* name = String_trim(Process_fields[fields[i]].name); char* name = String_trim(Process_fields[fields[i]].name);
Panel_add(sortPanel, (Object*) ListItem_new(name, fields[i])); Panel_add(sortPanel, (Object*) ListItem_new(name, fields[i]));
if (fields[i] == st->settings->sortKey) if (fields[i] == Settings_getActiveSortKey(st->settings))
Panel_setSelected(sortPanel, i); Panel_setSelected(sortPanel, i);
free(name); free(name);
@ -188,12 +189,8 @@ static Htop_Reaction sortBy(State* st) {
return reaction | HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR; 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 actionResize(State* st) {
clear();
Panel_resize(st->panel, COLS, LINES - (st->panel->y) - 1);
return HTOP_REDRAW_BAR;
} }
static Htop_Reaction actionSortByMemory(State* st) { static Htop_Reaction actionSortByMemory(State* st) {
@ -231,7 +228,7 @@ static Htop_Reaction actionToggleMergedCommand(State* st) {
static Htop_Reaction actionToggleTreeView(State* st) { static Htop_Reaction actionToggleTreeView(State* st) {
st->settings->treeView = !st->settings->treeView; st->settings->treeView = !st->settings->treeView;
if (st->settings->treeView) { if (st->settings->treeView) {
st->settings->direction = 1; st->settings->treeDirection = 1;
} }
ProcessList_expandTree(st->pl); ProcessList_expandTree(st->pl);
@ -251,16 +248,6 @@ static Htop_Reaction actionIncSearch(State* st) {
return HTOP_REFRESH | HTOP_KEEP_FOLLOWING; return HTOP_REFRESH | HTOP_KEEP_FOLLOWING;
} }
static Htop_Reaction actionIncNext(State* st) {
IncSet_next(((MainPanel*)st->panel)->inc, INC_SEARCH, st->panel, (IncMode_GetPanelValue) MainPanel_getValue);
return HTOP_REFRESH | HTOP_KEEP_FOLLOWING;
}
static Htop_Reaction actionIncPrev(State* st) {
IncSet_prev(((MainPanel*)st->panel)->inc, INC_SEARCH, st->panel, (IncMode_GetPanelValue) MainPanel_getValue);
return HTOP_REFRESH | HTOP_KEEP_FOLLOWING;
}
static Htop_Reaction actionHigherPriority(State* st) { static Htop_Reaction actionHigherPriority(State* st) {
bool changed = changePriority((MainPanel*)st->panel, -1); bool changed = changePriority((MainPanel*)st->panel, -1);
return changed ? HTOP_REFRESH : HTOP_OK; return changed ? HTOP_REFRESH : HTOP_OK;
@ -273,13 +260,11 @@ static Htop_Reaction actionLowerPriority(State* st) {
static Htop_Reaction actionInvertSortOrder(State* st) { static Htop_Reaction actionInvertSortOrder(State* st) {
Settings_invertSortOrder(st->settings); Settings_invertSortOrder(st->settings);
if (st->pauseProcessUpdate)
ProcessList_sort(st->pl);
return HTOP_REFRESH | HTOP_SAVE_SETTINGS; return HTOP_REFRESH | HTOP_SAVE_SETTINGS;
} }
static Htop_Reaction actionSetSortColumn(State* st) {
return sortBy(st);
}
static Htop_Reaction actionExpandOrCollapse(State* st) { static Htop_Reaction actionExpandOrCollapse(State* st) {
bool changed = expandCollapse(st->panel); bool changed = expandCollapse(st->panel);
return changed ? HTOP_RECALCULATE : HTOP_OK; return changed ? HTOP_RECALCULATE : HTOP_OK;
@ -340,7 +325,7 @@ static Htop_Reaction actionKill(State* st) {
if (sgn) { if (sgn) {
if (sgn->key != 0) { if (sgn->key != 0) {
Panel_setHeader(st->panel, "Sending..."); Panel_setHeader(st->panel, "Sending...");
Panel_draw(st->panel, true, true); Panel_draw(st->panel, false, true, true, State_hideFunctionBar(st));
refresh(); refresh();
MainPanel_foreachProcess((MainPanel*)st->panel, Process_sendSignal, (Arg) { .i = sgn->key }, NULL); MainPanel_foreachProcess((MainPanel*)st->panel, Process_sendSignal, (Arg) { .i = sgn->key }, NULL);
napms(500); napms(500);
@ -351,7 +336,7 @@ static Htop_Reaction actionKill(State* st) {
} }
static Htop_Reaction actionFilterByUser(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:"); Panel_setHeader(usersPanel, "Show processes of:");
UsersTable_foreach(st->ut, addUserToVector, usersPanel); UsersTable_foreach(st->ut, addUserToVector, usersPanel);
Vector_insertionSort(usersPanel->items); Vector_insertionSort(usersPanel->items);
@ -371,7 +356,7 @@ static Htop_Reaction actionFilterByUser(State* st) {
Htop_Reaction Action_follow(State* st) { Htop_Reaction Action_follow(State* st) {
st->pl->following = MainPanel_selectedPid((MainPanel*)st->panel); st->pl->following = MainPanel_selectedPid((MainPanel*)st->panel);
Panel_setSelectionColor(st->panel, CRT_colors[PANEL_SELECTION_FOLLOW]); Panel_setSelectionColor(st->panel, PANEL_SELECTION_FOLLOW);
return HTOP_KEEP_FOLLOWING; return HTOP_KEEP_FOLLOWING;
} }
@ -460,8 +445,8 @@ static const struct {
{ .key = " H: ", .info = "hide/show user process threads" }, { .key = " H: ", .info = "hide/show user process threads" },
{ .key = " K: ", .info = "hide/show kernel threads" }, { .key = " K: ", .info = "hide/show kernel threads" },
{ .key = " F: ", .info = "cursor follows process" }, { .key = " F: ", .info = "cursor follows process" },
{ .key = " F6 + -: ", .info = "expand/collapse tree" }, { .key = " + -: ", .info = "expand/collapse tree" },
{ .key = " P M T: ", .info = "sort by CPU%, MEM% or TIME" }, { .key = "N P M T: ", .info = "sort by PID, CPU%, MEM% or TIME" },
{ .key = " I: ", .info = "invert sort order" }, { .key = " I: ", .info = "invert sort order" },
{ .key = " F6 > .: ", .info = "select sort column" }, { .key = " F6 > .: ", .info = "select sort column" },
{ .key = NULL, .info = NULL } { .key = NULL, .info = NULL }
@ -562,24 +547,24 @@ static Htop_Reaction actionHelp(State* st) {
int item; int item;
for (item = 0; helpLeft[item].key; item++) { for (item = 0; helpLeft[item].key; item++) {
attrset(CRT_colors[DEFAULT_COLOR]); attrset(CRT_colors[DEFAULT_COLOR]);
mvaddstr(line + item, 9, helpLeft[item].info); mvaddstr(line + item, 10, helpLeft[item].info);
attrset(CRT_colors[HELP_BOLD]); attrset(CRT_colors[HELP_BOLD]);
mvaddstr(line + item, 0, helpLeft[item].key); mvaddstr(line + item, 1, helpLeft[item].key);
if (String_eq(helpLeft[item].key, " H: ")) { if (String_eq(helpLeft[item].key, " H: ")) {
attrset(CRT_colors[PROCESS_THREAD]); attrset(CRT_colors[PROCESS_THREAD]);
mvaddstr(line + item, 32, "threads"); mvaddstr(line + item, 33, "threads");
} else if (String_eq(helpLeft[item].key, " K: ")) { } else if (String_eq(helpLeft[item].key, " K: ")) {
attrset(CRT_colors[PROCESS_THREAD]); attrset(CRT_colors[PROCESS_THREAD]);
mvaddstr(line + item, 26, "threads"); mvaddstr(line + item, 27, "threads");
} }
} }
int leftHelpItems = item; int leftHelpItems = item;
for (item = 0; helpRight[item].key; item++) { for (item = 0; helpRight[item].key; item++) {
attrset(CRT_colors[HELP_BOLD]); attrset(CRT_colors[HELP_BOLD]);
mvaddstr(line + item, 40, helpRight[item].key); mvaddstr(line + item, 41, helpRight[item].key);
attrset(CRT_colors[DEFAULT_COLOR]); attrset(CRT_colors[DEFAULT_COLOR]);
mvaddstr(line + item, 49, helpRight[item].info); mvaddstr(line + item, 50, helpRight[item].info);
} }
line += MAXIMUM(leftHelpItems, item); line += MAXIMUM(leftHelpItems, item);
line++; line++;
@ -638,60 +623,57 @@ static Htop_Reaction actionShowCommandScreen(State* st) {
} }
void Action_setBindings(Htop_Action* keys) { 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['m'] = actionToggleMergedCommand;
keys['t'] = actionToggleTreeView;
keys[KEY_F(5)] = actionToggleTreeView;
keys[KEY_F(4)] = actionIncFilter;
keys['\\'] = actionIncFilter;
keys[KEY_F(3)] = actionIncSearch;
keys['/'] = actionIncSearch;
keys['n'] = actionIncNext;
keys['N'] = actionIncPrev;
keys[']'] = actionHigherPriority;
keys[KEY_F(7)] = actionHigherPriority;
keys['['] = actionLowerPriority;
keys[KEY_F(8)] = actionLowerPriority;
keys['I'] = actionInvertSortOrder;
keys[KEY_F(6)] = actionSetSortColumn;
keys[KEY_F(18)] = actionExpandCollapseOrSortColumn;
keys['<'] = actionSetSortColumn;
keys[','] = actionSetSortColumn;
keys['>'] = actionSetSortColumn;
keys['.'] = actionSetSortColumn;
keys[KEY_F(10)] = actionQuit;
keys['q'] = actionQuit;
keys['a'] = actionSetAffinity;
keys[KEY_F(9)] = actionKill;
keys['k'] = actionKill;
keys[KEY_RECLICK] = actionExpandOrCollapse;
keys['+'] = actionExpandOrCollapse;
keys['='] = actionExpandOrCollapse;
keys['-'] = actionExpandOrCollapse;
keys['\177'] = actionCollapseIntoParent;
keys['u'] = actionFilterByUser;
keys['F'] = Action_follow;
keys['S'] = actionSetup;
keys['C'] = actionSetup;
keys[KEY_F(2)] = actionSetup;
keys['x'] = actionShowLocks;
keys['l'] = actionLsof;
keys['s'] = actionStrace;
keys[' '] = actionTag; keys[' '] = actionTag;
keys['\014'] = actionRedraw; // Ctrl+L keys['+'] = actionExpandOrCollapse;
keys[KEY_F(1)] = actionHelp; keys[','] = actionSetSortColumn;
keys['h'] = actionHelp; keys['-'] = actionExpandOrCollapse;
keys['.'] = actionSetSortColumn;
keys['/'] = actionIncSearch;
keys['<'] = actionSetSortColumn;
keys['='] = actionExpandOrCollapse;
keys['>'] = actionSetSortColumn;
keys['?'] = actionHelp; 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['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['c'] = actionTagAllChildren;
keys['e'] = actionShowEnvScreen; 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['w'] = actionShowCommandScreen;
keys['Z'] = actionTogglePauseProcessUpdate; 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

@ -41,6 +41,10 @@ typedef struct State_ {
bool hideProcessSelection; bool hideProcessSelection;
} State; } State;
static inline bool State_hideFunctionBar(const State* st) {
return st->settings->hideFunctionBar == 2 || (st->settings->hideFunctionBar == 1 && st->hideProcessSelection);
}
typedef Htop_Reaction (*Htop_Action)(State* st); typedef Htop_Reaction (*Htop_Action)(State* st);
Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess); Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess);

View File

@ -59,25 +59,25 @@ static void MaskItem_delete(Object* cast) {
static void MaskItem_display(const Object* cast, RichString* out) { static void MaskItem_display(const Object* cast, RichString* out) {
const MaskItem* this = (const MaskItem*)cast; const MaskItem* this = (const MaskItem*)cast;
assert (this != NULL); assert (this != NULL);
RichString_append(out, CRT_colors[CHECK_BOX], "["); RichString_appendAscii(out, CRT_colors[CHECK_BOX], "[");
if (this->value == 2) { if (this->value == 2) {
RichString_append(out, CRT_colors[CHECK_MARK], "x"); RichString_appendAscii(out, CRT_colors[CHECK_MARK], "x");
} else if (this->value == 1) { } else if (this->value == 1) {
RichString_append(out, CRT_colors[CHECK_MARK], "o"); RichString_appendAscii(out, CRT_colors[CHECK_MARK], "o");
} else { } else {
RichString_append(out, CRT_colors[CHECK_MARK], " "); RichString_appendAscii(out, CRT_colors[CHECK_MARK], " ");
} }
RichString_append(out, CRT_colors[CHECK_BOX], "]"); RichString_appendAscii(out, CRT_colors[CHECK_BOX], "]");
RichString_append(out, CRT_colors[CHECK_TEXT], " "); RichString_appendAscii(out, CRT_colors[CHECK_TEXT], " ");
if (this->indent) { if (this->indent) {
RichString_append(out, CRT_colors[PROCESS_TREE], this->indent); RichString_appendWide(out, CRT_colors[PROCESS_TREE], this->indent);
RichString_append(out, CRT_colors[PROCESS_TREE], RichString_appendWide(out, CRT_colors[PROCESS_TREE],
this->sub_tree == 2 this->sub_tree == 2
? CRT_treeStr[TREE_STR_OPEN] ? CRT_treeStr[TREE_STR_OPEN]
: CRT_treeStr[TREE_STR_SHUT]); : CRT_treeStr[TREE_STR_SHUT]);
RichString_append(out, CRT_colors[CHECK_TEXT], " "); RichString_appendAscii(out, CRT_colors[CHECK_TEXT], " ");
} }
RichString_append(out, CRT_colors[CHECK_TEXT], this->text); RichString_appendWide(out, CRT_colors[CHECK_TEXT], this->text);
} }
static const ObjectClass MaskItem_class = { static const ObjectClass MaskItem_class = {
@ -173,7 +173,6 @@ static void AffinityPanel_update(AffinityPanel* this, bool keepSelected) {
Panel* super = (Panel*) this; Panel* super = (Panel*) this;
FunctionBar_setLabel(super->currentBar, KEY_F(3), this->topoView ? "Collapse/Expand" : ""); FunctionBar_setLabel(super->currentBar, KEY_F(3), this->topoView ? "Collapse/Expand" : "");
FunctionBar_draw(super->currentBar);
int oldSelected = Panel_getSelectedIndex(super); int oldSelected = Panel_getSelectedIndex(super);
Panel_prune(super); Panel_prune(super);
@ -281,7 +280,7 @@ static MaskItem* AffinityPanel_addObject(AffinityPanel* this, hwloc_obj_t obj, u
indent_buf[0] = '\0'; indent_buf[0] = '\0';
if (depth > 0) { if (depth > 0) {
for (unsigned i = 1; i < depth; i++) { for (unsigned i = 1; i < depth; i++) {
xSnprintf(&indent_buf[off], left, "%s ", (indent & (1u << i)) ? CRT_treeStr[TREE_STR_VERT] : " "); xSnprintf(&indent_buf[off], left, "%s ", (indent & (1U << i)) ? CRT_treeStr[TREE_STR_VERT] : " ");
size_t len = strlen(&indent_buf[off]); size_t len = strlen(&indent_buf[off]);
off += len; off += len;
left -= len; left -= len;
@ -323,9 +322,9 @@ static MaskItem* AffinityPanel_addObject(AffinityPanel* this, hwloc_obj_t obj, u
static MaskItem* AffinityPanel_buildTopology(AffinityPanel* this, hwloc_obj_t obj, unsigned indent, MaskItem* parent) { static MaskItem* AffinityPanel_buildTopology(AffinityPanel* this, hwloc_obj_t obj, unsigned indent, MaskItem* parent) {
MaskItem* item = AffinityPanel_addObject(this, obj, indent, parent); MaskItem* item = AffinityPanel_addObject(this, obj, indent, parent);
if (obj->next_sibling) { if (obj->next_sibling) {
indent |= (1u << obj->depth); indent |= (1U << obj->depth);
} else { } else {
indent &= ~(1u << obj->depth); indent &= ~(1U << obj->depth);
} }
for (unsigned i = 0; i < obj->arity; i++) { for (unsigned i = 0; i < obj->arity; i++) {

View File

@ -77,7 +77,7 @@ AvailableColumnsPanel* AvailableColumnsPanel_new(Panel* columns) {
Panel_setHeader(super, "Available 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) { if (i != COMM && Process_fields[i].description) {
char description[256]; char description[256];
xSnprintf(description, sizeof(description), "%s - %s", Process_fields[i].name, Process_fields[i].description); xSnprintf(description, sizeof(description), "%s - %s", Process_fields[i].name, Process_fields[i].description);

View File

@ -35,7 +35,6 @@ static inline void AvailableMetersPanel_addMeter(Header* header, Panel* panel, c
Panel_add(panel, (Object*) Meter_toListItem(meter, false)); Panel_add(panel, (Object*) Meter_toListItem(meter, false));
Panel_setSelected(panel, Panel_size(panel) - 1); Panel_setSelected(panel, Panel_size(panel) - 1);
MetersPanel_setMoving((MetersPanel*)panel, true); MetersPanel_setMoving((MetersPanel*)panel, true);
FunctionBar_draw(panel->currentBar);
} }
static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) { static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) {

View File

@ -35,24 +35,21 @@ static void BatteryMeter_updateValues(Meter* this, char* buffer, size_t len) {
this->values[0] = percent; this->values[0] = percent;
const char *onAcText, *onBatteryText, *unknownText; const char* text;
switch (isOnAC) {
unknownText = "%.1f%%"; case AC_PRESENT:
if (this->mode == TEXT_METERMODE) { text = this->mode == TEXT_METERMODE ? " (Running on A/C)" : "(A/C)";
onAcText = "%.1f%% (Running on A/C)"; break;
onBatteryText = "%.1f%% (Running on battery)"; case AC_ABSENT:
} else { text = this->mode == TEXT_METERMODE ? " (Running on battery)" : "(bat)";
onAcText = "%.1f%%(A/C)"; break;
onBatteryText = "%.1f%%(bat)"; case AC_ERROR:
default:
text = "";
break;
} }
if (isOnAC == AC_PRESENT) { xSnprintf(buffer, len, "%.1f%%%s", percent, text);
xSnprintf(buffer, len, onAcText, percent);
} else if (isOnAC == AC_ABSENT) {
xSnprintf(buffer, len, onBatteryText, percent);
} else {
xSnprintf(buffer, len, unknownText, percent);
}
} }
const MeterClass BatteryMeter_class = { const MeterClass BatteryMeter_class = {

View File

@ -67,7 +67,7 @@ static void CPUMeter_updateValues(Meter* this, char* buffer, size_t size) {
double percent = Platform_setCPUValues(this, cpu); double percent = Platform_setCPUValues(this, cpu);
if (this->pl->settings->showCPUUsage) { if (this->pl->settings->showCPUUsage) {
xSnprintf(cpuUsageBuffer, sizeof(cpuUsageBuffer), "%5.1f%%", percent); xSnprintf(cpuUsageBuffer, sizeof(cpuUsageBuffer), "%.1f%%", percent);
} }
if (this->pl->settings->showCPUFrequency) { if (this->pl->settings->showCPUFrequency) {
@ -104,49 +104,49 @@ static void CPUMeter_display(const Object* cast, RichString* out) {
const Meter* this = (const Meter*)cast; const Meter* this = (const Meter*)cast;
RichString_prune(out); RichString_prune(out);
if (this->param > this->pl->cpuCount) { if (this->param > this->pl->cpuCount) {
RichString_append(out, CRT_colors[METER_TEXT], "absent"); RichString_appendAscii(out, CRT_colors[METER_TEXT], "absent");
return; return;
} }
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_NORMAL]); xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_NORMAL]);
RichString_append(out, CRT_colors[METER_TEXT], ":"); RichString_appendAscii(out, CRT_colors[METER_TEXT], ":");
RichString_append(out, CRT_colors[CPU_NORMAL], buffer); RichString_appendAscii(out, CRT_colors[CPU_NORMAL], buffer);
if (this->pl->settings->detailedCPUTime) { if (this->pl->settings->detailedCPUTime) {
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_KERNEL]); xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_KERNEL]);
RichString_append(out, CRT_colors[METER_TEXT], "sy:"); RichString_appendAscii(out, CRT_colors[METER_TEXT], "sy:");
RichString_append(out, CRT_colors[CPU_SYSTEM], buffer); RichString_appendAscii(out, CRT_colors[CPU_SYSTEM], buffer);
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_NICE]); xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_NICE]);
RichString_append(out, CRT_colors[METER_TEXT], "ni:"); RichString_appendAscii(out, CRT_colors[METER_TEXT], "ni:");
RichString_append(out, CRT_colors[CPU_NICE_TEXT], buffer); RichString_appendAscii(out, CRT_colors[CPU_NICE_TEXT], buffer);
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_IRQ]); xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_IRQ]);
RichString_append(out, CRT_colors[METER_TEXT], "hi:"); RichString_appendAscii(out, CRT_colors[METER_TEXT], "hi:");
RichString_append(out, CRT_colors[CPU_IRQ], buffer); RichString_appendAscii(out, CRT_colors[CPU_IRQ], buffer);
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_SOFTIRQ]); xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_SOFTIRQ]);
RichString_append(out, CRT_colors[METER_TEXT], "si:"); RichString_appendAscii(out, CRT_colors[METER_TEXT], "si:");
RichString_append(out, CRT_colors[CPU_SOFTIRQ], buffer); RichString_appendAscii(out, CRT_colors[CPU_SOFTIRQ], buffer);
if (!isnan(this->values[CPU_METER_STEAL])) { if (!isnan(this->values[CPU_METER_STEAL])) {
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_STEAL]); xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_STEAL]);
RichString_append(out, CRT_colors[METER_TEXT], "st:"); RichString_appendAscii(out, CRT_colors[METER_TEXT], "st:");
RichString_append(out, CRT_colors[CPU_STEAL], buffer); RichString_appendAscii(out, CRT_colors[CPU_STEAL], buffer);
} }
if (!isnan(this->values[CPU_METER_GUEST])) { if (!isnan(this->values[CPU_METER_GUEST])) {
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_GUEST]); xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_GUEST]);
RichString_append(out, CRT_colors[METER_TEXT], "gu:"); RichString_appendAscii(out, CRT_colors[METER_TEXT], "gu:");
RichString_append(out, CRT_colors[CPU_GUEST], buffer); RichString_appendAscii(out, CRT_colors[CPU_GUEST], buffer);
} }
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_IOWAIT]); xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_IOWAIT]);
RichString_append(out, CRT_colors[METER_TEXT], "wa:"); RichString_appendAscii(out, CRT_colors[METER_TEXT], "wa:");
RichString_append(out, CRT_colors[CPU_IOWAIT], buffer); RichString_appendAscii(out, CRT_colors[CPU_IOWAIT], buffer);
} else { } else {
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_KERNEL]); xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_KERNEL]);
RichString_append(out, CRT_colors[METER_TEXT], "sys:"); RichString_appendAscii(out, CRT_colors[METER_TEXT], "sys:");
RichString_append(out, CRT_colors[CPU_SYSTEM], buffer); RichString_appendAscii(out, CRT_colors[CPU_SYSTEM], buffer);
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_NICE]); xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_NICE]);
RichString_append(out, CRT_colors[METER_TEXT], "low:"); RichString_appendAscii(out, CRT_colors[METER_TEXT], "low:");
RichString_append(out, CRT_colors[CPU_NICE_TEXT], buffer); RichString_appendAscii(out, CRT_colors[CPU_NICE_TEXT], buffer);
if (!isnan(this->values[CPU_METER_IRQ])) { if (!isnan(this->values[CPU_METER_IRQ])) {
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_IRQ]); xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_IRQ]);
RichString_append(out, CRT_colors[METER_TEXT], "vir:"); RichString_appendAscii(out, CRT_colors[METER_TEXT], "vir:");
RichString_append(out, CRT_colors[CPU_GUEST], buffer); RichString_appendAscii(out, CRT_colors[CPU_GUEST], buffer);
} }
} }
@ -161,8 +161,8 @@ static void CPUMeter_display(const Object* cast, RichString* out) {
} else { } else {
xSnprintf(cpuTemperatureBuffer, sizeof(cpuTemperatureBuffer), "%5.1f%sC", cpuTemperature, CRT_degreeSign); xSnprintf(cpuTemperatureBuffer, sizeof(cpuTemperatureBuffer), "%5.1f%sC", cpuTemperature, CRT_degreeSign);
} }
RichString_append(out, CRT_colors[METER_TEXT], "temp:"); RichString_appendAscii(out, CRT_colors[METER_TEXT], "temp:");
RichString_append(out, CRT_colors[METER_VALUE], cpuTemperatureBuffer); RichString_appendWide(out, CRT_colors[METER_VALUE], cpuTemperatureBuffer);
} }
#endif #endif
} }

138
CRT.c
View File

@ -41,28 +41,33 @@ in the source distribution for its full text.
#define ColorPairGrayBlack ColorPair(Magenta,Magenta) #define ColorPairGrayBlack ColorPair(Magenta,Magenta)
#define ColorIndexGrayBlack ColorIndex(Magenta,Magenta) #define ColorIndexGrayBlack ColorIndex(Magenta,Magenta)
static const char* const CRT_treeStrAscii[TREE_STR_COUNT] = { #define ColorPairWhiteDefault ColorPair(Red, Red)
"-", // TREE_STR_HORZ #define ColorIndexWhiteDefault ColorIndex(Red, Red)
"|", // TREE_STR_VERT
"`", // TREE_STR_RTEE static const char* const CRT_treeStrAscii[LAST_TREE_STR] = {
"`", // TREE_STR_BEND [TREE_STR_VERT] = "|",
",", // TREE_STR_TEND [TREE_STR_RTEE] = "`",
"+", // TREE_STR_OPEN [TREE_STR_BEND] = "`",
"-", // TREE_STR_SHUT [TREE_STR_TEND] = ",",
[TREE_STR_OPEN] = "+",
[TREE_STR_SHUT] = "-",
[TREE_STR_ASC] = "+",
[TREE_STR_DESC] = "-",
}; };
#ifdef HAVE_LIBNCURSESW #ifdef HAVE_LIBNCURSESW
static const char* const CRT_treeStrUtf8[TREE_STR_COUNT] = { static const char* const CRT_treeStrUtf8[LAST_TREE_STR] = {
"\xe2\x94\x80", // TREE_STR_HORZ ─ [TREE_STR_VERT] = "\xe2\x94\x82", //
"\xe2\x94\x82", // TREE_STR_VERT │ [TREE_STR_RTEE] = "\xe2\x94\x9c", //
"\xe2\x94\x9c", // TREE_STR_RTEE ├ [TREE_STR_BEND] = "\xe2\x94\x94", //
"\xe2\x94\x94", // TREE_STR_BEND └ [TREE_STR_TEND] = "\xe2\x94\x8c", //
"\xe2\x94\x8c", // TREE_STR_TEND ┌ [TREE_STR_OPEN] = "+", // +, TODO use 🮯 'BOX DRAWINGS LIGHT HORIZONTAL
"+", // TREE_STR_OPEN +, TODO use 🮯 'BOX DRAWINGS LIGHT HORIZONTAL // WITH VERTICAL STROKE' (U+1FBAF, "\xf0\x9f\xae\xaf") when
// WITH VERTICAL STROKE' (U+1FBAF, "\xf0\x9f\xae\xaf") when // Unicode 13 is common
// Unicode 13 is common [TREE_STR_SHUT] = "\xe2\x94\x80", // ─
"\xe2\x94\x80", // TREE_STR_SHUT ─ [TREE_STR_ASC] = "\xe2\x96\xb3", //
[TREE_STR_DESC] = "\xe2\x96\xbd", // ▽
}; };
bool CRT_utf8 = false; bool CRT_utf8 = false;
@ -92,7 +97,7 @@ static const char* initDegreeSign(void) {
const int* CRT_colors; const int* CRT_colors;
int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[COLORSCHEME_DEFAULT] = { [COLORSCHEME_DEFAULT] = {
[RESET_COLOR] = ColorPair(White, Black), [RESET_COLOR] = ColorPair(White, Black),
[DEFAULT_COLOR] = ColorPair(White, Black), [DEFAULT_COLOR] = ColorPair(White, Black),
@ -116,6 +121,7 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[METER_VALUE_IOWRITE] = ColorPair(Blue, Black), [METER_VALUE_IOWRITE] = ColorPair(Blue, Black),
[METER_VALUE_NOTICE] = A_BOLD | ColorPair(White, Black), [METER_VALUE_NOTICE] = A_BOLD | ColorPair(White, Black),
[METER_VALUE_OK] = ColorPair(Green, Black), [METER_VALUE_OK] = ColorPair(Green, Black),
[METER_VALUE_WARN] = A_BOLD | ColorPair(Yellow, Black),
[LED_COLOR] = ColorPair(Green, Black), [LED_COLOR] = ColorPair(Green, Black),
[TASKS_RUNNING] = A_BOLD | ColorPair(Green, Black), [TASKS_RUNNING] = A_BOLD | ColorPair(Green, Black),
[PROCESS] = A_NORMAL, [PROCESS] = A_NORMAL,
@ -200,6 +206,7 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[METER_VALUE_IOWRITE] = A_NORMAL, [METER_VALUE_IOWRITE] = A_NORMAL,
[METER_VALUE_NOTICE] = A_BOLD, [METER_VALUE_NOTICE] = A_BOLD,
[METER_VALUE_OK] = A_NORMAL, [METER_VALUE_OK] = A_NORMAL,
[METER_VALUE_WARN] = A_BOLD,
[LED_COLOR] = A_NORMAL, [LED_COLOR] = A_NORMAL,
[TASKS_RUNNING] = A_BOLD, [TASKS_RUNNING] = A_BOLD,
[PROCESS] = A_NORMAL, [PROCESS] = A_NORMAL,
@ -284,6 +291,7 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[METER_VALUE_IOWRITE] = ColorPair(Yellow, White), [METER_VALUE_IOWRITE] = ColorPair(Yellow, White),
[METER_VALUE_NOTICE] = A_BOLD | ColorPair(Yellow, White), [METER_VALUE_NOTICE] = A_BOLD | ColorPair(Yellow, White),
[METER_VALUE_OK] = ColorPair(Green, White), [METER_VALUE_OK] = ColorPair(Green, White),
[METER_VALUE_WARN] = A_BOLD | ColorPair(Yellow, White),
[LED_COLOR] = ColorPair(Green, White), [LED_COLOR] = ColorPair(Green, White),
[TASKS_RUNNING] = ColorPair(Green, White), [TASKS_RUNNING] = ColorPair(Green, White),
[PROCESS] = ColorPair(Black, White), [PROCESS] = ColorPair(Black, White),
@ -346,10 +354,10 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[ZRAM] = ColorPair(Yellow, White) [ZRAM] = ColorPair(Yellow, White)
}, },
[COLORSCHEME_LIGHTTERMINAL] = { [COLORSCHEME_LIGHTTERMINAL] = {
[RESET_COLOR] = ColorPair(Blue, Black), [RESET_COLOR] = ColorPair(Black, Black),
[DEFAULT_COLOR] = ColorPair(Blue, Black), [DEFAULT_COLOR] = ColorPair(Black, Black),
[FUNCTION_BAR] = ColorPair(Black, Cyan), [FUNCTION_BAR] = ColorPair(Black, Cyan),
[FUNCTION_KEY] = ColorPair(Blue, Black), [FUNCTION_KEY] = ColorPair(Black, Black),
[PANEL_HEADER_FOCUS] = ColorPair(Black, Green), [PANEL_HEADER_FOCUS] = ColorPair(Black, Green),
[PANEL_HEADER_UNFOCUS] = ColorPair(Black, Green), [PANEL_HEADER_UNFOCUS] = ColorPair(Black, Green),
[PANEL_SELECTION_FOCUS] = ColorPair(Black, Cyan), [PANEL_SELECTION_FOCUS] = ColorPair(Black, Cyan),
@ -362,17 +370,18 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[BATTERY] = ColorPair(Yellow, Black), [BATTERY] = ColorPair(Yellow, Black),
[LARGE_NUMBER] = ColorPair(Red, Black), [LARGE_NUMBER] = ColorPair(Red, Black),
[METER_TEXT] = ColorPair(Blue, Black), [METER_TEXT] = ColorPair(Blue, Black),
[METER_VALUE] = ColorPair(Blue, Black), [METER_VALUE] = ColorPair(Black, Black),
[METER_VALUE_ERROR] = A_BOLD | ColorPair(Red, Black), [METER_VALUE_ERROR] = A_BOLD | ColorPair(Red, Black),
[METER_VALUE_IOREAD] = ColorPair(Green, Black), [METER_VALUE_IOREAD] = ColorPair(Green, Black),
[METER_VALUE_IOWRITE] = ColorPair(Yellow, Black), [METER_VALUE_IOWRITE] = ColorPair(Yellow, Black),
[METER_VALUE_NOTICE] = A_BOLD | ColorPair(Yellow, Black), [METER_VALUE_NOTICE] = A_BOLD | ColorPairWhiteDefault,
[METER_VALUE_OK] = ColorPair(Green, Black), [METER_VALUE_OK] = ColorPair(Green, Black),
[METER_VALUE_WARN] = A_BOLD | ColorPair(Yellow, Black),
[LED_COLOR] = ColorPair(Green, Black), [LED_COLOR] = ColorPair(Green, Black),
[TASKS_RUNNING] = ColorPair(Green, Black), [TASKS_RUNNING] = ColorPair(Green, Black),
[PROCESS] = ColorPair(Blue, Black), [PROCESS] = ColorPair(Black, Black),
[PROCESS_SHADOW] = A_BOLD | ColorPairGrayBlack, [PROCESS_SHADOW] = A_BOLD | ColorPairGrayBlack,
[PROCESS_TAG] = ColorPair(Yellow, Blue), [PROCESS_TAG] = ColorPair(White, Blue),
[PROCESS_MEGABYTES] = ColorPair(Blue, Black), [PROCESS_MEGABYTES] = ColorPair(Blue, Black),
[PROCESS_GIGABYTES] = ColorPair(Green, Black), [PROCESS_GIGABYTES] = ColorPair(Green, Black),
[PROCESS_BASENAME] = ColorPair(Green, Black), [PROCESS_BASENAME] = ColorPair(Green, Black),
@ -396,34 +405,34 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[MEMORY_BUFFERS] = ColorPair(Cyan, Black), [MEMORY_BUFFERS] = ColorPair(Cyan, Black),
[MEMORY_BUFFERS_TEXT] = ColorPair(Cyan, Black), [MEMORY_BUFFERS_TEXT] = ColorPair(Cyan, Black),
[MEMORY_CACHE] = ColorPair(Yellow, Black), [MEMORY_CACHE] = ColorPair(Yellow, Black),
[LOAD_AVERAGE_FIFTEEN] = ColorPair(Blue, Black), [LOAD_AVERAGE_FIFTEEN] = ColorPair(Black, Black),
[LOAD_AVERAGE_FIVE] = ColorPair(Blue, Black), [LOAD_AVERAGE_FIVE] = ColorPair(Black, Black),
[LOAD_AVERAGE_ONE] = ColorPair(Yellow, Black), [LOAD_AVERAGE_ONE] = ColorPair(Black, Black),
[LOAD] = ColorPair(Yellow, Black), [LOAD] = ColorPairWhiteDefault,
[HELP_BOLD] = ColorPair(Blue, Black), [HELP_BOLD] = ColorPair(Blue, Black),
[CLOCK] = ColorPair(Yellow, Black), [CLOCK] = ColorPairWhiteDefault,
[DATE] = ColorPair(White, Black), [DATE] = ColorPairWhiteDefault,
[DATETIME] = ColorPair(White, Black), [DATETIME] = ColorPairWhiteDefault,
[CHECK_BOX] = ColorPair(Blue, Black), [CHECK_BOX] = ColorPair(Blue, Black),
[CHECK_MARK] = ColorPair(Blue, Black), [CHECK_MARK] = ColorPair(Black, Black),
[CHECK_TEXT] = ColorPair(Blue, Black), [CHECK_TEXT] = ColorPair(Black, Black),
[HOSTNAME] = ColorPair(Yellow, Black), [HOSTNAME] = ColorPairWhiteDefault,
[CPU_NICE] = ColorPair(Cyan, Black), [CPU_NICE] = ColorPair(Cyan, Black),
[CPU_NICE_TEXT] = ColorPair(Cyan, Black), [CPU_NICE_TEXT] = ColorPair(Cyan, Black),
[CPU_NORMAL] = ColorPair(Green, Black), [CPU_NORMAL] = ColorPair(Green, Black),
[CPU_SYSTEM] = ColorPair(Red, Black), [CPU_SYSTEM] = ColorPair(Red, Black),
[CPU_IOWAIT] = A_BOLD | ColorPair(Blue, Black), [CPU_IOWAIT] = A_BOLD | ColorPair(Black, Black),
[CPU_IRQ] = A_BOLD | ColorPair(Blue, Black), [CPU_IRQ] = A_BOLD | ColorPair(Blue, Black),
[CPU_SOFTIRQ] = ColorPair(Blue, Black), [CPU_SOFTIRQ] = ColorPair(Blue, Black),
[CPU_STEAL] = ColorPair(Blue, Black), [CPU_STEAL] = ColorPair(Black, Black),
[CPU_GUEST] = ColorPair(Blue, Black), [CPU_GUEST] = ColorPair(Black, Black),
[PRESSURE_STALL_THREEHUNDRED] = ColorPair(Blue, Black), [PRESSURE_STALL_THREEHUNDRED] = ColorPair(Black, Black),
[PRESSURE_STALL_SIXTY] = ColorPair(Blue, Black), [PRESSURE_STALL_SIXTY] = ColorPair(Black, Black),
[PRESSURE_STALL_TEN] = ColorPair(Blue, Black), [PRESSURE_STALL_TEN] = ColorPair(Black, Black),
[ZFS_MFU] = ColorPair(Cyan, Black), [ZFS_MFU] = ColorPair(Cyan, Black),
[ZFS_MRU] = ColorPair(Yellow, Black), [ZFS_MRU] = ColorPair(Yellow, Black),
[ZFS_ANON] = A_BOLD | ColorPair(Magenta, Black), [ZFS_ANON] = A_BOLD | ColorPair(Magenta, Black),
[ZFS_HEADER] = ColorPair(Blue, Black), [ZFS_HEADER] = ColorPair(Black, Black),
[ZFS_OTHER] = A_BOLD | ColorPair(Magenta, Black), [ZFS_OTHER] = A_BOLD | ColorPair(Magenta, Black),
[ZFS_COMPRESSED] = ColorPair(Cyan, Black), [ZFS_COMPRESSED] = ColorPair(Cyan, Black),
[ZFS_RATIO] = A_BOLD | ColorPair(Magenta, Black), [ZFS_RATIO] = A_BOLD | ColorPair(Magenta, Black),
@ -452,6 +461,7 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[METER_VALUE_IOWRITE] = ColorPair(Black, Blue), [METER_VALUE_IOWRITE] = ColorPair(Black, Blue),
[METER_VALUE_NOTICE] = A_BOLD | ColorPair(White, Blue), [METER_VALUE_NOTICE] = A_BOLD | ColorPair(White, Blue),
[METER_VALUE_OK] = ColorPair(Green, Blue), [METER_VALUE_OK] = ColorPair(Green, Blue),
[METER_VALUE_WARN] = A_BOLD | ColorPair(Yellow, Black),
[LED_COLOR] = ColorPair(Green, Blue), [LED_COLOR] = ColorPair(Green, Blue),
[TASKS_RUNNING] = A_BOLD | ColorPair(Green, Blue), [TASKS_RUNNING] = A_BOLD | ColorPair(Green, Blue),
[PROCESS] = ColorPair(White, Blue), [PROCESS] = ColorPair(White, Blue),
@ -534,8 +544,9 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[METER_VALUE_ERROR] = A_BOLD | ColorPair(Red, Black), [METER_VALUE_ERROR] = A_BOLD | ColorPair(Red, Black),
[METER_VALUE_IOREAD] = ColorPair(Green, Black), [METER_VALUE_IOREAD] = ColorPair(Green, Black),
[METER_VALUE_IOWRITE] = ColorPair(Blue, Black), [METER_VALUE_IOWRITE] = ColorPair(Blue, Black),
[METER_VALUE_NOTICE] = A_BOLD | ColorPair(Yellow, Black), [METER_VALUE_NOTICE] = A_BOLD | ColorPair(White, Black),
[METER_VALUE_OK] = ColorPair(Green, Black), [METER_VALUE_OK] = ColorPair(Green, Black),
[METER_VALUE_WARN] = A_BOLD | ColorPair(Yellow, Black),
[LED_COLOR] = ColorPair(Green, Black), [LED_COLOR] = ColorPair(Green, Black),
[TASKS_RUNNING] = A_BOLD | ColorPair(Green, Black), [TASKS_RUNNING] = A_BOLD | ColorPair(Green, Black),
[PROCESS] = ColorPair(Cyan, Black), [PROCESS] = ColorPair(Cyan, Black),
@ -604,12 +615,7 @@ int CRT_scrollHAmount = 5;
int CRT_scrollWheelVAmount = 10; int CRT_scrollWheelVAmount = 10;
const char* CRT_termType; ColorScheme CRT_colorScheme = COLORSCHEME_DEFAULT;
int CRT_colorScheme = 0;
long CRT_pageSize = -1;
long CRT_pageSizeKB = -1;
ATTR_NORETURN ATTR_NORETURN
static void CRT_handleSIGTERM(int sgn) { static void CRT_handleSIGTERM(int sgn) {
@ -676,14 +682,14 @@ void CRT_init(const int* delay, int colorScheme, bool allowUnicode) {
start_color(); start_color();
} }
CRT_termType = getenv("TERM"); const char* termType = getenv("TERM");
if (String_eq(CRT_termType, "linux")) { if (termType && String_eq(termType, "linux")) {
CRT_scrollHAmount = 20; CRT_scrollHAmount = 20;
} else { } else {
CRT_scrollHAmount = 5; CRT_scrollHAmount = 5;
} }
if (String_startsWith(CRT_termType, "xterm") || String_eq(CRT_termType, "vt220")) { if (termType && (String_startsWith(termType, "xterm") || String_eq(termType, "vt220"))) {
define_key("\033[H", KEY_HOME); define_key("\033[H", KEY_HOME);
define_key("\033[F", KEY_END); define_key("\033[F", KEY_END);
define_key("\033[7~", KEY_HOME); define_key("\033[7~", KEY_HOME);
@ -696,6 +702,7 @@ void CRT_init(const int* delay, int colorScheme, bool allowUnicode) {
define_key("\033[12~", KEY_F(2)); define_key("\033[12~", KEY_F(2));
define_key("\033[13~", KEY_F(3)); define_key("\033[13~", KEY_F(3));
define_key("\033[14~", KEY_F(4)); define_key("\033[14~", KEY_F(4));
define_key("\033[14;2~", KEY_F(15));
define_key("\033[17;2~", KEY_F(18)); define_key("\033[17;2~", KEY_F(18));
char sequence[3] = "\033a"; char sequence[3] = "\033a";
for (char c = 'a'; c <= 'z'; c++) { for (char c = 'a'; c <= 'z'; c++) {
@ -746,11 +753,6 @@ void CRT_init(const int* delay, int colorScheme, bool allowUnicode) {
mousemask(BUTTON1_RELEASED, NULL); mousemask(BUTTON1_RELEASED, NULL);
#endif #endif
CRT_pageSize = sysconf(_SC_PAGESIZE);
if (CRT_pageSize == -1)
CRT_fatalError("Fatal error: Can not get PAGE_SIZE by sysconf(_SC_PAGESIZE)");
CRT_pageSizeKB = CRT_pageSize / 1024;
CRT_degreeSign = initDegreeSign(); CRT_degreeSign = initDegreeSign();
} }
@ -760,7 +762,7 @@ void CRT_done() {
} }
void CRT_fatalError(const char* note) { void CRT_fatalError(const char* note) {
char* sysMsg = strerror(errno); const char* sysMsg = strerror(errno);
CRT_done(); CRT_done();
fprintf(stderr, "%s: %s\n", note, sysMsg); fprintf(stderr, "%s: %s\n", note, sysMsg);
exit(2); exit(2);
@ -790,7 +792,7 @@ void CRT_setColors(int colorScheme) {
for (short int i = 0; i < 8; i++) { for (short int i = 0; i < 8; i++) {
for (short int j = 0; j < 8; j++) { for (short int j = 0; j < 8; j++) {
if (ColorIndex(i, j) != ColorIndexGrayBlack) { if (ColorIndex(i, j) != ColorIndexGrayBlack && ColorIndex(i, j) != ColorIndexWhiteDefault) {
short int bg = (colorScheme != COLORSCHEME_BLACKNIGHT) short int bg = (colorScheme != COLORSCHEME_BLACKNIGHT)
? (j == 0 ? -1 : j) ? (j == 0 ? -1 : j)
: j; : j;
@ -803,6 +805,8 @@ void CRT_setColors(int colorScheme) {
short int grayBlackBg = (colorScheme != COLORSCHEME_BLACKNIGHT) ? -1 : 0; short int grayBlackBg = (colorScheme != COLORSCHEME_BLACKNIGHT) ? -1 : 0;
init_pair(ColorIndexGrayBlack, grayBlackFg, grayBlackBg); init_pair(ColorIndexGrayBlack, grayBlackFg, grayBlackBg);
init_pair(ColorIndexWhiteDefault, White, -1);
CRT_colors = CRT_colorSchemes[colorScheme]; CRT_colors = CRT_colorSchemes[colorScheme];
} }
@ -819,9 +823,13 @@ void CRT_handleSIGSEGV(int signal) {
"- Your OS and kernel version (uname -a)\n" "- Your OS and kernel version (uname -a)\n"
"- Your distribution and release (lsb_release -a)\n" "- Your distribution and release (lsb_release -a)\n"
"- Likely steps to reproduce (How did it happened?)\n" "- Likely steps to reproduce (How did it happened?)\n"
);
#ifdef HAVE_EXECINFO_H #ifdef HAVE_EXECINFO_H
"- Backtrace of the issue (see below)\n" fprintf(stderr, "- Backtrace of the issue (see below)\n");
#endif #endif
fprintf(stderr,
"\n" "\n"
); );
@ -856,11 +864,15 @@ void CRT_handleSIGSEGV(int signal) {
"you should provide a disassembly of your binary.\n" "you should provide a disassembly of your binary.\n"
"This can usually be done by running the following command:\n" "This can usually be done by running the following command:\n"
"\n" "\n"
);
#ifdef HTOP_DARWIN #ifdef HTOP_DARWIN
" otool -tvV `which htop` > ~/htop.otool\n" fprintf(stderr, " otool -tvV `which htop` > ~/htop.otool\n");
#else #else
" objdump -d -S -w `which htop` > ~/htop.objdump\n" fprintf(stderr, " objdump -d -S -w `which htop` > ~/htop.objdump\n");
#endif #endif
fprintf(stderr,
"\n" "\n"
"Please include the generated file in your report.\n" "Please include the generated file in your report.\n"
"\n" "\n"

23
CRT.h
View File

@ -16,26 +16,27 @@ in the source distribution for its full text.
typedef enum TreeStr_ { typedef enum TreeStr_ {
TREE_STR_HORZ,
TREE_STR_VERT, TREE_STR_VERT,
TREE_STR_RTEE, TREE_STR_RTEE,
TREE_STR_BEND, TREE_STR_BEND,
TREE_STR_TEND, TREE_STR_TEND,
TREE_STR_OPEN, TREE_STR_OPEN,
TREE_STR_SHUT, TREE_STR_SHUT,
TREE_STR_COUNT TREE_STR_ASC,
TREE_STR_DESC,
LAST_TREE_STR
} TreeStr; } TreeStr;
typedef enum ColorSchemes_ { typedef enum ColorScheme_ {
COLORSCHEME_DEFAULT = 0, COLORSCHEME_DEFAULT,
COLORSCHEME_MONOCHROME, COLORSCHEME_MONOCHROME,
COLORSCHEME_BLACKONWHITE, COLORSCHEME_BLACKONWHITE,
COLORSCHEME_LIGHTTERMINAL, COLORSCHEME_LIGHTTERMINAL,
COLORSCHEME_MIDNIGHT, COLORSCHEME_MIDNIGHT,
COLORSCHEME_BLACKNIGHT, COLORSCHEME_BLACKNIGHT,
COLORSCHEME_BROKENGRAY, COLORSCHEME_BROKENGRAY,
LAST_COLORSCHEME, LAST_COLORSCHEME
} ColorSchemes; } ColorScheme;
typedef enum ColorElements_ { typedef enum ColorElements_ {
RESET_COLOR, RESET_COLOR,
@ -58,6 +59,7 @@ typedef enum ColorElements_ {
METER_VALUE_IOWRITE, METER_VALUE_IOWRITE,
METER_VALUE_NOTICE, METER_VALUE_NOTICE,
METER_VALUE_OK, METER_VALUE_OK,
METER_VALUE_WARN,
LED_COLOR, LED_COLOR,
UPTIME, UPTIME,
BATTERY, BATTERY,
@ -144,20 +146,13 @@ extern const char* const* CRT_treeStr;
extern const int* CRT_colors; extern const int* CRT_colors;
extern int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT];
extern int CRT_cursorX; extern int CRT_cursorX;
extern int CRT_scrollHAmount; extern int CRT_scrollHAmount;
extern int CRT_scrollWheelVAmount; extern int CRT_scrollWheelVAmount;
extern const char* CRT_termType; extern ColorScheme CRT_colorScheme;
extern int CRT_colorScheme;
extern long CRT_pageSize;
extern long CRT_pageSizeKB;
#ifdef HAVE_SETUID_ENABLED #ifdef HAVE_SETUID_ENABLED

View File

@ -1,3 +1,59 @@
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 What's new in version 3.0.3
* Process sorting in 'tree' mode * Process sorting in 'tree' mode

View File

@ -60,26 +60,21 @@ static HandlerResult ColorsPanel_eventHandler(Panel* super, int ch) {
case KEY_MOUSE: case KEY_MOUSE:
case KEY_RECLICK: case KEY_RECLICK:
case ' ': case ' ':
assert(mark >= 0);
assert(mark < LAST_COLORSCHEME);
for (int i = 0; ColorSchemeNames[i] != NULL; i++) for (int i = 0; ColorSchemeNames[i] != NULL; i++)
CheckItem_set((CheckItem*)Panel_get(super, i), false); CheckItem_set((CheckItem*)Panel_get(super, i), false);
CheckItem_set((CheckItem*)Panel_get(super, mark), true); CheckItem_set((CheckItem*)Panel_get(super, mark), true);
this->settings->colorScheme = mark; this->settings->colorScheme = mark;
result = HANDLED;
}
if (result == HANDLED) {
this->settings->changed = true; this->settings->changed = true;
const Header* header = this->scr->header;
CRT_setColors(mark); CRT_setColors(mark);
clear(); clear();
Panel* menu = (Panel*) Vector_get(this->scr->panels, 0);
Header_draw(header); result = HANDLED | REDRAW;
FunctionBar_draw(super->currentBar);
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; return result;
} }
@ -100,6 +95,8 @@ ColorsPanel* ColorsPanel_new(Settings* settings, ScreenManager* scr) {
this->settings = settings; this->settings = settings;
this->scr = scr; this->scr = scr;
assert(ARRAYSIZE(ColorSchemeNames) == LAST_COLORSCHEME + 1);
Panel_setHeader(super, "Colors"); Panel_setHeader(super, "Colors");
for (int i = 0; ColorSchemeNames[i] != NULL; i++) { for (int i = 0; ColorSchemeNames[i] != NULL; i++) {
Panel_add(super, (Object*) CheckItem_newByVal(ColorSchemeNames[i], false)); Panel_add(super, (Object*) CheckItem_newByVal(ColorSchemeNames[i], false));

View File

@ -44,7 +44,7 @@ static HandlerResult ColumnsPanel_eventHandler(Panel* super, int ch) {
{ {
if (selected < size - 1) { if (selected < size - 1) {
this->moving = !(this->moving); this->moving = !(this->moving);
Panel_setSelectionColor(super, this->moving ? CRT_colors[PANEL_SELECTION_FOLLOW] : CRT_colors[PANEL_SELECTION_FOCUS]); Panel_setSelectionColor(super, this->moving ? PANEL_SELECTION_FOLLOW : PANEL_SELECTION_FOCUS);
ListItem* selectedItem = (ListItem*) Panel_getSelected(super); ListItem* selectedItem = (ListItem*) Panel_getSelected(super);
if (selectedItem) if (selectedItem)
selectedItem->moving = this->moving; selectedItem->moving = this->moving;

View File

@ -17,9 +17,10 @@ static void CommandScreen_scan(InfoScreen* this) {
Panel_prune(panel); Panel_prune(panel);
const char* p = Process_getCommand(this->process); const char* p = Process_getCommand(this->process);
char* line = xMalloc(COLS + 1); char line[COLS + 1];
int line_offset = 0, last_spc = -1, len; int line_offset = 0, last_spc = -1, len;
for (; *p != '\0'; p++, line_offset++) { for (; *p != '\0'; p++, line_offset++) {
assert(line_offset >= 0 && (size_t)line_offset < sizeof(line));
line[line_offset] = *p; line[line_offset] = *p;
if (*p == ' ') { if (*p == ' ') {
last_spc = line_offset; last_spc = line_offset;
@ -41,7 +42,6 @@ static void CommandScreen_scan(InfoScreen* this) {
InfoScreen_addLine(this, line); InfoScreen_addLine(this, line);
} }
free(line);
Panel_setSelected(panel, idx); Panel_setSelected(panel, idx);
} }
@ -60,7 +60,7 @@ const InfoScreenClass CommandScreen_class = {
CommandScreen* CommandScreen_new(Process* process) { CommandScreen* CommandScreen_new(Process* process) {
CommandScreen* this = AllocThis(CommandScreen); CommandScreen* this = AllocThis(CommandScreen);
return (CommandScreen*) InfoScreen_init(&this->super, process, NULL, LINES - 3, " "); return (CommandScreen*) InfoScreen_init(&this->super, process, NULL, LINES - 2, " ");
} }
void CommandScreen_delete(Object* this) { void CommandScreen_delete(Object* this) {

View File

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

View File

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

View File

@ -88,7 +88,7 @@ static void DiskIOMeter_updateValues(Meter* this, char* buffer, size_t len) {
static void DIskIOMeter_display(ATTR_UNUSED const Object* cast, RichString* out) { static void DIskIOMeter_display(ATTR_UNUSED const Object* cast, RichString* out) {
if (!hasData) { if (!hasData) {
RichString_write(out, CRT_colors[METER_VALUE_ERROR], "no data"); RichString_writeAscii(out, CRT_colors[METER_VALUE_ERROR], "no data");
return; return;
} }
@ -96,15 +96,15 @@ static void DIskIOMeter_display(ATTR_UNUSED const Object* cast, RichString* out)
int color = cached_utilisation_diff > 40.0 ? METER_VALUE_NOTICE : METER_VALUE; int color = cached_utilisation_diff > 40.0 ? METER_VALUE_NOTICE : METER_VALUE;
xSnprintf(buffer, sizeof(buffer), "%.1f%%", cached_utilisation_diff); xSnprintf(buffer, sizeof(buffer), "%.1f%%", cached_utilisation_diff);
RichString_write(out, CRT_colors[color], buffer); RichString_writeAscii(out, CRT_colors[color], buffer);
RichString_append(out, CRT_colors[METER_TEXT], " read: "); RichString_appendAscii(out, CRT_colors[METER_TEXT], " read: ");
Meter_humanUnit(buffer, cached_read_diff, sizeof(buffer)); Meter_humanUnit(buffer, cached_read_diff, sizeof(buffer));
RichString_append(out, CRT_colors[METER_VALUE_IOREAD], buffer); RichString_appendAscii(out, CRT_colors[METER_VALUE_IOREAD], buffer);
RichString_append(out, CRT_colors[METER_TEXT], " write: "); RichString_appendAscii(out, CRT_colors[METER_TEXT], " write: ");
Meter_humanUnit(buffer, cached_write_diff, sizeof(buffer)); Meter_humanUnit(buffer, cached_write_diff, sizeof(buffer));
RichString_append(out, CRT_colors[METER_VALUE_IOWRITE], buffer); RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], buffer);
} }
const MeterClass DiskIOMeter_class = { const MeterClass DiskIOMeter_class = {

View File

@ -97,6 +97,7 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager*
Panel_setHeader(super, "Display options"); Panel_setHeader(super, "Display options");
Panel_add(super, (Object*) CheckItem_newByRef("Tree view", &(settings->treeView))); Panel_add(super, (Object*) CheckItem_newByRef("Tree view", &(settings->treeView)));
Panel_add(super, (Object*) CheckItem_newByRef("- Tree view is always sorted by PID (htop 2 behavior)", &(settings->treeViewAlwaysByPID)));
Panel_add(super, (Object*) CheckItem_newByRef("Shadow other users' processes", &(settings->shadowOtherUsers))); Panel_add(super, (Object*) CheckItem_newByRef("Shadow other users' processes", &(settings->shadowOtherUsers)));
Panel_add(super, (Object*) CheckItem_newByRef("Hide kernel threads", &(settings->hideKernelThreads))); Panel_add(super, (Object*) CheckItem_newByRef("Hide kernel threads", &(settings->hideKernelThreads)));
Panel_add(super, (Object*) CheckItem_newByRef("Hide userland process threads", &(settings->hideUserlandThreads))); Panel_add(super, (Object*) CheckItem_newByRef("Hide userland process threads", &(settings->hideUserlandThreads)));
@ -123,6 +124,7 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager*
Panel_add(super, (Object*) NumberItem_newByRef("Update interval (in seconds)", &(settings->delay), -1, 1, 255)); Panel_add(super, (Object*) NumberItem_newByRef("Update interval (in seconds)", &(settings->delay), -1, 1, 255));
Panel_add(super, (Object*) CheckItem_newByRef("Highlight new and old processes", &(settings->highlightChanges))); Panel_add(super, (Object*) CheckItem_newByRef("Highlight new and old processes", &(settings->highlightChanges)));
Panel_add(super, (Object*) NumberItem_newByRef("- Highlight time (in seconds)", &(settings->highlightDelaySecs), 0, 1, 24*60*60)); Panel_add(super, (Object*) NumberItem_newByRef("- Highlight time (in seconds)", &(settings->highlightDelaySecs), 0, 1, 24*60*60));
Panel_add(super, (Object*) NumberItem_newByRef("Hide main function bar (0 - off, 1 - on ESC until next input, 2 - permanently)", &(settings->hideFunctionBar), 0, 0, 2));
#ifdef HAVE_LIBHWLOC #ifdef HAVE_LIBHWLOC
Panel_add(super, (Object*) CheckItem_newByRef("Show topology when selecting affinity by default", &(settings->topologyAffinity))); Panel_add(super, (Object*) CheckItem_newByRef("Show topology when selecting affinity by default", &(settings->topologyAffinity)));
#endif #endif

View File

@ -14,30 +14,21 @@
#include "XUtils.h" #include "XUtils.h"
const InfoScreenClass EnvScreen_class = {
.super = {
.extends = Class(Object),
.delete = EnvScreen_delete
},
.scan = EnvScreen_scan,
.draw = EnvScreen_draw
};
EnvScreen* EnvScreen_new(Process* process) { EnvScreen* EnvScreen_new(Process* process) {
EnvScreen* this = xMalloc(sizeof(EnvScreen)); EnvScreen* this = xMalloc(sizeof(EnvScreen));
Object_setClass(this, Class(EnvScreen)); Object_setClass(this, Class(EnvScreen));
return (EnvScreen*) InfoScreen_init(&this->super, process, NULL, LINES - 3, " "); return (EnvScreen*) InfoScreen_init(&this->super, process, NULL, LINES - 2, " ");
} }
void EnvScreen_delete(Object* this) { void EnvScreen_delete(Object* this) {
free(InfoScreen_done((InfoScreen*)this)); free(InfoScreen_done((InfoScreen*)this));
} }
void EnvScreen_draw(InfoScreen* this) { static void EnvScreen_draw(InfoScreen* this) {
InfoScreen_drawTitled(this, "Environment of process %d - %s", this->process->pid, Process_getCommand(this->process)); 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; Panel* panel = this->display;
int idx = MAXIMUM(Panel_getSelectedIndex(panel), 0); int idx = MAXIMUM(Panel_getSelectedIndex(panel), 0);
@ -59,3 +50,12 @@ void EnvScreen_scan(InfoScreen* this) {
Vector_insertionSort(panel->items); Vector_insertionSort(panel->items);
Panel_setSelected(panel, idx); Panel_setSelected(panel, idx);
} }
const InfoScreenClass EnvScreen_class = {
.super = {
.extends = Class(Object),
.delete = EnvScreen_delete
},
.scan = EnvScreen_scan,
.draw = EnvScreen_draw
};

View File

@ -15,8 +15,4 @@ EnvScreen* EnvScreen_new(Process* process);
void EnvScreen_delete(Object* this); void EnvScreen_delete(Object* this);
void EnvScreen_draw(InfoScreen* this);
void EnvScreen_scan(InfoScreen* this);
#endif #endif

View File

@ -112,10 +112,11 @@ void FunctionBar_drawExtra(const FunctionBar* this, const char* buffer, int attr
attrset(attr); attrset(attr);
} }
mvaddstr(LINES - 1, x, buffer); mvaddstr(LINES - 1, x, buffer);
attrset(CRT_colors[RESET_COLOR]);
x += strlen(buffer); x += strlen(buffer);
} }
attrset(CRT_colors[RESET_COLOR]);
if (setCursor) { if (setCursor) {
CRT_cursorX = x; CRT_cursorX = x;
curs_set(1); curs_set(1);
@ -132,10 +133,10 @@ void FunctionBar_append(const char* buffer, int attr) {
} else { } else {
attrset(attr); attrset(attr);
} }
mvaddstr(LINES - 1, currentLen, buffer); mvaddstr(LINES - 1, currentLen + 1, buffer);
attrset(CRT_colors[RESET_COLOR]); attrset(CRT_colors[RESET_COLOR]);
currentLen += strlen(buffer); currentLen += strlen(buffer) + 1;
} }
int FunctionBar_synthesizeEvent(const FunctionBar* this, int pos) { int FunctionBar_synthesizeEvent(const FunctionBar* this, int pos) {

View File

@ -15,22 +15,37 @@ in the source distribution for its full text.
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "CRT.h"
#include "Macros.h" #include "Macros.h"
#include "XUtils.h" #include "XUtils.h"
typedef struct HashtableItem_ {
ht_key_t key;
size_t probe;
void* value;
} HashtableItem;
struct Hashtable_ {
size_t size;
HashtableItem* buckets;
size_t items;
bool owner;
};
#ifndef NDEBUG #ifndef NDEBUG
static void Hashtable_dump(const Hashtable* this) { static void Hashtable_dump(const Hashtable* this) {
fprintf(stderr, "Hashtable %p: size=%u items=%u owner=%s\n", fprintf(stderr, "Hashtable %p: size=%zu items=%zu owner=%s\n",
(const void*)this, (const void*)this,
this->size, this->size,
this->items, this->items,
this->owner ? "yes" : "no"); this->owner ? "yes" : "no");
unsigned int items = 0; size_t items = 0;
for (unsigned int i = 0; i < this->size; i++) { for (size_t i = 0; i < this->size; i++) {
fprintf(stderr, " item %5u: key = %5u probe = %2u value = %p\n", fprintf(stderr, " item %5zu: key = %5u probe = %2zu value = %p\n",
i, i,
this->buckets[i].key, this->buckets[i].key,
this->buckets[i].probe, this->buckets[i].probe,
@ -40,15 +55,15 @@ static void Hashtable_dump(const Hashtable* this) {
items++; items++;
} }
fprintf(stderr, "Hashtable %p: items=%u counted=%u\n", fprintf(stderr, "Hashtable %p: items=%zu counted=%zu\n",
(const void*)this, (const void*)this,
this->items, this->items,
items); items);
} }
static bool Hashtable_isConsistent(const Hashtable* this) { static bool Hashtable_isConsistent(const Hashtable* this) {
unsigned int items = 0; size_t items = 0;
for (unsigned int i = 0; i < this->size; i++) { for (size_t i = 0; i < this->size; i++) {
if (this->buckets[i].value) if (this->buckets[i].value)
items++; items++;
} }
@ -58,9 +73,9 @@ static bool Hashtable_isConsistent(const Hashtable* this) {
return res; return res;
} }
unsigned int Hashtable_count(const Hashtable* this) { size_t Hashtable_count(const Hashtable* this) {
unsigned int items = 0; size_t items = 0;
for (unsigned int i = 0; i < this->size; i++) { for (size_t i = 0; i < this->size; i++) {
if (this->buckets[i].value) if (this->buckets[i].value)
items++; items++;
} }
@ -80,18 +95,17 @@ static const uint64_t OEISprimes[] = {
34359738337, 68719476731, 137438953447 34359738337, 68719476731, 137438953447
}; };
static uint64_t nextPrime(unsigned int n) { static uint64_t nextPrime(size_t n) {
assert(n <= OEISprimes[ARRAYSIZE(OEISprimes) - 1]); /* on 32-bit make sure we do not return primes not fitting in size_t */
for (size_t i = 0; i < ARRAYSIZE(OEISprimes) && OEISprimes[i] < SIZE_MAX; i++) {
for (unsigned int i = 0; i < ARRAYSIZE(OEISprimes); i++) {
if (n <= OEISprimes[i]) if (n <= OEISprimes[i])
return OEISprimes[i]; return OEISprimes[i];
} }
return OEISprimes[ARRAYSIZE(OEISprimes) - 1]; CRT_fatalError("Hashtable: no prime found");
} }
Hashtable* Hashtable_new(unsigned int size, bool owner) { Hashtable* Hashtable_new(size_t size, bool owner) {
Hashtable* this; Hashtable* this;
this = xMalloc(sizeof(Hashtable)); this = xMalloc(sizeof(Hashtable));
@ -115,7 +129,7 @@ void Hashtable_clear(Hashtable* this) {
assert(Hashtable_isConsistent(this)); assert(Hashtable_isConsistent(this));
if (this->owner) if (this->owner)
for (unsigned int i = 0; i < this->size; i++) for (size_t i = 0; i < this->size; i++)
free(this->buckets[i].value); free(this->buckets[i].value);
memset(this->buckets, 0, this->size * sizeof(HashtableItem)); memset(this->buckets, 0, this->size * sizeof(HashtableItem));
@ -124,11 +138,11 @@ void Hashtable_clear(Hashtable* this) {
assert(Hashtable_isConsistent(this)); assert(Hashtable_isConsistent(this));
} }
static void insert(Hashtable* this, hkey_t key, void* value) { static void insert(Hashtable* this, ht_key_t key, void* value) {
unsigned int index = key % this->size; size_t index = key % this->size;
unsigned int probe = 0; size_t probe = 0;
#ifndef NDEBUG #ifndef NDEBUG
unsigned int origIndex = index; size_t origIndex = index;
#endif #endif
for (;;) { for (;;) {
@ -167,7 +181,7 @@ static void insert(Hashtable* this, hkey_t key, void* value) {
} }
} }
void Hashtable_setSize(Hashtable* this, unsigned int size) { void Hashtable_setSize(Hashtable* this, size_t size) {
assert(Hashtable_isConsistent(this)); assert(Hashtable_isConsistent(this));
@ -175,14 +189,14 @@ void Hashtable_setSize(Hashtable* this, unsigned int size) {
return; return;
HashtableItem* oldBuckets = this->buckets; HashtableItem* oldBuckets = this->buckets;
unsigned int oldSize = this->size; size_t oldSize = this->size;
this->size = nextPrime(size); this->size = nextPrime(size);
this->buckets = (HashtableItem*) xCalloc(this->size, sizeof(HashtableItem)); this->buckets = (HashtableItem*) xCalloc(this->size, sizeof(HashtableItem));
this->items = 0; this->items = 0;
/* rehash */ /* rehash */
for (unsigned int i = 0; i < oldSize; i++) { for (size_t i = 0; i < oldSize; i++) {
if (!oldBuckets[i].value) if (!oldBuckets[i].value)
continue; continue;
@ -194,15 +208,19 @@ void Hashtable_setSize(Hashtable* this, unsigned int size) {
assert(Hashtable_isConsistent(this)); assert(Hashtable_isConsistent(this));
} }
void Hashtable_put(Hashtable* this, hkey_t key, void* value) { void Hashtable_put(Hashtable* this, ht_key_t key, void* value) {
assert(Hashtable_isConsistent(this)); assert(Hashtable_isConsistent(this));
assert(this->size > 0); assert(this->size > 0);
assert(value); assert(value);
/* grow on load-factor > 0.7 */ /* grow on load-factor > 0.7 */
if (10 * this->items > 7 * this->size) if (10 * this->items > 7 * this->size) {
if (SIZE_MAX / 2 < this->size)
CRT_fatalError("Hashtable: size overflow");
Hashtable_setSize(this, 2 * this->size); Hashtable_setSize(this, 2 * this->size);
}
insert(this, key, value); insert(this, key, value);
@ -211,11 +229,11 @@ void Hashtable_put(Hashtable* this, hkey_t key, void* value) {
assert(this->size > this->items); assert(this->size > this->items);
} }
void* Hashtable_remove(Hashtable* this, hkey_t key) { void* Hashtable_remove(Hashtable* this, ht_key_t key) {
unsigned int index = key % this->size; size_t index = key % this->size;
unsigned int probe = 0; size_t probe = 0;
#ifndef NDEBUG #ifndef NDEBUG
unsigned int origIndex = index; size_t origIndex = index;
#endif #endif
assert(Hashtable_isConsistent(this)); assert(Hashtable_isConsistent(this));
@ -230,7 +248,7 @@ void* Hashtable_remove(Hashtable* this, hkey_t key) {
res = this->buckets[index].value; res = this->buckets[index].value;
} }
unsigned int next = (index + 1) % this->size; size_t next = (index + 1) % this->size;
while (this->buckets[next].value && this->buckets[next].probe > 0) { while (this->buckets[next].value && this->buckets[next].probe > 0) {
this->buckets[index] = this->buckets[next]; this->buckets[index] = this->buckets[next];
@ -266,12 +284,12 @@ void* Hashtable_remove(Hashtable* this, hkey_t key) {
return res; return res;
} }
void* Hashtable_get(Hashtable* this, hkey_t key) { void* Hashtable_get(Hashtable* this, ht_key_t key) {
unsigned int index = key % this->size; size_t index = key % this->size;
unsigned int probe = 0; size_t probe = 0;
void* res = NULL; void* res = NULL;
#ifndef NDEBUG #ifndef NDEBUG
unsigned int origIndex = index; size_t origIndex = index;
#endif #endif
assert(Hashtable_isConsistent(this)); assert(Hashtable_isConsistent(this));
@ -296,7 +314,7 @@ void* Hashtable_get(Hashtable* this, hkey_t key) {
void Hashtable_foreach(Hashtable* this, Hashtable_PairFunction f, void* userData) { void Hashtable_foreach(Hashtable* this, Hashtable_PairFunction f, void* userData) {
assert(Hashtable_isConsistent(this)); assert(Hashtable_isConsistent(this));
for (unsigned int i = 0; i < this->size; i++) { for (size_t i = 0; i < this->size; i++) {
HashtableItem* walk = &this->buckets[i]; HashtableItem* walk = &this->buckets[i];
if (walk->value) if (walk->value)
f(walk->key, walk->value, userData); f(walk->key, walk->value, userData);

View File

@ -8,44 +8,34 @@ in the source distribution for its full text.
*/ */
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h>
typedef unsigned int hkey_t; typedef unsigned int ht_key_t;
typedef void(*Hashtable_PairFunction)(hkey_t key, void* value, void* userdata); typedef void(*Hashtable_PairFunction)(ht_key_t key, void* value, void* userdata);
typedef struct HashtableItem_ { typedef struct Hashtable_ Hashtable;
hkey_t key;
unsigned int probe;
void* value;
} HashtableItem;
typedef struct Hashtable_ {
unsigned int size;
HashtableItem* buckets;
unsigned int items;
bool owner;
} Hashtable;
#ifndef NDEBUG #ifndef NDEBUG
unsigned int Hashtable_count(const Hashtable* this); size_t Hashtable_count(const Hashtable* this);
#endif /* NDEBUG */ #endif /* NDEBUG */
Hashtable* Hashtable_new(unsigned int size, bool owner); Hashtable* Hashtable_new(size_t size, bool owner);
void Hashtable_delete(Hashtable* this); void Hashtable_delete(Hashtable* this);
void Hashtable_clear(Hashtable* this); void Hashtable_clear(Hashtable* this);
void Hashtable_setSize(Hashtable* this, unsigned int size); void Hashtable_setSize(Hashtable* this, size_t size);
void Hashtable_put(Hashtable* this, hkey_t key, void* value); void Hashtable_put(Hashtable* this, ht_key_t key, void* value);
void* Hashtable_remove(Hashtable* this, hkey_t key); void* Hashtable_remove(Hashtable* this, ht_key_t key);
void* Hashtable_get(Hashtable* this, hkey_t key); void* Hashtable_get(Hashtable* this, ht_key_t key);
void Hashtable_foreach(Hashtable* this, Hashtable_PairFunction f, void* userData); void Hashtable_foreach(Hashtable* this, Hashtable_PairFunction f, void* userData);

View File

@ -29,9 +29,9 @@ void IncSet_reset(IncSet* this, IncType type) {
IncMode_reset(&this->modes[type]); IncMode_reset(&this->modes[type]);
} }
static const char* const searchFunctions[] = {"Next ", "Cancel ", " Search: ", NULL}; static const char* const searchFunctions[] = {"Next ", "Prev ", "Cancel ", " Search: ", NULL};
static const char* const searchKeys[] = {"F3", "Esc", " "}; static const char* const searchKeys[] = {"F3", "S-F3", "Esc", " "};
static int searchEvents[] = {KEY_F(3), 27, ERR}; static const int searchEvents[] = {KEY_F(3), KEY_F(15), 27, ERR};
static inline void IncMode_initSearch(IncMode* search) { static inline void IncMode_initSearch(IncMode* search) {
memset(search, 0, sizeof(IncMode)); memset(search, 0, sizeof(IncMode));
@ -41,7 +41,7 @@ static inline void IncMode_initSearch(IncMode* search) {
static const char* const filterFunctions[] = {"Done ", "Clear ", " Filter: ", NULL}; static const char* const filterFunctions[] = {"Done ", "Clear ", " Filter: ", NULL};
static const char* const filterKeys[] = {"Enter", "Esc", " "}; 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) { static inline void IncMode_initFilter(IncMode* filter) {
memset(filter, 0, sizeof(IncMode)); memset(filter, 0, sizeof(IncMode));
@ -54,12 +54,13 @@ static inline void IncMode_done(IncMode* mode) {
} }
IncSet* IncSet_new(FunctionBar* bar) { IncSet* IncSet_new(FunctionBar* bar) {
IncSet* this = xCalloc(1, sizeof(IncSet)); IncSet* this = xMalloc(sizeof(IncSet));
IncMode_initSearch(&(this->modes[INC_SEARCH])); IncMode_initSearch(&(this->modes[INC_SEARCH]));
IncMode_initFilter(&(this->modes[INC_FILTER])); IncMode_initFilter(&(this->modes[INC_FILTER]));
this->active = NULL; this->active = NULL;
this->filtering = false;
this->defaultBar = bar; this->defaultBar = bar;
this->filtering = false;
this->found = false;
return this; return this;
} }
@ -99,20 +100,14 @@ static void updateWeakPanel(IncSet* this, Panel* panel, Vector* lines) {
static bool search(IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelValue) { static bool search(IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelValue) {
int size = Panel_size(panel); int size = Panel_size(panel);
bool found = false;
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
if (String_contains_i(getPanelValue(panel, i), mode->buffer)) { if (String_contains_i(getPanelValue(panel, i), mode->buffer)) {
Panel_setSelected(panel, i); Panel_setSelected(panel, i);
found = true; return true;
break;
} }
} }
FunctionBar_drawExtra(mode->bar, return false;
mode->buffer,
found ? -1 : CRT_colors[FAILED_SEARCH],
true);
return found;
} }
static bool IncMode_find(IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelValue, int step) { static bool IncMode_find(IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelValue, int step) {
@ -138,14 +133,6 @@ static bool IncMode_find(IncMode* mode, Panel* panel, IncMode_GetPanelValue getP
} }
} }
bool IncSet_next(IncSet* this, IncType type, Panel* panel, IncMode_GetPanelValue getPanelValue) {
return IncMode_find(&this->modes[type], panel, getPanelValue, 1);
}
bool IncSet_prev(IncSet* this, IncType type, Panel* panel, IncMode_GetPanelValue getPanelValue) {
return IncMode_find(&this->modes[type], panel, getPanelValue, -1);
}
bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue getPanelValue, Vector* lines) { bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue getPanelValue, Vector* lines) {
if (ch == ERR) if (ch == ERR)
return true; return true;
@ -154,11 +141,11 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue
int size = Panel_size(panel); int size = Panel_size(panel);
bool filterChanged = false; bool filterChanged = false;
bool doSearch = true; bool doSearch = true;
if (ch == KEY_F(3)) { if (ch == KEY_F(3) || ch == KEY_F(15)) {
if (size == 0) if (size == 0)
return true; return true;
IncMode_find(mode, panel, getPanelValue, 1); IncMode_find(mode, panel, getPanelValue, ch == KEY_F(3) ? 1 : -1);
doSearch = false; doSearch = false;
} else if (0 < ch && ch < 255 && isprint((unsigned char)ch)) { } else if (0 < ch && ch < 255 && isprint((unsigned char)ch)) {
if (mode->index < INCMODE_MAX) { if (mode->index < INCMODE_MAX) {
@ -172,7 +159,7 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue
} }
} }
} }
} else if ((ch == KEY_BACKSPACE || ch == 127)) { } else if (ch == KEY_BACKSPACE || ch == 127) {
if (mode->index > 0) { if (mode->index > 0) {
mode->index--; mode->index--;
mode->buffer[mode->index] = 0; mode->buffer[mode->index] = 0;
@ -187,7 +174,7 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue
doSearch = false; doSearch = false;
} }
} else if (ch == KEY_RESIZE) { } else if (ch == KEY_RESIZE) {
Panel_resize(panel, COLS, LINES - panel->y - 1); doSearch = (mode->index > 0);
} else { } else {
if (mode->isFilter) { if (mode->isFilter) {
filterChanged = true; filterChanged = true;
@ -202,7 +189,6 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue
} }
this->active = NULL; this->active = NULL;
Panel_setDefaultBar(panel); Panel_setDefaultBar(panel);
FunctionBar_draw(this->defaultBar);
doSearch = false; doSearch = false;
} }
if (doSearch) { if (doSearch) {
@ -221,13 +207,12 @@ const char* IncSet_getListItemValue(Panel* panel, int i) {
void IncSet_activate(IncSet* this, IncType type, Panel* panel) { void IncSet_activate(IncSet* this, IncType type, Panel* panel) {
this->active = &(this->modes[type]); this->active = &(this->modes[type]);
FunctionBar_drawExtra(this->active->bar, this->active->buffer, -1, true);
panel->currentBar = this->active->bar; panel->currentBar = this->active->bar;
} }
void IncSet_drawBar(const IncSet* this) { void IncSet_drawBar(const IncSet* this) {
if (this->active) { if (this->active) {
FunctionBar_drawExtra(this->active->bar, this->active->buffer, -1, true); FunctionBar_drawExtra(this->active->bar, this->active->buffer, (this->active->isFilter || this->found) ? -1 : CRT_colors[FAILED_SEARCH], true);
} else { } else {
FunctionBar_draw(this->defaultBar); FunctionBar_draw(this->defaultBar);
} }

View File

@ -48,10 +48,6 @@ IncSet* IncSet_new(FunctionBar* bar);
void IncSet_delete(IncSet* this); void IncSet_delete(IncSet* this);
bool IncSet_next(IncSet* this, IncType type, Panel* panel, IncMode_GetPanelValue getPanelValue);
bool IncSet_prev(IncSet* this, IncType type, Panel* panel, IncMode_GetPanelValue getPanelValue);
bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue getPanelValue, Vector* lines); bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue getPanelValue, Vector* lines);
const char* IncSet_getListItemValue(Panel* panel, int i); const char* IncSet_getListItemValue(Panel* panel, int i);

View File

@ -19,14 +19,14 @@ static const char* const InfoScreenFunctions[] = {"Search ", "Filter ", "Refresh
static const char* const InfoScreenKeys[] = {"F3", "F4", "F5", "Esc"}; 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, const Process* process, FunctionBar* bar, int height, const char* panelHeader) { InfoScreen* InfoScreen_init(InfoScreen* this, const Process* process, FunctionBar* bar, int height, const char* panelHeader) {
this->process = process; this->process = process;
if (!bar) { if (!bar) {
bar = FunctionBar_new(InfoScreenFunctions, InfoScreenKeys, InfoScreenEvents); bar = FunctionBar_new(InfoScreenFunctions, InfoScreenKeys, InfoScreenEvents);
} }
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->inc = IncSet_new(bar);
this->lines = Vector_new(this->display->items->type, true, DEFAULT_SIZE); this->lines = Vector_new(this->display->items->type, true, DEFAULT_SIZE);
Panel_setHeader(this->display, panelHeader); Panel_setHeader(this->display, panelHeader);
@ -44,21 +44,21 @@ void InfoScreen_drawTitled(InfoScreen* this, const char* fmt, ...) {
va_list ap; va_list ap;
va_start(ap, fmt); va_start(ap, fmt);
char* title = xMalloc(COLS + 1); char title[COLS + 1];
int len = vsnprintf(title, COLS + 1, fmt, ap); int len = vsnprintf(title, sizeof(title), fmt, ap);
va_end(ap);
if (len > COLS) { if (len > COLS) {
memset(&title[COLS - 3], '.', 3); memset(&title[COLS - 3], '.', 3);
} }
attrset(CRT_colors[METER_TEXT]); attrset(CRT_colors[METER_TEXT]);
mvhline(0, 0, ' ', COLS); mvhline(0, 0, ' ', COLS);
mvwprintw(stdscr, 0, 0, title); mvaddstr(0, 0, title);
attrset(CRT_colors[DEFAULT_COLOR]); attrset(CRT_colors[DEFAULT_COLOR]);
this->display->needsRedraw = true; Panel_draw(this->display, true, true, true, false);
Panel_draw(this->display, true, true);
IncSet_drawBar(this->inc); IncSet_drawBar(this->inc);
free(title);
va_end(ap);
} }
void InfoScreen_addLine(InfoScreen* this, const char* line) { void InfoScreen_addLine(InfoScreen* this, const char* line) {
@ -89,7 +89,8 @@ void InfoScreen_run(InfoScreen* this) {
bool looping = true; bool looping = true;
while (looping) { while (looping) {
Panel_draw(panel, true, true); Panel_draw(panel, false, true, true, false);
IncSet_drawBar(this->inc);
if (this->inc->active) { if (this->inc->active) {
(void) move(LINES - 1, CRT_cursorX); (void) move(LINES - 1, CRT_cursorX);

View File

@ -16,7 +16,6 @@ typedef struct InfoScreen_ {
Object super; Object super;
const Process* process; const Process* process;
Panel* display; Panel* display;
FunctionBar* bar;
IncSet* inc; IncSet* inc;
Vector* lines; Vector* lines;
} InfoScreen; } InfoScreen;

View File

@ -29,7 +29,7 @@ static void ListItem_display(const Object* cast, RichString* out) {
assert (this != NULL); assert (this != NULL);
if (this->moving) { if (this->moving) {
RichString_write(out, CRT_colors[DEFAULT_COLOR], RichString_writeWide(out, CRT_colors[DEFAULT_COLOR],
#ifdef HAVE_LIBNCURSESW #ifdef HAVE_LIBNCURSESW
CRT_utf8 ? "" : CRT_utf8 ? "" :
#endif #endif
@ -37,7 +37,7 @@ static void ListItem_display(const Object* cast, RichString* out) {
} else { } else {
RichString_prune(out); RichString_prune(out);
} }
RichString_append(out, CRT_colors[DEFAULT_COLOR], this->value); RichString_appendWide(out, CRT_colors[DEFAULT_COLOR], this->value);
} }
ListItem* ListItem_new(const char* value, int key) { ListItem* ListItem_new(const char* value, int key) {
@ -57,7 +57,7 @@ void ListItem_append(ListItem* this, const char* text) {
this->value[newLen] = '\0'; this->value[newLen] = '\0';
} }
static long ListItem_compare(const void* cast1, const void* cast2) { static int ListItem_compare(const void* cast1, const void* cast2) {
const ListItem* obj1 = (const ListItem*) cast1; const ListItem* obj1 = (const ListItem*) cast1;
const ListItem* obj2 = (const ListItem*) cast2; const ListItem* obj2 = (const ListItem*) cast2;
return strcmp(obj1->value, obj2->value); return strcmp(obj1->value, obj2->value);

View File

@ -24,8 +24,36 @@ static const int LoadMeter_attributes[] = {
LOAD LOAD
}; };
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) { static void LoadAverageMeter_updateValues(Meter* this, char* buffer, size_t size) {
Platform_getLoadAverage(&this->values[0], &this->values[1], &this->values[2]); Platform_getLoadAverage(&this->values[0], &this->values[1], &this->values[2]);
// only show bar for 1min value
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]); xSnprintf(buffer, size, "%.2f/%.2f/%.2f", this->values[0], this->values[1], this->values[2]);
} }
@ -33,19 +61,29 @@ static void LoadAverageMeter_display(const Object* cast, RichString* out) {
const Meter* this = (const Meter*)cast; const Meter* this = (const Meter*)cast;
char buffer[20]; char buffer[20];
xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[0]); 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]); 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]); 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, size_t size) { static void LoadMeter_updateValues(Meter* this, char* buffer, size_t size) {
double five, fifteen; double five, fifteen;
Platform_getLoadAverage(&this->values[0], &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]); xSnprintf(buffer, size, "%.2f", this->values[0]);
} }
@ -53,7 +91,7 @@ static void LoadMeter_display(const Object* cast, RichString* out) {
const Meter* this = (const Meter*)cast; const Meter* this = (const Meter*)cast;
char buffer[20]; char buffer[20];
xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[0]); xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[0]);
RichString_write(out, CRT_colors[LOAD], buffer); RichString_writeAscii(out, CRT_colors[LOAD], buffer);
} }
const MeterClass LoadAverageMeter_class = { const MeterClass LoadAverageMeter_class = {

View File

@ -25,7 +25,7 @@ static const char* const MainFunctions[] = {"Help ", "Setup ", "Search", "Filt
void MainPanel_updateTreeFunctions(MainPanel* this, bool mode) { void MainPanel_updateTreeFunctions(MainPanel* this, bool mode) {
FunctionBar* bar = MainPanel_getFunctionBar(this); FunctionBar* bar = MainPanel_getFunctionBar(this);
FunctionBar_setLabel(bar, KEY_F(5), mode ? "Sorted" : "Tree "); FunctionBar_setLabel(bar, KEY_F(5), mode ? "List " : "Tree ");
} }
void MainPanel_pidSearch(MainPanel* this, int ch) { void MainPanel_pidSearch(MainPanel* this, int ch) {
@ -44,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) { static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
MainPanel* this = (MainPanel*) super; MainPanel* this = (MainPanel*) super;
@ -51,6 +56,11 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
Htop_Reaction reaction = HTOP_OK; Htop_Reaction reaction = HTOP_OK;
/* Let supervising ScreenManager handle resize */
if (ch == KEY_RESIZE)
return IGNORED;
/* reset on every normal key */
if (ch != ERR) if (ch != ERR)
this->state->hideProcessSelection = false; this->state->hideProcessSelection = false;
@ -60,7 +70,11 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
Settings* settings = this->state->settings; Settings* settings = this->state->settings;
int hx = super->scrollH + x + 1; int hx = super->scrollH + x + 1;
ProcessField field = ProcessList_keyAt(pl, hx); ProcessField field = ProcessList_keyAt(pl, hx);
if (field == settings->sortKey) { if (settings->treeView && settings->treeViewAlwaysByPID) {
settings->treeView = false;
settings->direction = 1;
reaction |= Action_setSortKey(settings, field);
} else if (field == Settings_getActiveSortKey(settings)) {
Settings_invertSortOrder(settings); Settings_invertSortOrder(settings);
} else { } else {
reaction |= Action_setSortKey(settings, field); reaction |= Action_setSortKey(settings, field);
@ -68,7 +82,7 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
reaction |= HTOP_RECALCULATE | HTOP_REDRAW_BAR | HTOP_SAVE_SETTINGS; reaction |= HTOP_RECALCULATE | HTOP_REDRAW_BAR | HTOP_SAVE_SETTINGS;
result = HANDLED; result = HANDLED;
} else if (ch != ERR && this->inc->active) { } 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) { if (filterChanged) {
this->state->pl->incFilter = IncSet_filter(this->inc); this->state->pl->incFilter = IncSet_filter(this->inc);
reaction = HTOP_REFRESH | HTOP_REDRAW_BAR; reaction = HTOP_REFRESH | HTOP_REDRAW_BAR;
@ -96,16 +110,12 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
if (reaction & HTOP_REDRAW_BAR) { if (reaction & HTOP_REDRAW_BAR) {
MainPanel_updateTreeFunctions(this, this->state->settings->treeView); MainPanel_updateTreeFunctions(this, this->state->settings->treeView);
IncSet_drawBar(this->inc);
if (this->state->pauseProcessUpdate) {
FunctionBar_append("PAUSED", CRT_colors[PAUSED]);
}
} }
if (reaction & HTOP_UPDATE_PANELHDR) { if (reaction & HTOP_UPDATE_PANELHDR) {
ProcessList_printHeader(this->state->pl, Panel_getHeader(super)); result |= REDRAW;
} }
if (reaction & HTOP_REFRESH) { if (reaction & HTOP_REFRESH) {
result |= REDRAW; result |= REFRESH;
} }
if (reaction & HTOP_RECALCULATE) { if (reaction & HTOP_RECALCULATE) {
result |= RESCAN; result |= RESCAN;
@ -118,7 +128,7 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
} }
if (!(reaction & HTOP_KEEP_FOLLOWING)) { if (!(reaction & HTOP_KEEP_FOLLOWING)) {
this->state->pl->following = -1; this->state->pl->following = -1;
Panel_setSelectionColor(super, CRT_colors[PANEL_SELECTION_FOCUS]); Panel_setSelectionColor(super, PANEL_SELECTION_FOCUS);
} }
return result; return result;
} }
@ -131,11 +141,6 @@ int MainPanel_selectedPid(MainPanel* this) {
return -1; return -1;
} }
const char* MainPanel_getValue(MainPanel* this, int i) {
Process* p = (Process*) Panel_get((Panel*)this, i);
return Process_getCommand(p);
}
bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, Arg arg, bool* wasAnyTagged) { bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, Arg arg, bool* wasAnyTagged) {
Panel* super = (Panel*) this; Panel* super = (Panel*) this;
bool ok = true; bool ok = true;
@ -160,12 +165,32 @@ bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, Ar
return ok; return ok;
} }
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 = { const PanelClass MainPanel_class = {
.super = { .super = {
.extends = Class(Panel), .extends = Class(Panel),
.delete = MainPanel_delete .delete = MainPanel_delete
}, },
.eventHandler = MainPanel_eventHandler .eventHandler = MainPanel_eventHandler,
.drawFunctionBar = MainPanel_drawFunctionBar,
.printHeader = MainPanel_printHeader
}; };
MainPanel* MainPanel_new() { MainPanel* MainPanel_new() {

View File

@ -38,8 +38,6 @@ void MainPanel_pidSearch(MainPanel* this, int ch);
int MainPanel_selectedPid(MainPanel* this); 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, Arg arg, bool* wasAnyTagged);
extern const PanelClass MainPanel_class; extern const PanelClass MainPanel_class;

View File

@ -133,6 +133,7 @@ linux_platform_headers = \
linux/LinuxProcessList.h \ linux/LinuxProcessList.h \
linux/Platform.h \ linux/Platform.h \
linux/PressureStallMeter.h \ linux/PressureStallMeter.h \
linux/ProcessField.h \
linux/SELinuxMeter.h \ linux/SELinuxMeter.h \
linux/SystemdMeter.h \ linux/SystemdMeter.h \
linux/ZramMeter.h \ linux/ZramMeter.h \
@ -164,9 +165,10 @@ endif
# ------- # -------
freebsd_platform_headers = \ freebsd_platform_headers = \
freebsd/Platform.h \
freebsd/FreeBSDProcessList.h \ freebsd/FreeBSDProcessList.h \
freebsd/FreeBSDProcess.h \ freebsd/FreeBSDProcess.h \
freebsd/Platform.h \
freebsd/ProcessField.h \
zfs/ZfsArcMeter.h \ zfs/ZfsArcMeter.h \
zfs/ZfsCompressedArcMeter.h \ zfs/ZfsCompressedArcMeter.h \
zfs/ZfsArcStats.h \ zfs/ZfsArcStats.h \
@ -184,14 +186,16 @@ endif
# ------------ # ------------
dragonflybsd_platform_headers = \ dragonflybsd_platform_headers = \
dragonflybsd/Platform.h \
dragonflybsd/DragonFlyBSDProcessList.h \ dragonflybsd/DragonFlyBSDProcessList.h \
dragonflybsd/DragonFlyBSDProcess.h dragonflybsd/DragonFlyBSDProcess.h \
dragonflybsd/Platform.h \
dragonflybsd/ProcessField.h
if HTOP_DRAGONFLYBSD if HTOP_DRAGONFLYBSD
AM_LDFLAGS += -lkvm -lkinfo myhtopplatsources = \
myhtopplatsources = dragonflybsd/Platform.c dragonflybsd/DragonFlyBSDProcessList.c \ dragonflybsd/Platform.c \
dragonflybsd/DragonFlyBSDProcess.c dragonflybsd/DragonFlyBSDProcessList.c \
dragonflybsd/DragonFlyBSDProcess.c
myhtopplatheaders = $(dragonflybsd_platform_headers) myhtopplatheaders = $(dragonflybsd_platform_headers)
endif endif
@ -200,9 +204,10 @@ endif
# ------- # -------
openbsd_platform_headers = \ openbsd_platform_headers = \
openbsd/Platform.h \
openbsd/OpenBSDProcessList.h \ openbsd/OpenBSDProcessList.h \
openbsd/OpenBSDProcess.h openbsd/OpenBSDProcess.h \
openbsd/Platform.h \
openbsd/ProcessField.h
if HTOP_OPENBSD if HTOP_OPENBSD
myhtopplatsources = openbsd/Platform.c openbsd/OpenBSDProcessList.c \ myhtopplatsources = openbsd/Platform.c openbsd/OpenBSDProcessList.c \
@ -215,9 +220,10 @@ endif
# ------ # ------
darwin_platform_headers = \ darwin_platform_headers = \
darwin/Platform.h \
darwin/DarwinProcess.h \ darwin/DarwinProcess.h \
darwin/DarwinProcessList.h \ darwin/DarwinProcessList.h \
darwin/Platform.h \
darwin/ProcessField.h \
zfs/ZfsArcMeter.h \ zfs/ZfsArcMeter.h \
zfs/ZfsCompressedArcMeter.h \ zfs/ZfsCompressedArcMeter.h \
zfs/ZfsArcStats.h \ zfs/ZfsArcStats.h \
@ -237,6 +243,7 @@ endif
solaris_platform_headers = \ solaris_platform_headers = \
solaris/Platform.h \ solaris/Platform.h \
solaris/ProcessField.h \
solaris/SolarisProcess.h \ solaris/SolarisProcess.h \
solaris/SolarisProcessList.h \ solaris/SolarisProcessList.h \
zfs/ZfsArcMeter.h \ zfs/ZfsArcMeter.h \
@ -256,6 +263,7 @@ endif
unsupported_platform_headers = \ unsupported_platform_headers = \
unsupported/Platform.h \ unsupported/Platform.h \
unsupported/ProcessField.h \
unsupported/UnsupportedProcess.h \ unsupported/UnsupportedProcess.h \
unsupported/UnsupportedProcessList.h unsupported/UnsupportedProcessList.h

View File

@ -34,18 +34,18 @@ static void MemoryMeter_updateValues(Meter* this, char* buffer, size_t size) {
static void MemoryMeter_display(const Object* cast, RichString* out) { static void MemoryMeter_display(const Object* cast, RichString* out) {
char buffer[50]; char buffer[50];
const Meter* this = (const Meter*)cast; const Meter* this = (const Meter*)cast;
RichString_write(out, CRT_colors[METER_TEXT], ":"); RichString_writeAscii(out, CRT_colors[METER_TEXT], ":");
Meter_humanUnit(buffer, this->total, 50); Meter_humanUnit(buffer, this->total, sizeof(buffer));
RichString_append(out, CRT_colors[METER_VALUE], buffer); RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer);
Meter_humanUnit(buffer, this->values[0], 50); Meter_humanUnit(buffer, this->values[0], sizeof(buffer));
RichString_append(out, CRT_colors[METER_TEXT], " used:"); RichString_appendAscii(out, CRT_colors[METER_TEXT], " used:");
RichString_append(out, CRT_colors[MEMORY_USED], buffer); RichString_appendAscii(out, CRT_colors[MEMORY_USED], buffer);
Meter_humanUnit(buffer, this->values[1], 50); Meter_humanUnit(buffer, this->values[1], sizeof(buffer));
RichString_append(out, CRT_colors[METER_TEXT], " buffers:"); RichString_appendAscii(out, CRT_colors[METER_TEXT], " buffers:");
RichString_append(out, CRT_colors[MEMORY_BUFFERS_TEXT], buffer); RichString_appendAscii(out, CRT_colors[MEMORY_BUFFERS_TEXT], buffer);
Meter_humanUnit(buffer, this->values[2], 50); Meter_humanUnit(buffer, this->values[2], sizeof(buffer));
RichString_append(out, CRT_colors[METER_TEXT], " cache:"); RichString_appendAscii(out, CRT_colors[METER_TEXT], " cache:");
RichString_append(out, CRT_colors[MEMORY_CACHE], buffer); RichString_appendAscii(out, CRT_colors[MEMORY_CACHE], buffer);
} }
const MeterClass MemoryMeter_class = { const MeterClass MemoryMeter_class = {

66
Meter.c
View File

@ -39,6 +39,7 @@ Meter* Meter_new(const struct ProcessList_* pl, int param, const MeterClass* typ
this->param = param; this->param = param;
this->pl = pl; this->pl = pl;
this->curItems = type->maxItems; this->curItems = type->maxItems;
this->curAttributes = NULL;
this->values = type->maxItems ? xCalloc(type->maxItems, sizeof(double)) : NULL; this->values = type->maxItems ? xCalloc(type->maxItems, sizeof(double)) : NULL;
this->total = type->total; this->total = type->total;
this->caption = xStrdup(type->caption); this->caption = xStrdup(type->caption);
@ -100,7 +101,7 @@ static inline void Meter_displayBuffer(const Meter* this, const char* buffer, Ri
if (Object_displayFn(this)) { if (Object_displayFn(this)) {
Object_display(this, out); Object_display(this, out);
} else { } else {
RichString_write(out, CRT_colors[Meter_attributes(this)[0]], buffer); RichString_writeWide(out, CRT_colors[Meter_attributes(this)[0]], buffer);
} }
} }
@ -156,16 +157,20 @@ ListItem* Meter_toListItem(Meter* this, bool moving) {
static void TextMeterMode_draw(Meter* this, int x, int y, int w) { static void TextMeterMode_draw(Meter* this, int x, int y, int w) {
char buffer[METER_BUFFER_LEN]; char buffer[METER_BUFFER_LEN];
Meter_updateValues(this, buffer, sizeof(buffer)); Meter_updateValues(this, buffer, sizeof(buffer));
(void) w;
attrset(CRT_colors[METER_TEXT]); 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); int captionLen = strlen(this->caption);
x += captionLen; x += captionLen;
attrset(CRT_colors[RESET_COLOR]); w -= captionLen;
if (w <= 0)
return;
RichString_begin(out); RichString_begin(out);
Meter_displayBuffer(this, buffer, &out); Meter_displayBuffer(this, buffer, &out);
RichString_printVal(out, y, x); RichString_printoffnVal(out, y, x, 0, w - 1);
RichString_end(out); RichString_end(out);
} }
@ -185,7 +190,7 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
w -= captionLen; w -= captionLen;
attrset(CRT_colors[BAR_BORDER]); attrset(CRT_colors[BAR_BORDER]);
mvaddch(y, x, '['); mvaddch(y, x, '[');
mvaddch(y, x + w, ']'); mvaddch(y, x + MAXIMUM(w, 0), ']');
attrset(CRT_colors[RESET_COLOR]); attrset(CRT_colors[RESET_COLOR]);
w--; w--;
@ -195,14 +200,29 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
return; return;
// The text in the bar is right aligned; // The text in the bar is right aligned;
// calculate needed padding and generate leading spaces // Pad with maximal spaces and then calculate needed starting position offset
const int textLen = mbstowcs(NULL, buffer, 0);
const int padding = CLAMP(w - textLen, 0, w);
RichString_begin(bar); RichString_begin(bar);
RichString_appendChr(&bar, ' ', padding); RichString_appendChr(&bar, ' ', w);
RichString_append(&bar, 0, buffer); RichString_appendWide(&bar, 0, buffer);
assert(RichString_sizeVal(bar) >= w); 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;
}
}
// 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]; int blockSizes[10];
@ -220,11 +240,11 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
// (Control against invalid values) // (Control against invalid values)
nextOffset = CLAMP(nextOffset, 0, w); nextOffset = CLAMP(nextOffset, 0, w);
for (int j = offset; j < nextOffset; j++) for (int j = offset; j < nextOffset; j++)
if (RichString_getCharVal(bar, j) == ' ') { if (RichString_getCharVal(bar, startPos + j) == ' ') {
if (CRT_colorScheme == COLORSCHEME_MONOCHROME) { if (CRT_colorScheme == COLORSCHEME_MONOCHROME) {
RichString_setChar(&bar, j, BarMeterMode_characters[i]); RichString_setChar(&bar, startPos + j, BarMeterMode_characters[i]);
} else { } else {
RichString_setChar(&bar, j, '|'); RichString_setChar(&bar, startPos + j, '|');
} }
} }
offset = nextOffset; offset = nextOffset;
@ -233,14 +253,15 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
// ...then print the buffer. // ...then print the buffer.
offset = 0; offset = 0;
for (uint8_t i = 0; i < this->curItems; i++) { for (uint8_t i = 0; i < this->curItems; i++) {
RichString_setAttrn(&bar, CRT_colors[Meter_attributes(this)[i]], offset, offset + blockSizes[i] - 1); int attr = this->curAttributes ? this->curAttributes[i] : Meter_attributes(this)[i];
RichString_printoffnVal(bar, y, x + offset, offset, blockSizes[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 += blockSizes[i];
offset = CLAMP(offset, 0, w); offset = CLAMP(offset, 0, w);
} }
if (offset < w) { if (offset < w) {
RichString_setAttrn(&bar, CRT_colors[BAR_SHADOW], offset, w - 1); RichString_setAttrn(&bar, CRT_colors[BAR_SHADOW], startPos + offset, w - offset);
RichString_printoffnVal(bar, y, x + offset, offset, w - offset); RichString_printoffnVal(bar, y, x + offset, startPos + offset, w - offset);
} }
RichString_end(bar); RichString_end(bar);
@ -271,9 +292,6 @@ static const char* const GraphMeterMode_dotsAscii[] = {
/*20*/":", /*21*/":", /*22*/":" /*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) { static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
if (!this->drawData) { if (!this->drawData) {
@ -282,6 +300,8 @@ static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
GraphData* data = this->drawData; GraphData* data = this->drawData;
const int nValues = METER_BUFFER_LEN; const int nValues = METER_BUFFER_LEN;
const char* const* GraphMeterMode_dots;
int GraphMeterMode_pixPerRow;
#ifdef HAVE_LIBNCURSESW #ifdef HAVE_LIBNCURSESW
if (CRT_utf8) { if (CRT_utf8) {
GraphMeterMode_dots = GraphMeterMode_dotsUtf8; GraphMeterMode_dots = GraphMeterMode_dotsUtf8;

View File

@ -100,6 +100,7 @@ struct Meter_ {
int h; int h;
const ProcessList* pl; const ProcessList* pl;
uint8_t curItems; uint8_t curItems;
const int* curAttributes;
double* values; double* values;
double total; double total;
void* meterData; void* meterData;

View File

@ -20,9 +20,9 @@ in the source distribution for its full text.
// Note: In code the meters are known to have bar/text/graph "Modes", but in UI // Note: In code the meters are known to have bar/text/graph "Modes", but in UI
// we call them "Styles". // we call them "Styles".
static const char* const MetersFunctions[] = {"Style ", "Move ", " ", "Delete", "Done ", NULL}; static const char* const MetersFunctions[] = {"Style ", "Move ", " ", "Delete", "Done ", NULL};
static const char* const MetersKeys[] = {"Space", "Enter", " ", "Del", "F10"}; static const char* const MetersKeys[] = {"Space", "Enter", "", "Del", "F10"};
static int MetersEvents[] = {' ', 13, ERR, KEY_DC, KEY_F(10)}; static const int MetersEvents[] = {' ', 13, ERR, KEY_DC, KEY_F(10)};
// We avoid UTF-8 arrows ← → here as they might display full-width on Chinese // We avoid UTF-8 arrows ← → here as they might display full-width on Chinese
// terminals, breaking our aligning. // terminals, breaking our aligning.
@ -30,7 +30,7 @@ static int MetersEvents[] = {' ', 13, ERR, KEY_DC, KEY_F(10)};
// considered "Ambiguous characters". // considered "Ambiguous characters".
static const char* const MetersMovingFunctions[] = {"Style ", "Lock ", "Up ", "Down ", "Left ", "Right ", " ", "Delete", "Done ", NULL}; 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 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; static FunctionBar* Meters_movingBar = NULL;
void MetersPanel_cleanup() { void MetersPanel_cleanup() {
@ -55,13 +55,12 @@ void MetersPanel_setMoving(MetersPanel* this, bool moving) {
selected->moving = moving; selected->moving = moving;
} }
if (!moving) { if (!moving) {
Panel_setSelectionColor(super, CRT_colors[PANEL_SELECTION_FOCUS]); Panel_setSelectionColor(super, PANEL_SELECTION_FOCUS);
Panel_setDefaultBar(super); Panel_setDefaultBar(super);
} else { } else {
Panel_setSelectionColor(super, CRT_colors[PANEL_SELECTION_FOLLOW]); Panel_setSelectionColor(super, PANEL_SELECTION_FOLLOW);
super->currentBar = Meters_movingBar; super->currentBar = Meters_movingBar;
} }
FunctionBar_draw(this->super.currentBar);
} }
static inline bool moveToNeighbor(MetersPanel* this, MetersPanel* neighbor, int selected) { static inline bool moveToNeighbor(MetersPanel* this, MetersPanel* neighbor, int selected) {

View File

@ -88,24 +88,24 @@ static void NetworkIOMeter_updateValues(ATTR_UNUSED Meter* this, char* buffer, s
static void NetworkIOMeter_display(ATTR_UNUSED const Object* cast, RichString* out) { static void NetworkIOMeter_display(ATTR_UNUSED const Object* cast, RichString* out) {
if (!hasData) { if (!hasData) {
RichString_write(out, CRT_colors[METER_VALUE_ERROR], "no data"); RichString_writeAscii(out, CRT_colors[METER_VALUE_ERROR], "no data");
return; return;
} }
char buffer[64]; char buffer[64];
RichString_write(out, CRT_colors[METER_TEXT], "rx: "); RichString_writeAscii(out, CRT_colors[METER_TEXT], "rx: ");
Meter_humanUnit(buffer, cached_rxb_diff, sizeof(buffer)); Meter_humanUnit(buffer, cached_rxb_diff, sizeof(buffer));
RichString_append(out, CRT_colors[METER_VALUE_IOREAD], buffer); RichString_appendAscii(out, CRT_colors[METER_VALUE_IOREAD], buffer);
RichString_append(out, CRT_colors[METER_VALUE_IOREAD], "iB/s"); RichString_appendAscii(out, CRT_colors[METER_VALUE_IOREAD], "iB/s");
RichString_append(out, CRT_colors[METER_TEXT], " tx: "); RichString_appendAscii(out, CRT_colors[METER_TEXT], " tx: ");
Meter_humanUnit(buffer, cached_txb_diff, sizeof(buffer)); Meter_humanUnit(buffer, cached_txb_diff, sizeof(buffer));
RichString_append(out, CRT_colors[METER_VALUE_IOWRITE], buffer); RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], buffer);
RichString_append(out, CRT_colors[METER_VALUE_IOWRITE], "iB/s"); RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], "iB/s");
xSnprintf(buffer, sizeof(buffer), " (%lu/%lu packets) ", cached_rxp_diff, cached_txp_diff); xSnprintf(buffer, sizeof(buffer), " (%lu/%lu packets) ", cached_rxp_diff, cached_txp_diff);
RichString_append(out, CRT_colors[METER_TEXT], buffer); RichString_appendAscii(out, CRT_colors[METER_TEXT], buffer);
} }
const MeterClass NetworkIOMeter_class = { const MeterClass NetworkIOMeter_class = {

View File

@ -24,7 +24,7 @@ struct Object_;
typedef struct Object_ Object; typedef struct Object_ Object;
typedef void(*Object_Display)(const Object*, RichString*); typedef void(*Object_Display)(const Object*, RichString*);
typedef long(*Object_Compare)(const void*, const void*); typedef int(*Object_Compare)(const void*, const void*);
typedef void(*Object_Delete)(Object*); typedef void(*Object_Delete)(Object*);
#define Object_getClass(obj_) ((const Object*)(obj_))->klass #define Object_getClass(obj_) ((const Object*)(obj_))->klass

View File

@ -74,7 +74,7 @@ OpenFilesScreen* OpenFilesScreen_new(const Process* process) {
} else { } else {
this->pid = process->pid; this->pid = process->pid;
} }
return (OpenFilesScreen*) InfoScreen_init(&this->super, process, NULL, LINES - 3, " FD TYPE MODE 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) { void OpenFilesScreen_delete(Object* this) {

View File

@ -29,14 +29,14 @@ static void CheckItem_display(const Object* cast, RichString* out) {
const CheckItem* this = (const CheckItem*)cast; const CheckItem* this = (const CheckItem*)cast;
assert (this != NULL); assert (this != NULL);
RichString_write(out, CRT_colors[CHECK_BOX], "["); RichString_writeAscii(out, CRT_colors[CHECK_BOX], "[");
if (CheckItem_get(this)) { if (CheckItem_get(this)) {
RichString_append(out, CRT_colors[CHECK_MARK], "x"); RichString_appendAscii(out, CRT_colors[CHECK_MARK], "x");
} else { } else {
RichString_append(out, CRT_colors[CHECK_MARK], " "); RichString_appendAscii(out, CRT_colors[CHECK_MARK], " ");
} }
RichString_append(out, CRT_colors[CHECK_BOX], "] "); RichString_appendAscii(out, CRT_colors[CHECK_BOX], "] ");
RichString_append(out, CRT_colors[CHECK_TEXT], this->super.text); RichString_appendWide(out, CRT_colors[CHECK_TEXT], this->super.text);
} }
static void NumberItem_display(const Object* cast, RichString* out) { static void NumberItem_display(const Object* cast, RichString* out) {
@ -44,7 +44,7 @@ static void NumberItem_display(const Object* cast, RichString* out) {
assert (this != NULL); assert (this != NULL);
char buffer[12]; char buffer[12];
RichString_write(out, CRT_colors[CHECK_BOX], "["); RichString_writeAscii(out, CRT_colors[CHECK_BOX], "[");
int written; int written;
if (this->scale < 0) { if (this->scale < 0) {
written = xSnprintf(buffer, sizeof(buffer), "%.*f", -this->scale, pow(10, this->scale) * NumberItem_get(this)); written = xSnprintf(buffer, sizeof(buffer), "%.*f", -this->scale, pow(10, this->scale) * NumberItem_get(this));
@ -53,12 +53,12 @@ static void NumberItem_display(const Object* cast, RichString* out) {
} else { } else {
written = xSnprintf(buffer, sizeof(buffer), "%d", NumberItem_get(this)); written = xSnprintf(buffer, sizeof(buffer), "%d", NumberItem_get(this));
} }
RichString_append(out, CRT_colors[CHECK_MARK], buffer); RichString_appendAscii(out, CRT_colors[CHECK_MARK], buffer);
RichString_append(out, CRT_colors[CHECK_BOX], "]"); RichString_appendAscii(out, CRT_colors[CHECK_BOX], "]");
for (int i = written; i < 5; i++) { for (int i = written; i < 5; i++) {
RichString_append(out, CRT_colors[CHECK_BOX], " "); RichString_appendAscii(out, CRT_colors[CHECK_BOX], " ");
} }
RichString_append(out, CRT_colors[CHECK_TEXT], this->super.text); RichString_appendWide(out, CRT_colors[CHECK_TEXT], this->super.text);
} }
const OptionItemClass OptionItem_class = { const OptionItemClass OptionItem_class = {

90
Panel.c
View File

@ -30,7 +30,7 @@ const PanelClass Panel_class = {
.eventHandler = Panel_selectByTyping, .eventHandler = Panel_selectByTyping,
}; };
Panel* Panel_new(int x, int y, int w, int h, bool owner, const ObjectClass* type, FunctionBar* fuBar) { Panel* Panel_new(int x, int y, int w, int h, const ObjectClass* type, bool owner, FunctionBar* fuBar) {
Panel* this; Panel* this;
this = xMalloc(sizeof(Panel)); this = xMalloc(sizeof(Panel));
Object_setClass(this, Class(Panel)); Object_setClass(this, Class(Panel));
@ -55,11 +55,13 @@ void Panel_init(Panel* this, int x, int y, int w, int h, const ObjectClass* type
this->scrollH = 0; this->scrollH = 0;
this->selected = 0; this->selected = 0;
this->oldSelected = 0; this->oldSelected = 0;
this->selectedLen = 0;
this->needsRedraw = true; this->needsRedraw = true;
this->wasFocus = false;
RichString_beginAllocated(this->header); RichString_beginAllocated(this->header);
this->defaultBar = fuBar; this->defaultBar = fuBar;
this->currentBar = fuBar; this->currentBar = fuBar;
this->selectionColor = CRT_colors[PANEL_SELECTION_FOCUS]; this->selectionColorId = PANEL_SELECTION_FOCUS;
} }
void Panel_done(Panel* this) { void Panel_done(Panel* this) {
@ -70,19 +72,12 @@ void Panel_done(Panel* this) {
RichString_end(this->header); RichString_end(this->header);
} }
void Panel_setSelectionColor(Panel* this, int color) { void Panel_setSelectionColor(Panel* this, ColorElements colorId) {
this->selectionColor = color; this->selectionColorId = colorId;
}
RichString* Panel_getHeader(Panel* this) {
assert (this != NULL);
this->needsRedraw = true;
return &(this->header);
} }
inline void Panel_setHeader(Panel* this, const char* header) { 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; this->needsRedraw = true;
} }
@ -97,10 +92,6 @@ void Panel_move(Panel* this, int x, int y) {
void Panel_resize(Panel* this, int w, int h) { void Panel_resize(Panel* this, int w, int h) {
assert (this != NULL); assert (this != NULL);
if (RichString_sizeVal(this->header) > 0) {
h--;
}
this->w = w; this->w = w;
this->h = h; this->h = h;
this->needsRedraw = true; this->needsRedraw = true;
@ -217,7 +208,7 @@ void Panel_splice(Panel* this, Vector* from) {
this->needsRedraw = true; this->needsRedraw = true;
} }
void Panel_draw(Panel* this, bool focus, bool highlightSelected) { void Panel_draw(Panel* this, bool force_redraw, bool focus, bool highlightSelected, bool hideFunctionBar) {
assert (this != NULL); assert (this != NULL);
int size = Vector_size(this->items); int size = Vector_size(this->items);
@ -226,12 +217,21 @@ void Panel_draw(Panel* this, bool focus, bool highlightSelected) {
int x = this->x; int x = this->x;
int h = this->h; int h = this->h;
if (hideFunctionBar)
h++;
const int header_attr = focus
? CRT_colors[PANEL_HEADER_FOCUS]
: CRT_colors[PANEL_HEADER_UNFOCUS];
if (force_redraw) {
if (Panel_printHeaderFn(this))
Panel_printHeader(this);
else
RichString_setAttr(&this->header, header_attr);
}
int headerLen = RichString_sizeVal(this->header); int headerLen = RichString_sizeVal(this->header);
if (headerLen > 0) { if (headerLen > 0) {
int attr = focus attrset(header_attr);
? CRT_colors[PANEL_HEADER_FOCUS]
: CRT_colors[PANEL_HEADER_UNFOCUS];
attrset(attr);
mvhline(y, x, ' ', this->w); mvhline(y, x, ' ', this->w);
if (scrollH < headerLen) { if (scrollH < headerLen) {
RichString_printoffnVal(this->header, y, x, scrollH, RichString_printoffnVal(this->header, y, x, scrollH,
@ -239,6 +239,7 @@ void Panel_draw(Panel* this, bool focus, bool highlightSelected) {
} }
attrset(CRT_colors[RESET_COLOR]); attrset(CRT_colors[RESET_COLOR]);
y++; y++;
h--;
} }
// ensure scroll area is on screen // ensure scroll area is on screen
@ -262,10 +263,10 @@ void Panel_draw(Panel* this, bool focus, bool highlightSelected) {
int upTo = MINIMUM(first + h, size); int upTo = MINIMUM(first + h, size);
int selectionColor = focus int selectionColor = focus
? this->selectionColor ? CRT_colors[this->selectionColorId]
: CRT_colors[PANEL_SELECTION_UNFOCUS]; : CRT_colors[PANEL_SELECTION_UNFOCUS];
if (this->needsRedraw) { if (this->needsRedraw || force_redraw) {
int line = 0; int line = 0;
for (int i = first; line < h && i < upTo; i++) { for (int i = first; line < h && i < upTo; i++) {
Object* itemObj = Vector_get(this->items, i); Object* itemObj = Vector_get(this->items, i);
@ -293,7 +294,6 @@ void Panel_draw(Panel* this, bool focus, bool highlightSelected) {
mvhline(y + line, x, ' ', this->w); mvhline(y + line, x, ' ', this->w);
line++; line++;
} }
this->needsRedraw = false;
} else { } else {
Object* oldObj = Vector_get(this->items, this->oldSelected); Object* oldObj = Vector_get(this->items, this->oldSelected);
@ -319,17 +319,35 @@ void Panel_draw(Panel* this, bool focus, bool highlightSelected) {
RichString_end(new); RichString_end(new);
RichString_end(old); 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->oldSelected = this->selected;
this->wasFocus = focus;
this->needsRedraw = false;
move(0, 0); 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) { bool Panel_onKey(Panel* this, int key) {
assert (this != NULL); assert (this != NULL);
int size = Vector_size(this->items); const int size = Vector_size(this->items);
#define CLAMP_INDEX(var, delta, min, max) \ #define PANEL_SCROLL(amount) \
CLAMP((var) + (delta), (min), MAXIMUM(0, (max))) 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) { switch (key) {
case KEY_DOWN: case KEY_DOWN:
@ -363,27 +381,19 @@ bool Panel_onKey(Panel* this, int key) {
break; break;
case KEY_PPAGE: case KEY_PPAGE:
this->selected -= (this->h - 1); PANEL_SCROLL(-(this->h - Panel_headerHeight(this)));
this->scrollV = CLAMP_INDEX(this->scrollV, -(this->h - 1), 0, size - this->h);
this->needsRedraw = true;
break; break;
case KEY_NPAGE: case KEY_NPAGE:
this->selected += (this->h - 1); PANEL_SCROLL(+(this->h - Panel_headerHeight(this)));
this->scrollV = CLAMP_INDEX(this->scrollV, +(this->h - 1), 0, size - this->h);
this->needsRedraw = true;
break; break;
case KEY_WHEELUP: case KEY_WHEELUP:
this->selected -= CRT_scrollWheelVAmount; PANEL_SCROLL(-CRT_scrollWheelVAmount);
this->scrollV = CLAMP_INDEX(this->scrollV, -CRT_scrollWheelVAmount, 0, size - this->h);
this->needsRedraw = true;
break; break;
case KEY_WHEELDOWN: case KEY_WHEELDOWN:
this->selected += CRT_scrollWheelVAmount; PANEL_SCROLL(+CRT_scrollWheelVAmount);
this->scrollV = CLAMP_INDEX(this->scrollV, +CRT_scrollWheelVAmount, 0, size - this->h);
this->needsRedraw = true;
break; break;
case KEY_HOME: case KEY_HOME:
@ -408,7 +418,7 @@ bool Panel_onKey(Panel* this, int key) {
return false; return false;
} }
#undef CLAMP_INDEX #undef PANEL_SCROLL
// ensure selection within bounds // ensure selection within bounds
if (this->selected < 0 || size == 0) { if (this->selected < 0 || size == 0) {

35
Panel.h
View File

@ -9,6 +9,7 @@ in the source distribution for its full text.
#include <stdbool.h> #include <stdbool.h>
#include "CRT.h"
#include "FunctionBar.h" #include "FunctionBar.h"
#include "Object.h" #include "Object.h"
#include "RichString.h" #include "RichString.h"
@ -22,9 +23,10 @@ typedef enum HandlerResult_ {
HANDLED = 0x01, HANDLED = 0x01,
IGNORED = 0x02, IGNORED = 0x02,
BREAK_LOOP = 0x04, BREAK_LOOP = 0x04,
REDRAW = 0x08, REFRESH = 0x08,
RESCAN = 0x10, REDRAW = 0x10,
SYNTH_KEY = 0x20, RESCAN = 0x20,
SYNTH_KEY = 0x40,
} HandlerResult; } HandlerResult;
#define EVENT_SET_SELECTED (-1) #define EVENT_SET_SELECTED (-1)
@ -33,16 +35,24 @@ typedef enum HandlerResult_ {
#define EVENT_IS_HEADER_CLICK(ev_) ((ev_) >= -10000 && (ev_) <= -9000) #define EVENT_IS_HEADER_CLICK(ev_) ((ev_) >= -10000 && (ev_) <= -9000)
#define EVENT_HEADER_CLICK_GET_X(ev_) ((ev_) + 10000) #define EVENT_HEADER_CLICK_GET_X(ev_) ((ev_) + 10000)
typedef HandlerResult(*Panel_EventHandler)(Panel*, int); typedef HandlerResult (*Panel_EventHandler)(Panel*, int);
typedef void (*Panel_DrawFunctionBar)(Panel*, bool);
typedef void (*Panel_PrintHeader)(Panel*);
typedef struct PanelClass_ { typedef struct PanelClass_ {
const ObjectClass super; const ObjectClass super;
const Panel_EventHandler eventHandler; const Panel_EventHandler eventHandler;
const Panel_DrawFunctionBar drawFunctionBar;
const Panel_PrintHeader printHeader;
} PanelClass; } PanelClass;
#define As_Panel(this_) ((const PanelClass*)((this_)->super.klass)) #define As_Panel(this_) ((const PanelClass*)((this_)->super.klass))
#define Panel_eventHandlerFn(this_) As_Panel(this_)->eventHandler #define Panel_eventHandlerFn(this_) As_Panel(this_)->eventHandler
#define Panel_eventHandler(this_, ev_) As_Panel(this_)->eventHandler((Panel*)(this_), ev_) #define Panel_eventHandler(this_, ev_) (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_ { struct Panel_ {
Object super; Object super;
@ -55,10 +65,11 @@ struct Panel_ {
int scrollV; int scrollV;
short scrollH; short scrollH;
bool needsRedraw; bool needsRedraw;
bool wasFocus;
FunctionBar* currentBar; FunctionBar* currentBar;
FunctionBar* defaultBar; FunctionBar* defaultBar;
RichString header; RichString header;
int selectionColor; ColorElements selectionColorId;
}; };
#define Panel_setDefaultBar(this_) do { (this_)->currentBar = (this_)->defaultBar; } while (0) #define Panel_setDefaultBar(this_) do { (this_)->currentBar = (this_)->defaultBar; } while (0)
@ -67,7 +78,7 @@ struct Panel_ {
extern const PanelClass Panel_class; extern const PanelClass Panel_class;
Panel* Panel_new(int x, int y, int w, int h, bool owner, const 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_delete(Object* cast);
@ -75,9 +86,7 @@ void Panel_init(Panel* this, int x, int y, int w, int h, const ObjectClass* type
void Panel_done(Panel* this); void Panel_done(Panel* this);
void Panel_setSelectionColor(Panel* this, int color); void Panel_setSelectionColor(Panel* this, ColorElements colorId);
RichString* Panel_getHeader(Panel* this);
void Panel_setHeader(Panel* this, const char* header); void Panel_setHeader(Panel* this, const char* header);
@ -109,7 +118,7 @@ int Panel_size(Panel* this);
void Panel_setSelected(Panel* this, int selected); void Panel_setSelected(Panel* this, int selected);
void Panel_draw(Panel* this, bool focus, bool highlightSelected); void Panel_draw(Panel* this, bool force_redraw, bool focus, bool highlightSelected, bool hideFunctionBar);
void Panel_splice(Panel* this, Vector* from); void Panel_splice(Panel* this, Vector* from);

170
Process.c
View File

@ -38,23 +38,15 @@ in the source distribution for its full text.
static uid_t Process_getuid = (uid_t)-1; static uid_t Process_getuid = (uid_t)-1;
char Process_pidFormat[20] = "%7d "; int Process_pidDigits = 7;
static char Process_titleBuffer[20][20];
void Process_setupColumnWidths() { void Process_setupColumnWidths() {
int maxPid = Platform_getMaxPid(); int maxPid = Platform_getMaxPid();
if (maxPid == -1) if (maxPid == -1)
return; return;
int digits = ceil(log10(maxPid)); Process_pidDigits = ceil(log10(maxPid));
assert(digits < 20); assert(Process_pidDigits <= PROCESS_MAX_PID_DIGITS);
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);
} }
void Process_humanNumber(RichString* str, unsigned long long number, bool coloring) { void Process_humanNumber(RichString* str, unsigned long long number, bool coloring) {
@ -74,53 +66,53 @@ void Process_humanNumber(RichString* str, unsigned long long number, bool colori
if (number < 1000) { if (number < 1000) {
//Plain number, no markings //Plain number, no markings
len = xSnprintf(buffer, sizeof(buffer), "%5llu ", number); len = xSnprintf(buffer, sizeof(buffer), "%5llu ", number);
RichString_appendn(str, processColor, buffer, len); RichString_appendnAscii(str, processColor, buffer, len);
} else if (number < 100000) { } else if (number < 100000) {
//2 digit MB, 3 digit KB //2 digit MB, 3 digit KB
len = xSnprintf(buffer, sizeof(buffer), "%2llu", number/1000); len = xSnprintf(buffer, sizeof(buffer), "%2llu", number/1000);
RichString_appendn(str, processMegabytesColor, buffer, len); RichString_appendnAscii(str, processMegabytesColor, buffer, len);
number %= 1000; number %= 1000;
len = xSnprintf(buffer, sizeof(buffer), "%03llu ", number); len = xSnprintf(buffer, sizeof(buffer), "%03llu ", number);
RichString_appendn(str, processColor, buffer, len); RichString_appendnAscii(str, processColor, buffer, len);
} else if (number < 1000 * ONE_K) { } else if (number < 1000 * ONE_K) {
//3 digit MB //3 digit MB
number /= ONE_K; number /= ONE_K;
len = xSnprintf(buffer, sizeof(buffer), "%4lluM ", number); len = xSnprintf(buffer, sizeof(buffer), "%4lluM ", number);
RichString_appendn(str, processMegabytesColor, buffer, len); RichString_appendnAscii(str, processMegabytesColor, buffer, len);
} else if (number < 10000 * ONE_K) { } else if (number < 10000 * ONE_K) {
//1 digit GB, 3 digit MB //1 digit GB, 3 digit MB
number /= ONE_K; number /= ONE_K;
len = xSnprintf(buffer, sizeof(buffer), "%1llu", number/1000); len = xSnprintf(buffer, sizeof(buffer), "%1llu", number/1000);
RichString_appendn(str, processGigabytesColor, buffer, len); RichString_appendnAscii(str, processGigabytesColor, buffer, len);
number %= 1000; number %= 1000;
len = xSnprintf(buffer, sizeof(buffer), "%03lluM ", number); len = xSnprintf(buffer, sizeof(buffer), "%03lluM ", number);
RichString_appendn(str, processMegabytesColor, buffer, len); RichString_appendnAscii(str, processMegabytesColor, buffer, len);
} else if (number < 100000 * ONE_K) { } else if (number < 100000 * ONE_K) {
//2 digit GB, 1 digit MB //2 digit GB, 1 digit MB
number /= 100 * ONE_K; number /= 100 * ONE_K;
len = xSnprintf(buffer, sizeof(buffer), "%2llu", number/10); len = xSnprintf(buffer, sizeof(buffer), "%2llu", number/10);
RichString_appendn(str, processGigabytesColor, buffer, len); RichString_appendnAscii(str, processGigabytesColor, buffer, len);
number %= 10; number %= 10;
len = xSnprintf(buffer, sizeof(buffer), ".%1llu", number); len = xSnprintf(buffer, sizeof(buffer), ".%1llu", number);
RichString_appendn(str, processMegabytesColor, buffer, len); RichString_appendnAscii(str, processMegabytesColor, buffer, len);
RichString_append(str, processGigabytesColor, "G "); RichString_appendAscii(str, processGigabytesColor, "G ");
} else if (number < 1000 * ONE_M) { } else if (number < 1000 * ONE_M) {
//3 digit GB //3 digit GB
number /= ONE_M; number /= ONE_M;
len = xSnprintf(buffer, sizeof(buffer), "%4lluG ", number); len = xSnprintf(buffer, sizeof(buffer), "%4lluG ", number);
RichString_appendn(str, processGigabytesColor, buffer, len); RichString_appendnAscii(str, processGigabytesColor, buffer, len);
} else if (number < 10000ULL * ONE_M) { } else if (number < 10000ULL * ONE_M) {
//1 digit TB, 3 digit GB //1 digit TB, 3 digit GB
number /= ONE_M; number /= ONE_M;
len = xSnprintf(buffer, sizeof(buffer), "%1llu", number/1000); len = xSnprintf(buffer, sizeof(buffer), "%1llu", number/1000);
RichString_appendn(str, largeNumberColor, buffer, len); RichString_appendnAscii(str, largeNumberColor, buffer, len);
number %= 1000; number %= 1000;
len = xSnprintf(buffer, sizeof(buffer), "%03lluG ", number); len = xSnprintf(buffer, sizeof(buffer), "%03lluG ", number);
RichString_appendn(str, processGigabytesColor, buffer, len); RichString_appendnAscii(str, processGigabytesColor, buffer, len);
} else { } else {
//2 digit TB and above //2 digit TB and above
len = xSnprintf(buffer, sizeof(buffer), "%4.1lfT ", (double)number/ONE_G); len = xSnprintf(buffer, sizeof(buffer), "%4.1lfT ", (double)number/ONE_G);
RichString_appendn(str, largeNumberColor, buffer, len); RichString_appendnAscii(str, largeNumberColor, buffer, len);
} }
} }
@ -139,26 +131,25 @@ void Process_colorNumber(RichString* str, unsigned long long number, bool colori
} }
if (number == ULLONG_MAX) { if (number == ULLONG_MAX) {
int len = xSnprintf(buffer, sizeof(buffer), " N/A "); RichString_appendAscii(str, CRT_colors[PROCESS_SHADOW], " N/A ");
RichString_appendn(str, CRT_colors[PROCESS_SHADOW], buffer, len);
} else if (number >= 100000LL * ONE_DECIMAL_T) { } else if (number >= 100000LL * ONE_DECIMAL_T) {
xSnprintf(buffer, sizeof(buffer), "%11llu ", number / ONE_DECIMAL_G); xSnprintf(buffer, sizeof(buffer), "%11llu ", number / ONE_DECIMAL_G);
RichString_appendn(str, largeNumberColor, buffer, 12); RichString_appendnAscii(str, largeNumberColor, buffer, 12);
} else if (number >= 100LL * ONE_DECIMAL_T) { } else if (number >= 100LL * ONE_DECIMAL_T) {
xSnprintf(buffer, sizeof(buffer), "%11llu ", number / ONE_DECIMAL_M); xSnprintf(buffer, sizeof(buffer), "%11llu ", number / ONE_DECIMAL_M);
RichString_appendn(str, largeNumberColor, buffer, 8); RichString_appendnAscii(str, largeNumberColor, buffer, 8);
RichString_appendn(str, processMegabytesColor, buffer+8, 4); RichString_appendnAscii(str, processMegabytesColor, buffer+8, 4);
} else if (number >= 10LL * ONE_DECIMAL_G) { } else if (number >= 10LL * ONE_DECIMAL_G) {
xSnprintf(buffer, sizeof(buffer), "%11llu ", number / ONE_DECIMAL_K); xSnprintf(buffer, sizeof(buffer), "%11llu ", number / ONE_DECIMAL_K);
RichString_appendn(str, largeNumberColor, buffer, 5); RichString_appendnAscii(str, largeNumberColor, buffer, 5);
RichString_appendn(str, processMegabytesColor, buffer+5, 3); RichString_appendnAscii(str, processMegabytesColor, buffer+5, 3);
RichString_appendn(str, processColor, buffer+8, 4); RichString_appendnAscii(str, processColor, buffer+8, 4);
} else { } else {
xSnprintf(buffer, sizeof(buffer), "%11llu ", number); xSnprintf(buffer, sizeof(buffer), "%11llu ", number);
RichString_appendn(str, largeNumberColor, buffer, 2); RichString_appendnAscii(str, largeNumberColor, buffer, 2);
RichString_appendn(str, processMegabytesColor, buffer+2, 3); RichString_appendnAscii(str, processMegabytesColor, buffer+2, 3);
RichString_appendn(str, processColor, buffer+5, 3); RichString_appendnAscii(str, processColor, buffer+5, 3);
RichString_appendn(str, processShadowColor, buffer+8, 4); RichString_appendnAscii(str, processShadowColor, buffer+8, 4);
} }
} }
@ -172,16 +163,16 @@ void Process_printTime(RichString* str, unsigned long long totalHundredths) {
char buffer[10]; char buffer[10];
if (hours >= 100) { if (hours >= 100) {
xSnprintf(buffer, sizeof(buffer), "%7lluh ", hours); xSnprintf(buffer, sizeof(buffer), "%7lluh ", hours);
RichString_append(str, CRT_colors[LARGE_NUMBER], buffer); RichString_appendAscii(str, CRT_colors[LARGE_NUMBER], buffer);
} else { } else {
if (hours) { if (hours) {
xSnprintf(buffer, sizeof(buffer), "%2lluh", hours); xSnprintf(buffer, sizeof(buffer), "%2lluh", hours);
RichString_append(str, CRT_colors[LARGE_NUMBER], buffer); RichString_appendAscii(str, CRT_colors[LARGE_NUMBER], buffer);
xSnprintf(buffer, sizeof(buffer), "%02d:%02d ", minutes, seconds); xSnprintf(buffer, sizeof(buffer), "%02d:%02d ", minutes, seconds);
} else { } else {
xSnprintf(buffer, sizeof(buffer), "%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);
} }
} }
@ -192,34 +183,34 @@ void Process_fillStarttimeBuffer(Process* this) {
} }
static inline void Process_writeCommand(const Process* this, int attr, int baseattr, RichString* str) { static inline void Process_writeCommand(const Process* this, int attr, int baseattr, RichString* str) {
int start = RichString_size(str), finish = 0; int start = RichString_size(str);
int len = 0;
const char* comm = this->comm; const char* comm = this->comm;
if (this->settings->highlightBaseName || !this->settings->showProgramPath) { if (this->settings->highlightBaseName || !this->settings->showProgramPath) {
int i, basename = 0; int basename = 0;
for (i = 0; i < this->basenameOffset; i++) { for (int i = 0; i < this->basenameOffset; i++) {
if (comm[i] == '/') { if (comm[i] == '/') {
basename = i + 1; basename = i + 1;
} else if (comm[i] == ':') { } else if (comm[i] == ':') {
finish = i + 1; len = i + 1;
break; break;
} }
} }
if (!finish) { if (len == 0) {
if (this->settings->showProgramPath) { if (this->settings->showProgramPath) {
start += basename; start += basename;
} else { } else {
comm += basename; comm += basename;
} }
finish = this->basenameOffset - basename; len = this->basenameOffset - basename;
} }
finish += start - 1;
} }
RichString_append(str, attr, comm); RichString_appendWide(str, attr, comm);
if (this->settings->highlightBaseName) { if (this->settings->highlightBaseName) {
RichString_setAttrn(str, baseattr, start, finish); RichString_setAttrn(str, baseattr, start, len);
} }
} }
@ -234,26 +225,30 @@ void Process_outputRate(RichString* str, char* buffer, size_t n, double rate, in
} }
if (isnan(rate)) { if (isnan(rate)) {
int len = xSnprintf(buffer, n, " N/A "); RichString_appendAscii(str, CRT_colors[PROCESS_SHADOW], " N/A ");
RichString_appendn(str, CRT_colors[PROCESS_SHADOW], buffer, len);
} else if (rate < ONE_K) { } else if (rate < ONE_K) {
int len = snprintf(buffer, n, "%7.2f B/s ", rate); int len = snprintf(buffer, n, "%7.2f B/s ", rate);
RichString_appendn(str, processColor, buffer, len); RichString_appendnAscii(str, processColor, buffer, len);
} else if (rate < ONE_M) { } else if (rate < ONE_M) {
int len = snprintf(buffer, n, "%7.2f K/s ", rate / ONE_K); int len = snprintf(buffer, n, "%7.2f K/s ", rate / ONE_K);
RichString_appendn(str, processColor, buffer, len); RichString_appendnAscii(str, processColor, buffer, len);
} else if (rate < ONE_G) { } else if (rate < ONE_G) {
int len = snprintf(buffer, n, "%7.2f M/s ", rate / ONE_M); int len = snprintf(buffer, n, "%7.2f M/s ", rate / ONE_M);
RichString_appendn(str, processMegabytesColor, buffer, len); RichString_appendnAscii(str, processMegabytesColor, buffer, len);
} else if (rate < ONE_T) { } else if (rate < ONE_T) {
int len = snprintf(buffer, n, "%7.2f G/s ", rate / ONE_G); int len = snprintf(buffer, n, "%7.2f G/s ", rate / ONE_G);
RichString_appendn(str, largeNumberColor, buffer, len); RichString_appendnAscii(str, largeNumberColor, buffer, len);
} else { } else {
int len = snprintf(buffer, n, "%7.2f T/s ", rate / ONE_T); int len = snprintf(buffer, n, "%7.2f T/s ", rate / ONE_T);
RichString_appendn(str, largeNumberColor, buffer, len); RichString_appendnAscii(str, largeNumberColor, buffer, len);
} }
} }
void Process_printLeftAlignedField(RichString* str, int attr, const char* content, unsigned int width) {
int c = RichString_appendnWide(str, attr, content, MINIMUM(width, strlen(content)));
RichString_appendChr(str, ' ', width + 1 - c);
}
void Process_writeField(const Process* this, RichString* str, ProcessField field) { void Process_writeField(const Process* this, RichString* str, ProcessField field) {
char buffer[256]; buffer[255] = '\0'; char buffer[256]; buffer[255] = '\0';
int attr = CRT_colors[DEFAULT_COLOR]; int attr = CRT_colors[DEFAULT_COLOR];
@ -320,17 +315,18 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field
buf += written; buf += written;
n -= written; n -= written;
} }
const char* draw = CRT_treeStr[lastItem ? (this->settings->direction == 1 ? TREE_STR_BEND : TREE_STR_TEND) : TREE_STR_RTEE];
const char* draw = CRT_treeStr[lastItem ? TREE_STR_BEND : TREE_STR_RTEE];
xSnprintf(buf, n, "%s%s ", draw, this->showChildren ? CRT_treeStr[TREE_STR_SHUT] : CRT_treeStr[TREE_STR_OPEN] ); 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); Process_writeCommand(this, attr, baseattr, str);
return; return;
} }
} }
case MAJFLT: Process_colorNumber(str, this->majflt, coloring); return; case MAJFLT: Process_colorNumber(str, this->majflt, coloring); return;
case MINFLT: Process_colorNumber(str, this->minflt, coloring); return; case MINFLT: Process_colorNumber(str, this->minflt, coloring); return;
case M_RESIDENT: Process_humanNumber(str, this->m_resident * CRT_pageSizeKB, coloring); return; case M_RESIDENT: Process_humanNumber(str, this->m_resident, coloring); return;
case M_VIRT: Process_humanNumber(str, this->m_virt * CRT_pageSizeKB, coloring); return; case M_VIRT: Process_humanNumber(str, this->m_virt, coloring); return;
case NICE: { case NICE: {
xSnprintf(buffer, n, "%3ld ", this->nice); xSnprintf(buffer, n, "%3ld ", this->nice);
attr = this->nice < 0 ? CRT_colors[PROCESS_HIGH_PRIORITY] attr = this->nice < 0 ? CRT_colors[PROCESS_HIGH_PRIORITY]
@ -339,9 +335,9 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field
break; break;
} }
case NLWP: xSnprintf(buffer, n, "%4ld ", this->nlwp); break; case NLWP: xSnprintf(buffer, n, "%4ld ", this->nlwp); break;
case PGRP: xSnprintf(buffer, n, Process_pidFormat, this->pgrp); break; case PGRP: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->pgrp); break;
case PID: xSnprintf(buffer, n, Process_pidFormat, this->pid); break; case PID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->pid); break;
case PPID: xSnprintf(buffer, n, Process_pidFormat, this->ppid); break; case PPID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->ppid); break;
case PRIORITY: { case PRIORITY: {
if(this->priority <= -100) if(this->priority <= -100)
xSnprintf(buffer, n, " RT "); xSnprintf(buffer, n, " RT ");
@ -350,7 +346,7 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field
break; break;
} }
case PROCESSOR: xSnprintf(buffer, n, "%3d ", Settings_cpuId(this->settings, this->processor)); 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 STARTTIME: xSnprintf(buffer, n, "%s", this->starttime_show); break;
case STATE: { case STATE: {
xSnprintf(buffer, n, "%c ", this->state); xSnprintf(buffer, n, "%c ", this->state);
@ -366,27 +362,25 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field
} }
case ST_UID: xSnprintf(buffer, n, "%5d ", this->st_uid); break; case ST_UID: xSnprintf(buffer, n, "%5d ", this->st_uid); break;
case TIME: Process_printTime(str, this->time); return; case TIME: Process_printTime(str, this->time); return;
case TGID: xSnprintf(buffer, n, Process_pidFormat, this->tgid); break; case TGID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->tgid); break;
case TPGID: xSnprintf(buffer, n, Process_pidFormat, this->tpgid); 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 TTY_NR: xSnprintf(buffer, n, "%3u:%3u ", major(this->tty_nr), minor(this->tty_nr)); break;
case USER: { case USER: {
if (Process_getuid != this->st_uid) if (Process_getuid != this->st_uid)
attr = CRT_colors[PROCESS_SHADOW]; attr = CRT_colors[PROCESS_SHADOW];
if (this->user) { if (this->user) {
xSnprintf(buffer, n, "%-9s ", this->user); Process_printLeftAlignedField(str, attr, this->user, 9);
} else { return;
xSnprintf(buffer, n, "%-9d ", this->st_uid);
}
if (buffer[9] != '\0') {
buffer[9] = ' ';
buffer[10] = '\0';
} }
xSnprintf(buffer, n, "%-9d ", this->st_uid);
break; break;
} }
default: default:
xSnprintf(buffer, n, "- "); xSnprintf(buffer, n, "- ");
} }
RichString_append(str, attr, buffer); RichString_appendWide(str, attr, buffer);
} }
void Process_display(const Object* cast, RichString* out) { void Process_display(const Object* cast, RichString* out) {
@ -449,13 +443,13 @@ void Process_init(Process* this, const struct Settings_* settings) {
} }
void Process_toggleTag(Process* this) { void Process_toggleTag(Process* this) {
this->tag = this->tag == true ? false : true; this->tag = !this->tag;
} }
bool Process_isNew(const Process* this) { bool Process_isNew(const Process* this) {
assert(this->processList); assert(this->processList);
if (this->processList->scanTs >= this->seenTs) { if (this->processList->scanTs >= this->seenTs) {
return this->processList->scanTs - this->seenTs <= this->processList->settings->highlightDelaySecs; return this->processList->scanTs - this->seenTs <= 1000 * this->processList->settings->highlightDelaySecs;
} }
return false; return false;
} }
@ -486,18 +480,18 @@ bool Process_sendSignal(Process* this, Arg sgn) {
return ok; return ok;
} }
long Process_pidCompare(const void* v1, const void* v2) { int Process_pidCompare(const void* v1, const void* v2) {
const Process* p1 = (const Process*)v1; const Process* p1 = (const Process*)v1;
const Process* p2 = (const Process*)v2; const Process* p2 = (const Process*)v2;
return (p1->pid - p2->pid);
return SPACESHIP_NUMBER(p1->pid, p2->pid);
} }
long Process_compare(const void* v1, const void* v2) { int Process_compare(const void* v1, const void* v2) {
const Process *p1, *p2; const Process *p1, *p2;
const Settings *settings = ((const Process*)v1)->settings; const Settings *settings = ((const Process*)v1)->settings;
int r;
if (settings->direction == 1) { if (Settings_getActiveDirection(settings) == 1) {
p1 = (const Process*)v1; p1 = (const Process*)v1;
p2 = (const Process*)v2; p2 = (const Process*)v2;
} else { } else {
@ -505,7 +499,21 @@ long Process_compare(const void* v1, const void* v2) {
p1 = (const Process*)v2; p1 = (const Process*)v2;
} }
switch (settings->sortKey) { ProcessField key = Settings_getActiveSortKey(settings);
int result = Process_compareByKey(p1, p2, key);
// Implement tie-breaker (needed to make tree mode more stable)
if (!result)
result = SPACESHIP_NUMBER(p1->pid, p2->pid);
return result;
}
int Process_compareByKey_Base(const Process* p1, const Process* p2, ProcessField key) {
int r;
switch (key) {
case PERCENT_CPU: case PERCENT_CPU:
case PERCENT_NORM_CPU: case PERCENT_NORM_CPU:
return SPACESHIP_NUMBER(p2->percent_cpu, p1->percent_cpu); return SPACESHIP_NUMBER(p2->percent_cpu, p1->percent_cpu);

View File

@ -13,17 +13,14 @@ in the source distribution for its full text.
#include <sys/types.h> #include <sys/types.h>
#include "Object.h" #include "Object.h"
#include "ProcessField.h"
#include "RichString.h" #include "RichString.h"
#ifdef __ANDROID__
#define SYS_ioprio_get __NR_ioprio_get
#define SYS_ioprio_set __NR_ioprio_set
#endif
#define PROCESS_FLAG_IO 0x0001 #define PROCESS_FLAG_IO 0x0001
#define DEFAULT_HIGHLIGHT_SECS 5 #define DEFAULT_HIGHLIGHT_SECS 5
typedef enum ProcessFields { typedef enum ProcessField_ {
NULL_PROCESSFIELD = 0, NULL_PROCESSFIELD = 0,
PID = 1, PID = 1,
COMM = 2, COMM = 2,
@ -49,12 +46,12 @@ typedef enum ProcessFields {
NLWP = 51, NLWP = 51,
TGID = 52, TGID = 52,
PERCENT_NORM_CPU = 53, PERCENT_NORM_CPU = 53,
} ProcessField;
typedef struct ProcessPidColumn_ { /* Platform specific fields, defined in ${platform}/ProcessField.h */
int id; PLATFORM_PROCESS_FIELDS
const char* label;
} ProcessPidColumn; LAST_PROCESSFIELD
} ProcessField;
struct Settings_; struct Settings_;
@ -120,30 +117,34 @@ typedef struct ProcessFieldData_ {
const char* title; const char* title;
const char* description; const char* description;
uint32_t flags; uint32_t flags;
bool pidColumn;
} ProcessFieldData; } ProcessFieldData;
// Implemented in platform-specific code: // Implemented in platform-specific code:
void Process_writeField(const Process* this, RichString* str, ProcessField field); void Process_writeField(const Process* this, RichString* str, ProcessField field);
long Process_compare(const void* v1, const void* v2); int Process_compare(const void* v1, const void* v2);
void Process_delete(Object* cast); void Process_delete(Object* cast);
bool Process_isThread(const Process* this); bool Process_isThread(const Process* this);
extern ProcessFieldData Process_fields[]; extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD];
extern ProcessPidColumn Process_pidColumns[]; #define PROCESS_MAX_PID_DIGITS 19
extern char Process_pidFormat[20]; extern int Process_pidDigits;
typedef Process*(*Process_New)(const struct Settings_*); typedef Process*(*Process_New)(const struct Settings_*);
typedef void (*Process_WriteField)(const Process*, RichString*, ProcessField); typedef void (*Process_WriteField)(const Process*, RichString*, ProcessField);
typedef int (*Process_CompareByKey)(const Process*, const Process*, ProcessField);
typedef const char* (*Process_GetCommandStr)(const Process*); typedef const char* (*Process_GetCommandStr)(const Process*);
typedef struct ProcessClass_ { typedef struct ProcessClass_ {
const ObjectClass super; const ObjectClass super;
const Process_WriteField writeField; const Process_WriteField writeField;
const Process_CompareByKey compareByKey;
const Process_GetCommandStr getCommandStr; const Process_GetCommandStr getCommandStr;
} ProcessClass; } ProcessClass;
#define As_Process(this_) ((const ProcessClass*)((this_)->super.klass)) #define As_Process(this_) ((const ProcessClass*)((this_)->super.klass))
#define Process_getCommand(this_) (As_Process(this_)->getCommandStr ? As_Process(this_)->getCommandStr((const Process*)(this_)) : ((const Process*)(this_))->comm) #define Process_getCommand(this_) (As_Process(this_)->getCommandStr ? As_Process(this_)->getCommandStr((const Process*)(this_)) : ((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) { static inline pid_t Process_getParentPid(const Process* this) {
return this->tgid == this->pid ? this->ppid : this->tgid; return this->tgid == this->pid ? this->ppid : this->tgid;
@ -178,6 +179,8 @@ void Process_fillStarttimeBuffer(Process* this);
void Process_outputRate(RichString* str, char* buffer, size_t n, double rate, int coloring); void Process_outputRate(RichString* str, char* buffer, size_t n, double rate, int coloring);
void Process_printLeftAlignedField(RichString* str, int attr, const char* content, unsigned int width);
void Process_display(const Object* cast, RichString* out); void Process_display(const Object* cast, RichString* out);
void Process_done(Process* this); void Process_done(Process* this);
@ -198,6 +201,8 @@ bool Process_changePriorityBy(Process* this, Arg delta);
bool Process_sendSignal(Process* this, Arg 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);
int Process_compareByKey_Base(const Process* p1, const Process* p2, ProcessField key);
#endif #endif

View File

@ -10,8 +10,8 @@ in the source distribution for its full text.
#include <assert.h> #include <assert.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <time.h>
#include "Compat.h"
#include "CRT.h" #include "CRT.h"
#include "Hashtable.h" #include "Hashtable.h"
#include "Macros.h" #include "Macros.h"
@ -79,22 +79,48 @@ void ProcessList_setPanel(ProcessList* this, Panel* panel) {
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) { void ProcessList_printHeader(ProcessList* this, RichString* header) {
RichString_prune(header); RichString_prune(header);
const ProcessField* fields = this->settings->fields; const Settings* settings = this->settings;
const ProcessField* fields = settings->fields;
ProcessField key = Settings_getActiveSortKey(settings);
for (int i = 0; fields[i]; i++) { for (int i = 0; fields[i]; i++) {
const char* field = Process_fields[fields[i]].title; int color;
if (!field) { if (settings->treeView && settings->treeViewAlwaysByPID) {
field = "- "; color = CRT_colors[PANEL_HEADER_FOCUS];
} else if (key == fields[i]) {
color = CRT_colors[PANEL_SELECTION_FOCUS];
} else {
color = CRT_colors[PANEL_HEADER_FOCUS];
} }
int color = (this->settings->sortKey == fields[i]) ? RichString_appendWide(header, color, alignedProcessFieldTitle(fields[i]));
CRT_colors[PANEL_SELECTION_FOCUS] : CRT_colors[PANEL_HEADER_FOCUS]; if (key == fields[i] && RichString_getCharVal(*header, RichString_size(header) - 1) == ' ') {
RichString_append(header, color, field); header->chlen--; // rewind to override space
if (COMM == fields[i] && this->settings->showMergedCommand) { RichString_appendnWide(header,
RichString_append(header, color, "(merged)"); 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)");
} }
} }
} }
@ -122,7 +148,7 @@ void ProcessList_remove(ProcessList* this, Process* p) {
Process* pp = Hashtable_remove(this->processTable, p->pid); Process* pp = Hashtable_remove(this->processTable, p->pid);
assert(pp == p); (void)pp; assert(pp == p); (void)pp;
unsigned int pid = p->pid; pid_t pid = p->pid;
int idx = Vector_indexOf(this->processes, p, Process_pidCompare); int idx = Vector_indexOf(this->processes, p, Process_pidCompare);
assert(idx != -1); assert(idx != -1);
@ -130,7 +156,12 @@ void ProcessList_remove(ProcessList* this, Process* p) {
Vector_remove(this->processes, idx); Vector_remove(this->processes, idx);
} }
assert(Hashtable_get(this->processTable, pid) == NULL); (void)pid; 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)); assert(Hashtable_count(this->processTable) == Vector_count(this->processes));
} }
@ -149,7 +180,7 @@ int ProcessList_size(ProcessList* this) {
// //
// The algorithm is based on `depth-first search`, // The algorithm is based on `depth-first search`,
// even though `breadth-first search` approach may be more efficient on first glance, // even though `breadth-first search` approach may be more efficient on first glance,
// after comparision it may be not, as it's not safe to go deeper without first updating the tree structure. // 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. // 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. // Each call of the function looks for a 'layer'. A 'layer' is a list of processes with the same depth.
@ -329,14 +360,14 @@ static void ProcessList_buildTreeBranch(ProcessList* this, pid_t pid, int level,
Vector_delete(children); Vector_delete(children);
} }
static long ProcessList_treeProcessCompare(const void* v1, const void* v2) { static int ProcessList_treeProcessCompare(const void* v1, const void* v2) {
const Process *p1 = (const Process*)v1; const Process *p1 = (const Process*)v1;
const Process *p2 = (const Process*)v2; const Process *p2 = (const Process*)v2;
return SPACESHIP_NUMBER(p1->tree_left, p2->tree_left); return SPACESHIP_NUMBER(p1->tree_left, p2->tree_left);
} }
static long ProcessList_treeProcessCompareByPID(const void* v1, const void* v2) { static int ProcessList_treeProcessCompareByPID(const void* v1, const void* v2) {
const Process *p1 = (const Process*)v1; const Process *p1 = (const Process*)v1;
const Process *p2 = (const Process*)v2; const Process *p2 = (const Process*)v2;
@ -347,7 +378,7 @@ static long ProcessList_treeProcessCompareByPID(const void* v1, const void* v2)
static void ProcessList_buildTree(ProcessList* this) { static void ProcessList_buildTree(ProcessList* this) {
int node_counter = 1; int node_counter = 1;
int node_index = 0; int node_index = 0;
int direction = this->settings->direction; int direction = Settings_getActiveDirection(this->settings);
// Sort by PID // Sort by PID
Vector_quickSortCustomCompare(this->processes, ProcessList_treeProcessCompareByPID); Vector_quickSortCustomCompare(this->processes, ProcessList_treeProcessCompareByPID);
@ -446,12 +477,7 @@ ProcessField ProcessList_keyAt(const ProcessList* this, int at) {
const ProcessField* fields = this->settings->fields; const ProcessField* fields = this->settings->fields;
ProcessField field; ProcessField field;
for (int i = 0; (field = fields[i]); i++) { for (int i = 0; (field = fields[i]); i++) {
const char* title = Process_fields[field].title; int len = strlen(alignedProcessFieldTitle(field));
if (!title) {
title = "- ";
}
int len = strlen(title);
if (at >= x && at <= x + len) { if (at >= x && at <= x + len) {
return field; return field;
} }
@ -472,30 +498,26 @@ void ProcessList_rebuildPanel(ProcessList* this) {
const char* incFilter = this->incFilter; const char* incFilter = this->incFilter;
int currPos = Panel_getSelectedIndex(this->panel); int currPos = Panel_getSelectedIndex(this->panel);
pid_t currPid = this->following != -1 ? this->following : 0;
int currScrollV = this->panel->scrollV; int currScrollV = this->panel->scrollV;
Panel_prune(this->panel); Panel_prune(this->panel);
int size = ProcessList_size(this); int size = ProcessList_size(this);
int idx = 0; int idx = 0;
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
bool hidden = false;
Process* p = ProcessList_get(this, i); Process* p = ProcessList_get(this, i);
if ( (!p->show) if ( (!p->show)
|| (this->userId != (uid_t) -1 && (p->st_uid != this->userId)) || (this->userId != (uid_t) -1 && (p->st_uid != this->userId))
|| (incFilter && !(String_contains_i(Process_getCommand(p), incFilter))) || (incFilter && !(String_contains_i(Process_getCommand(p), incFilter)))
|| (this->pidMatchList && !Hashtable_get(this->pidMatchList, p->tgid)) ) || (this->pidMatchList && !Hashtable_get(this->pidMatchList, p->tgid)) )
hidden = true; continue;
if (!hidden) { Panel_set(this->panel, idx, (Object*)p);
Panel_set(this->panel, idx, (Object*)p); if ((this->following == -1 && idx == currPos) || (this->following != -1 && p->pid == this->following)) {
if ((this->following == -1 && idx == currPos) || (this->following != -1 && p->pid == currPid)) { Panel_setSelected(this->panel, idx);
Panel_setSelected(this->panel, idx); this->panel->scrollV = currScrollV;
this->panel->scrollV = currScrollV;
}
idx++;
} }
idx++;
} }
} }
@ -541,8 +563,10 @@ void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate) {
if (!firstScanDone) { if (!firstScanDone) {
this->scanTs = 0; this->scanTs = 0;
firstScanDone = true; firstScanDone = true;
} else if (clock_gettime(CLOCK_MONOTONIC, &now) == 0) { } else if (Compat_clock_monotonic_gettime(&now) == 0) {
this->scanTs = now.tv_sec; // save time in millisecond, so with a delay in deciseconds
// there are no irregularities
this->scanTs = 1000 * now.tv_sec + now.tv_nsec / 1000000;
} }
ProcessList_goThroughEntries(this, false); ProcessList_goThroughEntries(this, false);
@ -558,7 +582,7 @@ void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate) {
// process no longer exists // process no longer exists
if (this->settings->highlightChanges && p->wasShown) { if (this->settings->highlightChanges && p->wasShown) {
// mark tombed // mark tombed
p->tombTs = this->scanTs + this->settings->highlightDelaySecs; p->tombTs = this->scanTs + 1000 * this->settings->highlightDelaySecs;
} else { } else {
// immediately remove // immediately remove
ProcessList_remove(this, p); ProcessList_remove(this, p);

View File

@ -27,7 +27,7 @@ ProcessLocksScreen* ProcessLocksScreen_new(const Process* process) {
this->pid = process->tgid; this->pid = process->tgid;
else else
this->pid = process->pid; this->pid = process->pid;
return (ProcessLocksScreen*) InfoScreen_init(&this->super, process, NULL, LINES-3, " ID TYPE EXCLUSION READ/WRITE DEVICE:INODE START END FILENAME"); 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) { void ProcessLocksScreen_delete(Object* this) {

View File

@ -7,6 +7,7 @@ in the source distribution for its full text.
#include "RichString.h" #include "RichString.h"
#include <ctype.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -47,25 +48,35 @@ static void RichString_setLen(RichString* this, int len) {
#ifdef HAVE_LIBNCURSESW #ifdef HAVE_LIBNCURSESW
static inline void RichString_writeFrom(RichString* this, int attrs, const char* data_c, int from, int len) { static inline int RichString_writeFromWide(RichString* this, int attrs, const char* data_c, int from, int len) {
wchar_t data[len + 1]; wchar_t data[len + 1];
len = mbstowcs(data, data_c, len); len = mbstowcs(data, data_c, len);
if (len < 0) if (len <= 0)
return; return 0;
int newLen = from + len; int newLen = from + len;
RichString_setLen(this, newLen); RichString_setLen(this, newLen);
for (int i = from, j = 0; i < newLen; i++, j++) { for (int i = from, j = 0; i < newLen; i++, j++) {
this->chptr[i] = (CharType) { .attr = attrs & 0xffffff, .chars = { (iswprint(data[j]) ? data[j] : '?') } }; this->chptr[i] = (CharType) { .attr = attrs & 0xffffff, .chars = { (iswprint(data[j]) ? data[j] : '?') } };
} }
return wcswidth(data, len);
} }
inline void RichString_setAttrn(RichString* this, int attrs, int start, int finish) { static inline int RichString_writeFromAscii(RichString* this, int attrs, const char* data, int from, int len) {
cchar_t* ch = this->chptr + start; int newLen = from + len;
finish = CLAMP(finish, 0, this->chlen - 1); RichString_setLen(this, newLen);
for (int i = start; i <= finish; i++) { for (int i = from, j = 0; i < newLen; i++, j++) {
ch->attr = attrs; this->chptr[i] = (CharType) { .attr = attrs & 0xffffff, .chars = { (isprint(data[j]) ? data[j] : '?') } };
ch++; }
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;
} }
} }
@ -82,21 +93,25 @@ int RichString_findChar(RichString* this, char c, int start) {
#else /* HAVE_LIBNCURSESW */ #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; int newLen = from + len;
RichString_setLen(this, newLen); RichString_setLen(this, newLen);
for (int i = from, j = 0; i < newLen; i++, j++) { for (int i = from, j = 0; i < newLen; i++, j++) {
this->chptr[i] = (((unsigned char)data_c[j]) >= 32 ? ((unsigned char)data_c[j]) : '?') | attrs; this->chptr[i] = (((unsigned char)data_c[j]) >= 32 ? ((unsigned char)data_c[j]) : '?') | attrs;
} }
this->chptr[newLen] = 0; this->chptr[newLen] = 0;
return len;
} }
void RichString_setAttrn(RichString* this, int attrs, int start, int finish) { static inline int RichString_writeFromAscii(RichString* this, int attrs, const char* data_c, int from, int len) {
chtype* ch = this->chptr + start; return RichString_writeFromWide(this, attrs, data_c, from, len);
finish = CLAMP(finish, 0, this->chlen - 1); }
for (int i = start; i <= finish; i++) {
*ch = (*ch & 0xff) | attrs; void RichString_setAttrn(RichString* this, int attrs, int start, int charcount) {
ch++; int end = CLAMP(start + charcount, 0, this->chlen);
for (int i = start; i < end; i++) {
this->chptr[i] = (this->chptr[i] & 0xff) | attrs;
} }
} }
@ -129,17 +144,29 @@ void RichString_appendChr(RichString* this, char c, int count) {
} }
void RichString_setAttr(RichString* this, int attrs) { void RichString_setAttr(RichString* this, int attrs) {
RichString_setAttrn(this, attrs, 0, this->chlen - 1); RichString_setAttrn(this, attrs, 0, this->chlen);
} }
void RichString_append(RichString* this, int attrs, const char* data) { int RichString_appendWide(RichString* this, int attrs, const char* data) {
RichString_writeFrom(this, attrs, data, this->chlen, strlen(data)); return RichString_writeFromWide(this, attrs, data, this->chlen, strlen(data));
} }
void RichString_appendn(RichString* this, int attrs, const char* data, int len) { int RichString_appendnWide(RichString* this, int attrs, const char* data, int len) {
RichString_writeFrom(this, attrs, data, this->chlen, len); return RichString_writeFromWide(this, attrs, data, this->chlen, len);
} }
void RichString_write(RichString* this, int attrs, const char* data) { int RichString_writeWide(RichString* this, int attrs, const char* data) {
RichString_writeFrom(this, attrs, data, 0, strlen(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

@ -42,7 +42,7 @@ typedef struct RichString_ {
int highlightAttr; int highlightAttr;
} RichString; } RichString;
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); int RichString_findChar(RichString* this, char c, int start);
@ -52,10 +52,16 @@ void RichString_setAttr(RichString* this, int attrs);
void RichString_appendChr(RichString* this, char c, int count); void RichString_appendChr(RichString* this, char c, int count);
void RichString_append(RichString* this, int attrs, const char* data); int RichString_appendWide(RichString* this, int attrs, const char* data);
void RichString_appendn(RichString* this, int attrs, const char* data, int len); int RichString_appendnWide(RichString* this, int attrs, const char* data, int len);
void RichString_write(RichString* this, int attrs, const char* data); int RichString_writeWide(RichString* this, int attrs, const char* data);
int RichString_appendAscii(RichString* this, int attrs, const char* data);
int RichString_appendnAscii(RichString* this, int attrs, const char* data, int len);
int RichString_writeAscii(RichString* this, int attrs, const char* data);
#endif #endif

View File

@ -106,7 +106,7 @@ static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTi
if (*rescan) { if (*rescan) {
*oldTime = newTime; *oldTime = newTime;
ProcessList_scan(pl, this->state->pauseProcessUpdate); ProcessList_scan(pl, this->state->pauseProcessUpdate);
if (*sortTimeout == 0 || this->settings->treeView) { if (!this->state->pauseProcessUpdate && (*sortTimeout == 0 || this->settings->treeView)) {
ProcessList_sort(pl); ProcessList_sort(pl);
*sortTimeout = 1; *sortTimeout = 1;
} }
@ -119,29 +119,20 @@ static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTi
*rescan = false; *rescan = false;
} }
static void ScreenManager_drawPanels(ScreenManager* this, int focus) { static void ScreenManager_drawPanels(ScreenManager* this, int focus, bool force_redraw) {
const int nPanels = this->panelCount; const int nPanels = this->panelCount;
for (int i = 0; i < nPanels; i++) { for (int i = 0; i < nPanels; i++) {
Panel* panel = (Panel*) Vector_get(this->panels, i); Panel* panel = (Panel*) Vector_get(this->panels, i);
Panel_draw(panel, i == focus, !((panel == this->state->panel) && this->state->hideProcessSelection)); 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 + 1); mvvline(panel->y, panel->x + panel->w, ' ', panel->h + (State_hideFunctionBar(this->state) ? 1 : 0));
} }
} }
static Panel* setCurrentPanel(const ScreenManager* this, Panel* panel) {
FunctionBar_draw(panel->currentBar);
if (panel == this->state->panel && this->state->pauseProcessUpdate) {
FunctionBar_append("PAUSED", CRT_colors[PAUSED]);
}
return panel;
}
void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) { void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
bool quit = false; bool quit = false;
int focus = 0; int focus = 0;
Panel* panelFocus = setCurrentPanel(this, (Panel*) Vector_get(this->panels, focus)); Panel* panelFocus = (Panel*) Vector_get(this->panels, focus);
double oldTime = 0.0; double oldTime = 0.0;
@ -150,6 +141,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
bool timedOut = true; bool timedOut = true;
bool redraw = true; bool redraw = true;
bool force_redraw = true;
bool rescan = false; bool rescan = false;
int sortTimeout = 0; int sortTimeout = 0;
int resetSortTimeout = 5; int resetSortTimeout = 5;
@ -159,8 +151,9 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
checkRecalculation(this, &oldTime, &sortTimeout, &redraw, &rescan, &timedOut); checkRecalculation(this, &oldTime, &sortTimeout, &redraw, &rescan, &timedOut);
} }
if (redraw) { if (redraw || force_redraw) {
ScreenManager_drawPanels(this, focus); ScreenManager_drawPanels(this, focus, force_redraw);
force_redraw = false;
} }
int prevCh = ch; int prevCh = ch;
@ -187,7 +180,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
ch = KEY_MOUSE; ch = KEY_MOUSE;
if (panel == panelFocus || this->allowFocusChange) { if (panel == panelFocus || this->allowFocusChange) {
focus = i; focus = i;
panelFocus = setCurrentPanel(this, panel); panelFocus = panel;
Object* oldSelection = Panel_getSelected(panel); Object* oldSelection = Panel_getSelected(panel);
Panel_setSelected(panel, mevent.y - panel->y + panel->scrollV - 1); Panel_setSelected(panel, mevent.y - panel->y + panel->scrollV - 1);
if (Panel_getSelected(panel) == oldSelection) { if (Panel_getSelected(panel) == oldSelection) {
@ -234,9 +227,12 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
if (result & SYNTH_KEY) { if (result & SYNTH_KEY) {
ch = result >> 16; ch = result >> 16;
} }
if (result & REDRAW) { if (result & REFRESH) {
sortTimeout = 0; sortTimeout = 0;
} }
if (result & REDRAW) {
force_redraw = true;
}
if (result & RESCAN) { if (result & RESCAN) {
rescan = true; rescan = true;
sortTimeout = 0; sortTimeout = 0;
@ -269,7 +265,7 @@ tryLeft:
focus--; focus--;
} }
panelFocus = setCurrentPanel(this, (Panel*) Vector_get(this->panels, focus)); panelFocus = (Panel*) Vector_get(this->panels, focus);
if (Panel_size(panelFocus) == 0 && focus > 0) { if (Panel_size(panelFocus) == 0 && focus > 0) {
goto tryLeft; goto tryLeft;
} }
@ -290,7 +286,7 @@ tryRight:
focus++; focus++;
} }
panelFocus = setCurrentPanel(this, (Panel*) Vector_get(this->panels, focus)); panelFocus = (Panel*) Vector_get(this->panels, focus);
if (Panel_size(panelFocus) == 0 && focus < this->panelCount - 1) { if (Panel_size(panelFocus) == 0 && focus < this->panelCount - 1) {
goto tryRight; goto tryRight;
} }

View File

@ -96,10 +96,10 @@ static void readFields(ProcessField* fields, uint32_t* flags, const char* line)
free(trim); free(trim);
int i, j; int i, j;
*flags = 0; *flags = 0;
for (j = 0, i = 0; i < Platform_numberOfFields && ids[i]; i++) { for (j = 0, i = 0; i < LAST_PROCESSFIELD && ids[i]; i++) {
// This "+1" is for compatibility with the older enum format. // This "+1" is for compatibility with the older enum format.
int id = atoi(ids[i]) + 1; int id = atoi(ids[i]) + 1;
if (id > 0 && id < Platform_numberOfFields && Process_fields[id].name) { if (id > 0 && id < LAST_PROCESSFIELD && Process_fields[id].name) {
fields[j] = id; fields[j] = id;
*flags |= Process_fields[id].flags; *flags |= Process_fields[id].flags;
j++; j++;
@ -137,10 +137,17 @@ static bool Settings_read(Settings* this, const char* fileName, int initialCpuCo
} else if (String_eq(option[0], "sort_key")) { } else if (String_eq(option[0], "sort_key")) {
// This "+1" is for compatibility with the older enum format. // This "+1" is for compatibility with the older enum format.
this->sortKey = atoi(option[1]) + 1; this->sortKey = atoi(option[1]) + 1;
} else if (String_eq(option[0], "tree_sort_key")) {
// This "+1" is for compatibility with the older enum format.
this->treeSortKey = atoi(option[1]) + 1;
} else if (String_eq(option[0], "sort_direction")) { } else if (String_eq(option[0], "sort_direction")) {
this->direction = atoi(option[1]); this->direction = atoi(option[1]);
} else if (String_eq(option[0], "tree_sort_direction")) {
this->treeDirection = atoi(option[1]);
} else if (String_eq(option[0], "tree_view")) { } else if (String_eq(option[0], "tree_view")) {
this->treeView = atoi(option[1]); this->treeView = atoi(option[1]);
} else if (String_eq(option[0], "tree_view_always_by_pid")) {
this->treeViewAlwaysByPID = atoi(option[1]);
} else if (String_eq(option[0], "hide_kernel_threads")) { } else if (String_eq(option[0], "hide_kernel_threads")) {
this->hideKernelThreads = atoi(option[1]); this->hideKernelThreads = atoi(option[1]);
} else if (String_eq(option[0], "hide_userland_threads")) { } else if (String_eq(option[0], "hide_userland_threads")) {
@ -214,6 +221,8 @@ static bool Settings_read(Settings* this, const char* fileName, int initialCpuCo
} else if (String_eq(option[0], "right_meter_modes")) { } else if (String_eq(option[0], "right_meter_modes")) {
Settings_readMeterModes(this, option[1], 1); Settings_readMeterModes(this, option[1], 1);
didReadMeters = true; didReadMeters = true;
} else if (String_eq(option[0], "hide_function_bar")) {
this->hideFunctionBar = atoi(option[1]);
#ifdef HAVE_LIBHWLOC #ifdef HAVE_LIBHWLOC
} else if (String_eq(option[0], "topology_affinity")) { } else if (String_eq(option[0], "topology_affinity")) {
this->topologyAffinity = !!atoi(option[1]); this->topologyAffinity = !!atoi(option[1]);
@ -273,6 +282,8 @@ bool Settings_write(Settings* this) {
// This "-1" is for compatibility with the older enum format. // This "-1" is for compatibility with the older enum format.
fprintf(fd, "sort_key=%d\n", (int) this->sortKey - 1); fprintf(fd, "sort_key=%d\n", (int) this->sortKey - 1);
fprintf(fd, "sort_direction=%d\n", (int) this->direction); fprintf(fd, "sort_direction=%d\n", (int) this->direction);
fprintf(fd, "tree_sort_key=%d\n", (int) this->treeSortKey - 1);
fprintf(fd, "tree_sort_direction=%d\n", (int) this->treeDirection);
fprintf(fd, "hide_kernel_threads=%d\n", (int) this->hideKernelThreads); fprintf(fd, "hide_kernel_threads=%d\n", (int) this->hideKernelThreads);
fprintf(fd, "hide_userland_threads=%d\n", (int) this->hideUserlandThreads); fprintf(fd, "hide_userland_threads=%d\n", (int) this->hideUserlandThreads);
fprintf(fd, "shadow_other_users=%d\n", (int) this->shadowOtherUsers); fprintf(fd, "shadow_other_users=%d\n", (int) this->shadowOtherUsers);
@ -287,6 +298,7 @@ bool Settings_write(Settings* this) {
fprintf(fd, "strip_exe_from_cmdline=%d\n", (int) this->stripExeFromCmdline); fprintf(fd, "strip_exe_from_cmdline=%d\n", (int) this->stripExeFromCmdline);
fprintf(fd, "show_merged_command=%d\n", (int) this->showMergedCommand); fprintf(fd, "show_merged_command=%d\n", (int) this->showMergedCommand);
fprintf(fd, "tree_view=%d\n", (int) this->treeView); fprintf(fd, "tree_view=%d\n", (int) this->treeView);
fprintf(fd, "tree_view_always_by_pid=%d\n", (int) this->treeViewAlwaysByPID);
fprintf(fd, "header_margin=%d\n", (int) this->headerMargin); fprintf(fd, "header_margin=%d\n", (int) this->headerMargin);
fprintf(fd, "detailed_cpu_time=%d\n", (int) this->detailedCPUTime); fprintf(fd, "detailed_cpu_time=%d\n", (int) this->detailedCPUTime);
fprintf(fd, "cpu_count_from_one=%d\n", (int) this->countCPUsFromOne); fprintf(fd, "cpu_count_from_one=%d\n", (int) this->countCPUsFromOne);
@ -305,6 +317,7 @@ bool Settings_write(Settings* this) {
fprintf(fd, "left_meter_modes="); writeMeterModes(this, fd, 0); fprintf(fd, "left_meter_modes="); writeMeterModes(this, fd, 0);
fprintf(fd, "right_meters="); writeMeters(this, fd, 1); fprintf(fd, "right_meters="); writeMeters(this, fd, 1);
fprintf(fd, "right_meter_modes="); writeMeterModes(this, fd, 1); fprintf(fd, "right_meter_modes="); writeMeterModes(this, fd, 1);
fprintf(fd, "hide_function_bar=%d\n", (int) this->hideFunctionBar);
#ifdef HAVE_LIBHWLOC #ifdef HAVE_LIBHWLOC
fprintf(fd, "topology_affinity=%d\n", (int) this->topologyAffinity); fprintf(fd, "topology_affinity=%d\n", (int) this->topologyAffinity);
#endif #endif
@ -316,7 +329,9 @@ Settings* Settings_new(int initialCpuCount) {
Settings* this = xCalloc(1, sizeof(Settings)); Settings* this = xCalloc(1, sizeof(Settings));
this->sortKey = PERCENT_CPU; this->sortKey = PERCENT_CPU;
this->treeSortKey = PID;
this->direction = 1; this->direction = 1;
this->treeDirection = 1;
this->shadowOtherUsers = false; this->shadowOtherUsers = false;
this->showThreadNames = false; this->showThreadNames = false;
this->hideKernelThreads = false; this->hideKernelThreads = false;
@ -340,14 +355,15 @@ Settings* Settings_new(int initialCpuCount) {
this->findCommInCmdline = true; this->findCommInCmdline = true;
this->stripExeFromCmdline = true; this->stripExeFromCmdline = true;
this->showMergedCommand = false; this->showMergedCommand = false;
this->hideFunctionBar = 0;
#ifdef HAVE_LIBHWLOC #ifdef HAVE_LIBHWLOC
this->topologyAffinity = false; this->topologyAffinity = false;
#endif #endif
this->fields = xCalloc(Platform_numberOfFields + 1, sizeof(ProcessField)); this->fields = xCalloc(LAST_PROCESSFIELD + 1, sizeof(ProcessField));
// TODO: turn 'fields' into a Vector, // TODO: turn 'fields' into a Vector,
// (and ProcessFields into proper objects). // (and ProcessFields into proper objects).
this->flags = 0; this->flags = 0;
ProcessField* defaults = Platform_defaultFields; const ProcessField* defaults = Platform_defaultFields;
for (int i = 0; defaults[i]; i++) { for (int i = 0; defaults[i]; i++) {
this->fields[i] = defaults[i]; this->fields[i] = defaults[i];
this->flags |= Process_fields[defaults[i]].flags; this->flags |= Process_fields[defaults[i]].flags;
@ -427,9 +443,17 @@ Settings* Settings_new(int initialCpuCount) {
} }
void Settings_invertSortOrder(Settings* this) { void Settings_invertSortOrder(Settings* this) {
if (this->direction == 1) { int* attr = (this->treeView) ? &(this->treeDirection) : &(this->direction);
this->direction = -1; *attr = (*attr == 1) ? -1 : 1;
} else { }
void Settings_setSortKey(Settings* this, ProcessField sortKey) {
if (this->treeViewAlwaysByPID || !this->treeView) {
this->sortKey = sortKey;
this->direction = 1; this->direction = 1;
this->treeView = false;
} else {
this->treeSortKey = sortKey;
this->treeDirection = 1;
} }
} }

View File

@ -33,7 +33,9 @@ typedef struct Settings_ {
int delay; int delay;
int direction; int direction;
int treeDirection;
ProcessField sortKey; ProcessField sortKey;
ProcessField treeSortKey;
bool countCPUsFromOne; bool countCPUsFromOne;
bool detailedCPUTime; bool detailedCPUTime;
@ -44,6 +46,7 @@ typedef struct Settings_ {
bool degreeFahrenheit; bool degreeFahrenheit;
#endif #endif
bool treeView; bool treeView;
bool treeViewAlwaysByPID;
bool showProgramPath; bool showProgramPath;
bool shadowOtherUsers; bool shadowOtherUsers;
bool showThreadNames; bool showThreadNames;
@ -61,6 +64,7 @@ typedef struct Settings_ {
bool accountGuestInCPUMeter; bool accountGuestInCPUMeter;
bool headerMargin; bool headerMargin;
bool enableMouse; bool enableMouse;
int hideFunctionBar; // 0 - off, 1 - on ESC until next input, 2 - permanently
#ifdef HAVE_LIBHWLOC #ifdef HAVE_LIBHWLOC
bool topologyAffinity; bool topologyAffinity;
#endif #endif
@ -70,6 +74,16 @@ typedef struct Settings_ {
#define Settings_cpuId(settings, cpu) ((settings)->countCPUsFromOne ? (cpu)+1 : (cpu)) #define Settings_cpuId(settings, cpu) ((settings)->countCPUsFromOne ? (cpu)+1 : (cpu))
static inline ProcessField Settings_getActiveSortKey(const Settings* this) {
return (this->treeView)
? (this->treeViewAlwaysByPID ? PID : this->treeSortKey)
: this->sortKey;
}
static inline int Settings_getActiveDirection(const Settings* this) {
return this->treeView ? this->treeDirection : this->direction;
}
void Settings_delete(Settings* this); void Settings_delete(Settings* this);
bool Settings_write(Settings* this); bool Settings_write(Settings* this);
@ -78,4 +92,6 @@ Settings* Settings_new(int initialCpuCount);
void Settings_invertSortOrder(Settings* this); void Settings_invertSortOrder(Settings* this);
void Settings_setSortKey(Settings* this, ProcessField sortKey);
#endif #endif

View File

@ -19,7 +19,7 @@ in the source distribution for its full text.
Panel* SignalsPanel_new() { Panel* SignalsPanel_new() {
Panel* this = Panel_new(1, 1, 1, 1, true, Class(ListItem), FunctionBar_newEnterEsc("Send ", "Cancel ")); Panel* this = Panel_new(1, 1, 1, 1, Class(ListItem), true, FunctionBar_newEnterEsc("Send ", "Cancel "));
const int defaultSignal = SIGTERM; const int defaultSignal = SIGTERM;
int defaultPosition = 15; int defaultPosition = 15;
unsigned int i; unsigned int i;

View File

@ -32,12 +32,12 @@ static void SwapMeter_updateValues(Meter* this, char* buffer, size_t size) {
static void SwapMeter_display(const Object* cast, RichString* out) { static void SwapMeter_display(const Object* cast, RichString* out) {
char buffer[50]; char buffer[50];
const Meter* this = (const Meter*)cast; const Meter* this = (const Meter*)cast;
RichString_write(out, CRT_colors[METER_TEXT], ":"); RichString_writeAscii(out, CRT_colors[METER_TEXT], ":");
Meter_humanUnit(buffer, this->total, 50); Meter_humanUnit(buffer, this->total, sizeof(buffer));
RichString_append(out, CRT_colors[METER_VALUE], buffer); RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer);
Meter_humanUnit(buffer, this->values[0], 50); Meter_humanUnit(buffer, this->values[0], sizeof(buffer));
RichString_append(out, CRT_colors[METER_TEXT], " used:"); RichString_appendAscii(out, CRT_colors[METER_TEXT], " used:");
RichString_append(out, CRT_colors[METER_VALUE], buffer); RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer);
} }
const MeterClass SwapMeter_class = { const MeterClass SwapMeter_class = {

View File

@ -46,7 +46,7 @@ static void TasksMeter_display(const Object* cast, RichString* out) {
int processes = (int) this->values[2]; int processes = (int) this->values[2];
xSnprintf(buffer, sizeof(buffer), "%d", processes); xSnprintf(buffer, sizeof(buffer), "%d", processes);
RichString_write(out, CRT_colors[METER_VALUE], buffer); RichString_writeAscii(out, CRT_colors[METER_VALUE], buffer);
int threadValueColor = CRT_colors[METER_VALUE]; int threadValueColor = CRT_colors[METER_VALUE];
int threadCaptionColor = CRT_colors[METER_TEXT]; int threadCaptionColor = CRT_colors[METER_TEXT];
if (settings->highlightThreads) { if (settings->highlightThreads) {
@ -54,21 +54,21 @@ static void TasksMeter_display(const Object* cast, RichString* out) {
threadCaptionColor = CRT_colors[PROCESS_THREAD]; threadCaptionColor = CRT_colors[PROCESS_THREAD];
} }
if (!settings->hideUserlandThreads) { if (!settings->hideUserlandThreads) {
RichString_append(out, CRT_colors[METER_TEXT], ", "); RichString_appendAscii(out, CRT_colors[METER_TEXT], ", ");
xSnprintf(buffer, sizeof(buffer), "%d", (int)this->values[1]); xSnprintf(buffer, sizeof(buffer), "%d", (int)this->values[1]);
RichString_append(out, threadValueColor, buffer); RichString_appendAscii(out, threadValueColor, buffer);
RichString_append(out, threadCaptionColor, " thr"); RichString_appendAscii(out, threadCaptionColor, " thr");
} }
if (!settings->hideKernelThreads) { if (!settings->hideKernelThreads) {
RichString_append(out, CRT_colors[METER_TEXT], ", "); RichString_appendAscii(out, CRT_colors[METER_TEXT], ", ");
xSnprintf(buffer, sizeof(buffer), "%d", (int)this->values[0]); xSnprintf(buffer, sizeof(buffer), "%d", (int)this->values[0]);
RichString_append(out, threadValueColor, buffer); RichString_appendAscii(out, threadValueColor, buffer);
RichString_append(out, threadCaptionColor, " kthr"); RichString_appendAscii(out, threadCaptionColor, " kthr");
} }
RichString_append(out, CRT_colors[METER_TEXT], "; "); RichString_appendAscii(out, CRT_colors[METER_TEXT], "; ");
xSnprintf(buffer, sizeof(buffer), "%d", (int)this->values[3]); xSnprintf(buffer, sizeof(buffer), "%d", (int)this->values[3]);
RichString_append(out, CRT_colors[TASKS_RUNNING], buffer); RichString_appendAscii(out, CRT_colors[TASKS_RUNNING], buffer);
RichString_append(out, CRT_colors[METER_TEXT], " running"); RichString_appendAscii(out, CRT_colors[METER_TEXT], " running");
} }
const MeterClass TasksMeter_class = { const MeterClass TasksMeter_class = {

View File

@ -33,17 +33,7 @@ static const char* const TraceScreenFunctions[] = {"Search ", "Filter ", "AutoSc
static const char* const TraceScreenKeys[] = {"F3", "F4", "F8", "F9", "Esc"}; static const char* const TraceScreenKeys[] = {"F3", "F4", "F8", "F9", "Esc"};
static int TraceScreenEvents[] = {KEY_F(3), KEY_F(4), KEY_F(8), KEY_F(9), 27}; static const int TraceScreenEvents[] = {KEY_F(3), KEY_F(4), KEY_F(8), KEY_F(9), 27};
const InfoScreenClass TraceScreen_class = {
.super = {
.extends = Class(Object),
.delete = TraceScreen_delete
},
.draw = TraceScreen_draw,
.onErr = TraceScreen_updateTrace,
.onKey = TraceScreen_onKey,
};
TraceScreen* TraceScreen_new(Process* process) { TraceScreen* TraceScreen_new(Process* process) {
// This initializes all TraceScreen variables to "false" so only default = true ones need to be set below // This initializes all TraceScreen variables to "false" so only default = true ones need to be set below
@ -70,12 +60,8 @@ void TraceScreen_delete(Object* cast) {
free(InfoScreen_done((InfoScreen*)this)); free(InfoScreen_done((InfoScreen*)this));
} }
void TraceScreen_draw(InfoScreen* this) { static void TraceScreen_draw(InfoScreen* this) {
attrset(CRT_colors[PANEL_HEADER_FOCUS]); InfoScreen_drawTitled(this, "Trace of process %d - %s", this->process->pid, Process_getCommand(this->process));
mvhline(0, 0, ' ', COLS);
mvprintw(0, 0, "Trace of process %d - %s", this->process->pid, Process_getCommand(this->process));
attrset(CRT_colors[DEFAULT_COLOR]);
IncSet_drawBar(this->inc);
} }
bool TraceScreen_forkTracer(TraceScreen* this) { bool TraceScreen_forkTracer(TraceScreen* this) {
@ -131,7 +117,7 @@ err:
return false; return false;
} }
void TraceScreen_updateTrace(InfoScreen* super) { static void TraceScreen_updateTrace(InfoScreen* super) {
TraceScreen* this = (TraceScreen*) super; TraceScreen* this = (TraceScreen*) super;
char buffer[1025]; char buffer[1025];
@ -176,7 +162,7 @@ void TraceScreen_updateTrace(InfoScreen* super) {
} }
} }
bool TraceScreen_onKey(InfoScreen* super, int ch) { static bool TraceScreen_onKey(InfoScreen* super, int ch) {
TraceScreen* this = (TraceScreen*) super; TraceScreen* this = (TraceScreen*) super;
switch(ch) { switch(ch) {
case 'f': case 'f':
@ -195,3 +181,13 @@ bool TraceScreen_onKey(InfoScreen* super, int ch) {
this->follow = false; this->follow = false;
return false; return false;
} }
const InfoScreenClass TraceScreen_class = {
.super = {
.extends = Class(Object),
.delete = TraceScreen_delete
},
.draw = TraceScreen_draw,
.onErr = TraceScreen_updateTrace,
.onKey = TraceScreen_onKey,
};

View File

@ -32,12 +32,6 @@ TraceScreen* TraceScreen_new(Process* process);
void TraceScreen_delete(Object* cast); void TraceScreen_delete(Object* cast);
void TraceScreen_draw(InfoScreen* this);
bool TraceScreen_forkTracer(TraceScreen* this); bool TraceScreen_forkTracer(TraceScreen* this);
void TraceScreen_updateTrace(InfoScreen* super);
bool TraceScreen_onKey(InfoScreen* super, int ch);
#endif #endif

View File

@ -58,7 +58,7 @@ unsigned int Vector_count(const Vector* this);
#else /* NDEBUG */ #else /* NDEBUG */
static inline Object* Vector_get(Vector* this, int idx) { static inline Object* Vector_get(const Vector* this, int idx) {
return this->array[idx]; return this->array[idx];
} }

View File

@ -13,6 +13,7 @@ in the source distribution for its full text.
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
@ -36,9 +37,21 @@ void* xMalloc(size_t size) {
return data; return data;
} }
void* xMallocArray(size_t nmemb, size_t size) {
assert(nmemb > 0);
assert(size > 0);
if (SIZE_MAX / nmemb < size) {
fail();
}
return xMalloc(nmemb * size);
}
void* xCalloc(size_t nmemb, size_t size) { void* xCalloc(size_t nmemb, size_t size) {
assert(nmemb > 0); assert(nmemb > 0);
assert(size > 0); assert(size > 0);
if (SIZE_MAX / nmemb < size) {
fail();
}
void* data = calloc(nmemb, size); void* data = calloc(nmemb, size);
if (!data) { if (!data) {
fail(); fail();
@ -56,6 +69,15 @@ void* xRealloc(void* ptr, size_t size) {
return data; return data;
} }
void* xReallocArray(void* ptr, size_t nmemb, size_t size) {
assert(nmemb > 0);
assert(size > 0);
if (SIZE_MAX / nmemb < size) {
fail();
}
return xRealloc(ptr, nmemb * size);
}
char* String_cat(const char* s1, const char* s2) { char* String_cat(const char* s1, const char* s2) {
const size_t l1 = strlen(s1); const size_t l1 = strlen(s1);
const size_t l2 = strlen(s2); const size_t l2 = strlen(s2);

View File

@ -23,10 +23,14 @@ void fail(void) ATTR_NORETURN;
void* xMalloc(size_t size); void* xMalloc(size_t size);
void* xMallocArray(size_t nmemb, size_t size);
void* xCalloc(size_t nmemb, size_t size); void* xCalloc(size_t nmemb, size_t size);
void* xRealloc(void* ptr, size_t size); void* xRealloc(void* ptr, size_t size);
void* xReallocArray(void* ptr, size_t nmemb, size_t size);
/* /*
* String_startsWith gives better performance if strlen(match) can be computed * String_startsWith gives better performance if strlen(match) can be computed
* at compile time (e.g. when they are immutable string literals). :) * at compile time (e.g. when they are immutable string literals). :)

View File

@ -2,7 +2,7 @@
# Process this file with autoconf to produce a configure script. # Process this file with autoconf to produce a configure script.
AC_PREREQ(2.65) AC_PREREQ(2.65)
AC_INIT([htop],[3.0.3],[htop@groups.io]) AC_INIT([htop],[3.0.5],[htop@groups.io])
AC_CONFIG_SRCDIR([htop.c]) AC_CONFIG_SRCDIR([htop.c])
AC_CONFIG_AUX_DIR([.]) AC_CONFIG_AUX_DIR([.])
@ -93,13 +93,16 @@ AC_TYPE_UINT64_T
AC_FUNC_CLOSEDIR_VOID AC_FUNC_CLOSEDIR_VOID
AC_FUNC_STAT AC_FUNC_STAT
AC_CHECK_FUNCS([\ AC_CHECK_FUNCS([\
clock_gettime\
faccessat\ faccessat\
fstatat\ fstatat\
host_get_clock_service\
openat\ openat\
readlinkat\ readlinkat\
]) ])
AC_SEARCH_LIBS([dlopen], [dl dld]) AC_SEARCH_LIBS([dlopen], [dl dld])
AC_SEARCH_LIBS([clock_gettime], [rt])
save_cflags="${CFLAGS}" save_cflags="${CFLAGS}"
CFLAGS="${CFLAGS} -std=c99" CFLAGS="${CFLAGS} -std=c99"
@ -165,7 +168,7 @@ m4_define([HTOP_CHECK_SCRIPT],
LIBS="$htop_config_script_libs $LIBS " LIBS="$htop_config_script_libs $LIBS "
htop_script_success=yes htop_script_success=yes
], [ ], [
CFLAGS="$htop_save_CFLAGS" CFLAGS="$htop_save_CFLAGS"
]) ])
LDFLAGS="$htop_save_LDFLAGS" LDFLAGS="$htop_save_LDFLAGS"
fi fi
@ -188,15 +191,15 @@ AC_DEFUN([AX_CHECK_COMPILE_FLAG],
[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF [AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF
AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl
AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [ AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [
ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS
_AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1" _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1"
AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])],
[AS_VAR_SET(CACHEVAR,[yes])], [AS_VAR_SET(CACHEVAR,[yes])],
[AS_VAR_SET(CACHEVAR,[no])]) [AS_VAR_SET(CACHEVAR,[no])])
_AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags]) _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags])
AS_VAR_IF(CACHEVAR,yes, AS_VAR_IF(CACHEVAR,yes,
[m4_default([$2], :)], [m4_default([$2], :)],
[m4_default([$3], :)]) [m4_default([$3], :)])
AS_VAR_POPDEF([CACHEVAR])dnl AS_VAR_POPDEF([CACHEVAR])dnl
])dnl AX_CHECK_COMPILE_FLAGS ])dnl AX_CHECK_COMPILE_FLAGS
@ -209,14 +212,18 @@ if test "x$enable_unicode" = xyes; then
HTOP_CHECK_LIB([ncursesw6], [addnwstr], [HAVE_LIBNCURSESW], HTOP_CHECK_LIB([ncursesw6], [addnwstr], [HAVE_LIBNCURSESW],
HTOP_CHECK_LIB([ncursesw], [addnwstr], [HAVE_LIBNCURSESW], HTOP_CHECK_LIB([ncursesw], [addnwstr], [HAVE_LIBNCURSESW],
HTOP_CHECK_LIB([ncurses], [addnwstr], [HAVE_LIBNCURSESW], HTOP_CHECK_LIB([ncurses], [addnwstr], [HAVE_LIBNCURSESW],
missing_libraries="$missing_libraries libncursesw" missing_libraries="$missing_libraries libncursesw"
AC_MSG_ERROR([You may want to use --disable-unicode or install libncursesw.]) AC_MSG_ERROR([You may want to use --disable-unicode or install libncursesw.])
))))))) )))))))
AC_CHECK_HEADERS([ncursesw/curses.h],[:], AC_CHECK_HEADERS([ncursesw/curses.h],[:],
[AC_CHECK_HEADERS([ncurses/ncurses.h],[:], [AC_CHECK_HEADERS([ncurses/ncurses.h],[:],
[AC_CHECK_HEADERS([ncurses/curses.h],[:], [AC_CHECK_HEADERS([ncurses/curses.h],[:],
[AC_CHECK_HEADERS([ncurses.h],[:],[missing_headers="$missing_headers $ac_header"])])])]) [AC_CHECK_HEADERS([ncurses.h],[:],[missing_headers="$missing_headers $ac_header"])])])])
# check if additional linker flags are needed for keypad(3)
# (at this point we already link against a working ncurses library with wide character support)
AC_SEARCH_LIBS([keypad], [tinfow tinfo])
else else
HTOP_CHECK_SCRIPT([ncurses6], [refresh], [HAVE_LIBNCURSES], "ncurses6-config", HTOP_CHECK_SCRIPT([ncurses6], [refresh], [HAVE_LIBNCURSES], "ncurses6-config",
HTOP_CHECK_SCRIPT([ncurses], [refresh], [HAVE_LIBNCURSES], "ncurses5-config", HTOP_CHECK_SCRIPT([ncurses], [refresh], [HAVE_LIBNCURSES], "ncurses5-config",
@ -229,6 +236,19 @@ else
[AC_CHECK_HEADERS([ncurses/curses.h],[:], [AC_CHECK_HEADERS([ncurses/curses.h],[:],
[AC_CHECK_HEADERS([ncurses/ncurses.h],[:], [AC_CHECK_HEADERS([ncurses/ncurses.h],[:],
[AC_CHECK_HEADERS([ncurses.h],[:],[missing_headers="$missing_headers $ac_header"])])])]) [AC_CHECK_HEADERS([ncurses.h],[:],[missing_headers="$missing_headers $ac_header"])])])])
# check if additional linker flags are needed for keypad(3)
# (at this point we already link against a working ncurses library)
AC_SEARCH_LIBS([keypad], [tinfo])
fi
if test "$my_htop_platform" = "darwin"; then
AC_CHECK_HEADERS([mach/mach_time.h])
AC_CHECK_FUNCS([mach_timebase_info])
fi
if test "$my_htop_platform" = "dragonflybsd"; then
AC_CHECK_LIB([kvm], [kvm_open], [], [missing_libraries="$missing_libraries libkvm"])
fi fi
if test "$my_htop_platform" = "freebsd"; then if test "$my_htop_platform" = "freebsd"; then
@ -246,8 +266,7 @@ if test "$my_htop_platform" = "solaris"; then
fi fi
AC_ARG_ENABLE(hwloc, [AS_HELP_STRING([--enable-hwloc], [enable hwloc support for CPU affinity, disables Linux affinity])],, enable_hwloc="no") AC_ARG_ENABLE(hwloc, [AS_HELP_STRING([--enable-hwloc], [enable hwloc support for CPU affinity, disables Linux affinity])],, enable_hwloc="no")
if test "x$enable_hwloc" = xyes if test "x$enable_hwloc" = xyes; then
then
AC_CHECK_LIB([hwloc], [hwloc_get_proc_cpubind], [], [missing_libraries="$missing_libraries libhwloc"]) AC_CHECK_LIB([hwloc], [hwloc_get_proc_cpubind], [], [missing_libraries="$missing_libraries libhwloc"])
AC_CHECK_HEADERS([hwloc.h],[:], [missing_headers="$missing_headers $ac_header"]) AC_CHECK_HEADERS([hwloc.h],[:], [missing_headers="$missing_headers $ac_header"])
fi fi
@ -269,9 +288,9 @@ if test "x$enable_linux_affinity" = xcheck; then
if (errno == ENOSYS) return 1; if (errno == ENOSYS) return 1;
]])], ]])],
[enable_linux_affinity=yes [enable_linux_affinity=yes
AC_MSG_RESULT([yes])], AC_MSG_RESULT([yes])],
[enable_linux_affinity=no [enable_linux_affinity=no
AC_MSG_RESULT([no])], AC_MSG_RESULT([no])],
[AC_MSG_RESULT([yes (assumed while cross compiling)])]) [AC_MSG_RESULT([yes (assumed while cross compiling)])])
fi fi
fi fi
@ -279,20 +298,17 @@ if test "x$enable_linux_affinity" = xyes; then
AC_DEFINE(HAVE_LINUX_AFFINITY, 1, [Define if Linux sched_setaffinity and sched_getaffinity are to be used.]) AC_DEFINE(HAVE_LINUX_AFFINITY, 1, [Define if Linux sched_setaffinity and sched_getaffinity are to be used.])
fi fi
if test "x$enable_linux_affinity" = xyes -a "x$enable_hwloc" = xyes if test "x$enable_linux_affinity" = xyes -a "x$enable_hwloc" = xyes; then
then
AC_MSG_ERROR([--enable-hwloc and --enable-linux-affinity are mutual exclusive. Specify at most one of them.]) AC_MSG_ERROR([--enable-hwloc and --enable-linux-affinity are mutual exclusive. Specify at most one of them.])
fi fi
AC_ARG_ENABLE(setuid, [AS_HELP_STRING([--enable-setuid], [enable setuid support for platforms that need it])],, enable_setuid="no") AC_ARG_ENABLE(setuid, [AS_HELP_STRING([--enable-setuid], [enable setuid support for platforms that need it])],, enable_setuid="no")
if test "x$enable_setuid" = xyes if test "x$enable_setuid" = xyes; then
then
AC_DEFINE(HAVE_SETUID_ENABLED, 1, [Define if setuid support should be enabled.]) AC_DEFINE(HAVE_SETUID_ENABLED, 1, [Define if setuid support should be enabled.])
fi fi
AC_ARG_ENABLE(delayacct, [AS_HELP_STRING([--enable-delayacct], [enable Linux delay accounting])],, enable_delayacct="no") AC_ARG_ENABLE(delayacct, [AS_HELP_STRING([--enable-delayacct], [enable Linux delay accounting])],, enable_delayacct="no")
if test "x$enable_delayacct" = xyes if test "x$enable_delayacct" = xyes; then
then
m4_ifdef([PKG_PROG_PKG_CONFIG], [ m4_ifdef([PKG_PROG_PKG_CONFIG], [
PKG_PROG_PKG_CONFIG() PKG_PROG_PKG_CONFIG()
PKG_CHECK_MODULES(LIBNL3, libnl-3.0, [], [missing_libraries="$missing_libraries libnl-3"]) PKG_CHECK_MODULES(LIBNL3, libnl-3.0, [], [missing_libraries="$missing_libraries libnl-3"])
@ -321,6 +337,7 @@ AM_CFLAGS="\
-Wcast-qual\ -Wcast-qual\
-Wextra\ -Wextra\
-Wfloat-equal\ -Wfloat-equal\
-Wformat=2\
-Wmissing-format-attribute\ -Wmissing-format-attribute\
-Wmissing-noreturn\ -Wmissing-noreturn\
-Wmissing-prototypes\ -Wmissing-prototypes\
@ -346,10 +363,10 @@ AC_SUBST([AM_CPPFLAGS])
# Bail out on errors. # Bail out on errors.
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
if test ! -z "$missing_libraries"; then if test ! -z "$missing_libraries"; then
AC_MSG_ERROR([missing libraries: $missing_libraries]) AC_MSG_ERROR([missing libraries: $missing_libraries])
fi fi
if test ! -z "$missing_headers"; then if test ! -z "$missing_headers"; then
AC_MSG_ERROR([missing headers: $missing_headers]) AC_MSG_ERROR([missing headers: $missing_headers])
fi fi
AC_DEFINE_UNQUOTED(COPYRIGHT, "(C) 2004-2019 Hisham Muhammad. (C) 2020 htop dev team.", [Copyright message.]) AC_DEFINE_UNQUOTED(COPYRIGHT, "(C) 2004-2019 Hisham Muhammad. (C) 2020 htop dev team.", [Copyright message.])
@ -367,8 +384,7 @@ AC_SUBST(my_htop_platform)
AC_CONFIG_FILES([Makefile htop.1]) AC_CONFIG_FILES([Makefile htop.1])
AC_OUTPUT AC_OUTPUT
if test "$my_htop_platform" = "unsupported" if test "$my_htop_platform" = "unsupported"; then
then
echo "" echo ""
echo "****************************************************************" echo "****************************************************************"
echo "WARNING! This platform is not currently supported by htop." echo "WARNING! This platform is not currently supported by htop."

View File

@ -14,17 +14,37 @@ in the source distribution for its full text.
#include <mach/mach.h> #include <mach/mach.h>
#include "CRT.h" #include "CRT.h"
#include "Platform.h"
#include "Process.h" #include "Process.h"
const ProcessClass DarwinProcess_class = { const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
.super = { [0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, },
.extends = Class(Process), [PID] = { .name = "PID", .title = "PID", .description = "Process/thread ID", .flags = 0, .pidColumn = true, },
.display = Process_display, [COMM] = { .name = "Command", .title = "Command ", .description = "Command line", .flags = 0, },
.delete = Process_delete, [STATE] = { .name = "STATE", .title = "S ", .description = "Process state (S sleeping, R running, D disk, Z zombie, T traced, W paging)", .flags = 0, },
.compare = Process_compare [PPID] = { .name = "PPID", .title = "PPID", .description = "Parent process ID", .flags = 0, .pidColumn = true, },
}, [PGRP] = { .name = "PGRP", .title = "PGRP", .description = "Process group ID", .flags = 0, .pidColumn = true, },
.writeField = Process_writeField, [SESSION] = { .name = "SESSION", .title = "SID", .description = "Process's session ID", .flags = 0, .pidColumn = true, },
[TTY_NR] = { .name = "TTY_NR", .title = " TTY ", .description = "Controlling terminal", .flags = 0, },
[TPGID] = { .name = "TPGID", .title = "TPGID", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, .pidColumn = true, },
[MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, },
[MAJFLT] = { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, },
[PRIORITY] = { .name = "PRIORITY", .title = "PRI ", .description = "Kernel's internal priority for the process", .flags = 0, },
[NICE] = { .name = "NICE", .title = " NI ", .description = "Nice value (the higher the value, the more it lets other processes take priority)", .flags = 0, },
[STARTTIME] = { .name = "STARTTIME", .title = "START ", .description = "Time the process was started", .flags = 0, },
[PROCESSOR] = { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, },
[M_VIRT] = { .name = "M_VIRT", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, },
[M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, },
[ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, },
[PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, },
[PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, },
[USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
[TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, },
[NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, },
[TGID] = { .name = "TGID", .title = "TGID", .description = "Thread group ID (i.e. process ID)", .flags = 0, .pidColumn = true, },
[TRANSLATED] = { .name = "TRANSLATED", .title = "T ", .description = "Translation info (T translated, N native)", .flags = 0, },
}; };
Process* DarwinProcess_new(const Settings* settings) { Process* DarwinProcess_new(const Settings* settings) {
@ -35,6 +55,7 @@ Process* DarwinProcess_new(const Settings* settings) {
this->utime = 0; this->utime = 0;
this->stime = 0; this->stime = 0;
this->taskAccess = true; this->taskAccess = true;
this->translated = false;
return &this->super; return &this->super;
} }
@ -46,6 +67,34 @@ void Process_delete(Object* cast) {
free(this); free(this);
} }
static void DarwinProcess_writeField(const Process* this, RichString* str, ProcessField field) {
const DarwinProcess* dp = (const DarwinProcess*) this;
char buffer[256]; buffer[255] = '\0';
int attr = CRT_colors[DEFAULT_COLOR];
int n = sizeof(buffer) - 1;
switch (field) {
// add Platform-specific fields here
case TRANSLATED: xSnprintf(buffer, n, "%c ", dp->translated ? 'T' : 'N'); break;
default:
Process_writeField(this, str, field);
return;
}
RichString_appendWide(str, attr, buffer);
}
static int DarwinProcess_compareByKey(const Process* v1, const Process* v2, ProcessField key) {
const DarwinProcess* p1 = (const DarwinProcess*)v1;
const DarwinProcess* p2 = (const DarwinProcess*)v2;
switch (key) {
// add Platform-specific fields here
case TRANSLATED:
return SPACESHIP_NUMBER(p1->translated, p2->translated);
default:
return Process_compareByKey_Base(v1, v2, key);
}
}
bool Process_isThread(const Process* this) { bool Process_isThread(const Process* this) {
(void) this; (void) this;
return false; return false;
@ -195,6 +244,8 @@ ERROR_A:
} }
void DarwinProcess_setFromKInfoProc(Process* proc, const struct kinfo_proc* ps, bool exists) { void DarwinProcess_setFromKInfoProc(Process* proc, const struct kinfo_proc* ps, bool exists) {
DarwinProcess* dp = (DarwinProcess*)proc;
const struct extern_proc* ep = &ps->kp_proc; const struct extern_proc* ep = &ps->kp_proc;
/* UNSET HERE : /* UNSET HERE :
@ -223,6 +274,7 @@ void DarwinProcess_setFromKInfoProc(Process* proc, const struct kinfo_proc* ps,
/* e_tdev = (major << 24) | (minor & 0xffffff) */ /* e_tdev = (major << 24) | (minor & 0xffffff) */
/* e_tdev == -1 for "no device" */ /* e_tdev == -1 for "no device" */
proc->tty_nr = ps->kp_eproc.e_tdev & 0xff; /* TODO tty_nr is unsigned */ proc->tty_nr = ps->kp_eproc.e_tdev & 0xff; /* TODO tty_nr is unsigned */
dp->translated = ps->kp_proc.p_flag & P_TRANSLATED;
proc->starttime_ctime = ep->p_starttime.tv_sec; proc->starttime_ctime = ep->p_starttime.tv_sec;
Process_fillStarttimeBuffer(proc); Process_fillStarttimeBuffer(proc);
@ -240,26 +292,24 @@ void DarwinProcess_setFromKInfoProc(Process* proc, const struct kinfo_proc* ps,
proc->updated = true; proc->updated = true;
} }
void DarwinProcess_setFromLibprocPidinfo(DarwinProcess* proc, DarwinProcessList* dpl) { void DarwinProcess_setFromLibprocPidinfo(DarwinProcess* proc, DarwinProcessList* dpl, double time_interval) {
struct proc_taskinfo pti; struct proc_taskinfo pti;
if (sizeof(pti) == proc_pidinfo(proc->super.pid, PROC_PIDTASKINFO, 0, &pti, sizeof(pti))) { if (sizeof(pti) == proc_pidinfo(proc->super.pid, PROC_PIDTASKINFO, 0, &pti, sizeof(pti))) {
if (0 != proc->utime || 0 != proc->stime) { uint64_t total_existing_time = proc->stime + proc->utime;
uint64_t diff = (pti.pti_total_system - proc->stime) uint64_t total_current_time = pti.pti_total_system + pti.pti_total_user;
+ (pti.pti_total_user - proc->utime);
proc->super.percent_cpu = (double)diff * (double)dpl->super.cpuCount if (total_existing_time && 1E-6 < time_interval) {
/ ((double)dpl->global_diff * 100000.0); uint64_t total_time_diff = total_current_time - total_existing_time;
proc->super.percent_cpu = ((double)total_time_diff / time_interval) * 100.0;
// fprintf(stderr, "%f %llu %llu %llu %llu %llu\n", proc->super.percent_cpu, } else {
// proc->stime, proc->utime, pti.pti_total_system, pti.pti_total_user, dpl->global_diff); proc->super.percent_cpu = 0.0;
// exit(7);
} }
proc->super.time = (pti.pti_total_system + pti.pti_total_user) / 10000000; proc->super.time = total_current_time / 10000000;
proc->super.nlwp = pti.pti_threadnum; proc->super.nlwp = pti.pti_threadnum;
proc->super.m_virt = pti.pti_virtual_size / CRT_pageSize; proc->super.m_virt = pti.pti_virtual_size / ONE_K;
proc->super.m_resident = pti.pti_resident_size / CRT_pageSize; proc->super.m_resident = pti.pti_resident_size / ONE_K;
proc->super.majflt = pti.pti_faults; proc->super.majflt = pti.pti_faults;
proc->super.percent_mem = (double)pti.pti_resident_size * 100.0 proc->super.percent_mem = (double)pti.pti_resident_size * 100.0
/ (double)dpl->host_info.max_mem; / (double)dpl->host_info.max_mem;
@ -341,3 +391,15 @@ void DarwinProcess_scanThreads(DarwinProcess* dp) {
} }
proc->state = state; proc->state = state;
} }
const ProcessClass DarwinProcess_class = {
.super = {
.extends = Class(Process),
.display = Process_display,
.delete = Process_delete,
.compare = Process_compare
},
.writeField = DarwinProcess_writeField,
.compareByKey = DarwinProcess_compareByKey,
};

View File

@ -19,10 +19,13 @@ typedef struct DarwinProcess_ {
uint64_t utime; uint64_t utime;
uint64_t stime; uint64_t stime;
bool taskAccess; bool taskAccess;
bool translated;
} DarwinProcess; } DarwinProcess;
extern const ProcessClass DarwinProcess_class; extern const ProcessClass DarwinProcess_class;
extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD];
Process* DarwinProcess_new(const Settings* settings); Process* DarwinProcess_new(const Settings* settings);
void Process_delete(Object* cast); void Process_delete(Object* cast);
@ -31,7 +34,7 @@ bool Process_isThread(const Process* this);
void DarwinProcess_setFromKInfoProc(Process* proc, const struct kinfo_proc* ps, bool exists); void DarwinProcess_setFromKInfoProc(Process* proc, const struct kinfo_proc* ps, bool exists);
void DarwinProcess_setFromLibprocPidinfo(DarwinProcess* proc, DarwinProcessList* dpl); void DarwinProcess_setFromLibprocPidinfo(DarwinProcess* proc, DarwinProcessList* dpl, double time_interval);
/* /*
* Scan threads for process state information. * Scan threads for process state information.

View File

@ -7,7 +7,6 @@ in the source distribution for its full text.
#include "DarwinProcessList.h" #include "DarwinProcessList.h"
#include <err.h>
#include <errno.h> #include <errno.h>
#include <libproc.h> #include <libproc.h>
#include <stdbool.h> #include <stdbool.h>
@ -21,6 +20,7 @@ in the source distribution for its full text.
#include "CRT.h" #include "CRT.h"
#include "DarwinProcess.h" #include "DarwinProcess.h"
#include "Platform.h"
#include "ProcessList.h" #include "ProcessList.h"
#include "zfs/openzfs_sysctl.h" #include "zfs/openzfs_sysctl.h"
#include "zfs/ZfsArcStats.h" #include "zfs/ZfsArcStats.h"
@ -71,14 +71,14 @@ static void ProcessList_getHostInfo(host_basic_info_data_t* p) {
mach_msg_type_number_t info_size = HOST_BASIC_INFO_COUNT; mach_msg_type_number_t info_size = HOST_BASIC_INFO_COUNT;
if (0 != host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t)p, &info_size)) { if (0 != host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t)p, &info_size)) {
CRT_fatalError("Unable to retrieve host info\n"); CRT_fatalError("Unable to retrieve host info");
} }
} }
static void ProcessList_freeCPULoadInfo(processor_cpu_load_info_t* p) { static void ProcessList_freeCPULoadInfo(processor_cpu_load_info_t* p) {
if (NULL != p && NULL != *p) { if (NULL != p && NULL != *p) {
if (0 != munmap(*p, vm_page_size)) { if (0 != munmap(*p, vm_page_size)) {
CRT_fatalError("Unable to free old CPU load information\n"); CRT_fatalError("Unable to free old CPU load information");
} }
*p = NULL; *p = NULL;
} }
@ -90,7 +90,7 @@ static unsigned ProcessList_allocateCPULoadInfo(processor_cpu_load_info_t* p) {
// TODO Improving the accuracy of the load counts woule help a lot. // TODO Improving the accuracy of the load counts woule help a lot.
if (0 != host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &cpu_count, (processor_info_array_t*)p, &info_size)) { if (0 != host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &cpu_count, (processor_info_array_t*)p, &info_size)) {
CRT_fatalError("Unable to retrieve CPU info\n"); CRT_fatalError("Unable to retrieve CPU info");
} }
return cpu_count; return cpu_count;
@ -100,7 +100,7 @@ static void ProcessList_getVMStats(vm_statistics_t p) {
mach_msg_type_number_t info_size = HOST_VM_INFO_COUNT; mach_msg_type_number_t info_size = HOST_VM_INFO_COUNT;
if (host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)p, &info_size) != 0) { if (host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)p, &info_size) != 0) {
CRT_fatalError("Unable to retrieve VM statistics\n"); CRT_fatalError("Unable to retrieve VM statistics");
} }
} }
@ -131,7 +131,7 @@ static struct kinfo_proc* ProcessList_getKInfoProcs(size_t* count) {
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) { ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) {
DarwinProcessList* this = xCalloc(1, sizeof(DarwinProcessList)); DarwinProcessList* this = xCalloc(1, sizeof(DarwinProcessList));
ProcessList_init(&this->super, Class(Process), usersTable, pidMatchList, userId); ProcessList_init(&this->super, Class(DarwinProcess), usersTable, pidMatchList, userId);
/* Initialize the CPU information */ /* Initialize the CPU information */
this->super.cpuCount = ProcessList_allocateCPULoadInfo(&this->prev_load); this->super.cpuCount = ProcessList_allocateCPULoadInfo(&this->prev_load);
@ -158,6 +158,11 @@ void ProcessList_delete(ProcessList* this) {
free(this); free(this);
} }
static double ticksToNanoseconds(const double ticks) {
const double nanos_per_sec = 1e9;
return (ticks / Platform_timebaseToNS) * (nanos_per_sec / (double) Platform_clockTicksPerSec);
}
void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
DarwinProcessList* dpl = (DarwinProcessList*)super; DarwinProcessList* dpl = (DarwinProcessList*)super;
bool preExisting = true; bool preExisting = true;
@ -185,6 +190,8 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
} }
} }
const double time_interval = ticksToNanoseconds(dpl->global_diff) / (double) dpl->super.cpuCount;
/* Clear the thread counts */ /* Clear the thread counts */
super->kernelThreads = 0; super->kernelThreads = 0;
super->userlandThreads = 0; super->userlandThreads = 0;
@ -204,7 +211,7 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
proc = (DarwinProcess*)ProcessList_getProcess(super, ps[i].kp_proc.p_pid, &preExisting, DarwinProcess_new); proc = (DarwinProcess*)ProcessList_getProcess(super, ps[i].kp_proc.p_pid, &preExisting, DarwinProcess_new);
DarwinProcess_setFromKInfoProc(&proc->super, &ps[i], preExisting); DarwinProcess_setFromKInfoProc(&proc->super, &ps[i], preExisting);
DarwinProcess_setFromLibprocPidinfo(proc, dpl); DarwinProcess_setFromLibprocPidinfo(proc, dpl, time_interval);
// Disabled for High Sierra due to bug in macOS High Sierra // Disabled for High Sierra due to bug in macOS High Sierra
bool isScanThreadSupported = ! ( CompareKernelVersion(17, 0, 0) >= 0 && CompareKernelVersion(17, 5, 0) < 0); bool isScanThreadSupported = ! ( CompareKernelVersion(17, 0, 0) >= 0 && CompareKernelVersion(17, 5, 0) < 0);

View File

@ -6,32 +6,42 @@ Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "Platform.h" #include "config.h" // IWYU pragma: keep
#include "Macros.h"
#include "CPUMeter.h"
#include "MemoryMeter.h"
#include "SwapMeter.h"
#include "TasksMeter.h"
#include "LoadAverageMeter.h"
#include "ClockMeter.h"
#include "DateMeter.h"
#include "DateTimeMeter.h"
#include "HostnameMeter.h"
#include "ProcessLocksScreen.h"
#include "UptimeMeter.h"
#include "zfs/ZfsArcMeter.h"
#include "zfs/ZfsCompressedArcMeter.h"
#include "DarwinProcessList.h"
#include "Platform.h"
#include <errno.h>
#include <math.h> #include <math.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h>
#include <CoreFoundation/CoreFoundation.h>
#include <CoreFoundation/CFString.h> #include <CoreFoundation/CFString.h>
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/ps/IOPowerSources.h> #include <IOKit/ps/IOPowerSources.h>
#include <IOKit/ps/IOPSKeys.h> #include <IOKit/ps/IOPSKeys.h>
ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 }; #include "ClockMeter.h"
#include "CPUMeter.h"
#include "CRT.h"
#include "DarwinProcessList.h"
#include "DateMeter.h"
#include "DateTimeMeter.h"
#include "HostnameMeter.h"
#include "LoadAverageMeter.h"
#include "Macros.h"
#include "MemoryMeter.h"
#include "ProcessLocksScreen.h"
#include "SwapMeter.h"
#include "TasksMeter.h"
#include "UptimeMeter.h"
#include "zfs/ZfsArcMeter.h"
#include "zfs/ZfsCompressedArcMeter.h"
#ifdef HAVE_MACH_MACH_TIME_H
#include <mach/mach_time.h>
#endif
const ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
const SignalItem Platform_signals[] = { const SignalItem Platform_signals[] = {
{ .name = " 0 Cancel", .number = 0 }, { .name = " 0 Cancel", .number = 0 },
@ -71,35 +81,6 @@ const SignalItem Platform_signals[] = {
const unsigned int Platform_numberOfSignals = ARRAYSIZE(Platform_signals); const unsigned int Platform_numberOfSignals = ARRAYSIZE(Platform_signals);
ProcessFieldData Process_fields[] = {
[0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, },
[PID] = { .name = "PID", .title = " PID ", .description = "Process/thread ID", .flags = 0, },
[COMM] = { .name = "Command", .title = "Command ", .description = "Command line", .flags = 0, },
[STATE] = { .name = "STATE", .title = "S ", .description = "Process state (S sleeping, R running, D disk, Z zombie, T traced, W paging)", .flags = 0, },
[PPID] = { .name = "PPID", .title = " PPID ", .description = "Parent process ID", .flags = 0, },
[PGRP] = { .name = "PGRP", .title = " PGRP ", .description = "Process group ID", .flags = 0, },
[SESSION] = { .name = "SESSION", .title = " SID ", .description = "Process's session ID", .flags = 0, },
[TTY_NR] = { .name = "TTY_NR", .title = " TTY ", .description = "Controlling terminal", .flags = 0, },
[TPGID] = { .name = "TPGID", .title = " TPGID ", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, },
[MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, },
[MAJFLT] = { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, },
[PRIORITY] = { .name = "PRIORITY", .title = "PRI ", .description = "Kernel's internal priority for the process", .flags = 0, },
[NICE] = { .name = "NICE", .title = " NI ", .description = "Nice value (the higher the value, the more it lets other processes take priority)", .flags = 0, },
[STARTTIME] = { .name = "STARTTIME", .title = "START ", .description = "Time the process was started", .flags = 0, },
[PROCESSOR] = { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, },
[M_VIRT] = { .name = "M_VIRT", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, },
[M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, },
[ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, },
[PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, },
[PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, },
[USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
[TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, },
[NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, },
[TGID] = { .name = "TGID", .title = " TGID ", .description = "Thread group ID (i.e. process ID)", .flags = 0, },
[100] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, },
};
const MeterClass* const Platform_meterTypes[] = { const MeterClass* const Platform_meterTypes[] = {
&CPUMeter_class, &CPUMeter_class,
&ClockMeter_class, &ClockMeter_class,
@ -131,10 +112,28 @@ const MeterClass* const Platform_meterTypes[] = {
NULL NULL
}; };
int Platform_numberOfFields = 100; double Platform_timebaseToNS = 1.0;
long Platform_clockTicksPerSec = -1;
void Platform_init(void) { void Platform_init(void) {
/* no platform-specific setup needed */ // Check if we can determine the timebase used on this system.
// If the API is unavailable assume we get our timebase in nanoseconds.
#ifdef HAVE_MACH_TIMEBASE_INFO
mach_timebase_info_data_t info;
mach_timebase_info(&info);
Platform_timebaseToNS = (double)info.numer / (double)info.denom;
#else
Platform_timebaseToNS = 1.0;
#endif
// Determine the number of clock ticks per second
errno = 0;
Platform_clockTicksPerSec = sysconf(_SC_CLK_TCK);
if (errno || Platform_clockTicksPerSec < 1) {
CRT_fatalError("Unable to retrieve clock tick rate");
}
} }
void Platform_done(void) { void Platform_done(void) {
@ -179,16 +178,6 @@ int Platform_getMaxPid() {
return 99999; return 99999;
} }
ProcessPidColumn Process_pidColumns[] = {
{ .id = PID, .label = "PID" },
{ .id = PPID, .label = "PPID" },
{ .id = TPGID, .label = "TPGID" },
{ .id = TGID, .label = "TGID" },
{ .id = PGRP, .label = "PGRP" },
{ .id = SESSION, .label = "SID" },
{ .id = 0, .label = NULL },
};
static double Platform_setCPUAverageValues(Meter* mtr) { static double Platform_setCPUAverageValues(Meter* mtr) {
const ProcessList* dpl = mtr->pl; const ProcessList* dpl = mtr->pl;
int cpus = dpl->cpuCount; int cpus = dpl->cpuCount;

View File

@ -19,11 +19,12 @@ in the source distribution for its full text.
#include "ProcessLocksScreen.h" #include "ProcessLocksScreen.h"
#include "SignalsPanel.h" #include "SignalsPanel.h"
extern ProcessFieldData Process_fields[];
extern ProcessField Platform_defaultFields[]; extern const ProcessField Platform_defaultFields[];
extern int Platform_numberOfFields; extern double Platform_timebaseToNS;
extern long Platform_clockTicksPerSec;
extern const SignalItem Platform_signals[]; extern const SignalItem Platform_signals[];
@ -43,8 +44,6 @@ void Platform_getLoadAverage(double* one, double* five, double* fifteen);
int Platform_getMaxPid(void); int Platform_getMaxPid(void);
extern ProcessPidColumn Process_pidColumns[];
double Platform_setCPUValues(Meter* mtr, int cpu); double Platform_setCPUValues(Meter* mtr, int cpu);
void Platform_setMemoryValues(Meter* mtr); void Platform_setMemoryValues(Meter* mtr);

16
darwin/ProcessField.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef HEADER_DarwinProcessField
#define HEADER_DarwinProcessField
/*
htop - darwin/ProcessField.h
(C) 2020 htop dev team
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#define PLATFORM_PROCESS_FIELDS \
TRANSLATED = 100, \
// End of list
#endif /* HEADER_DarwinProcessField */

View File

@ -18,26 +18,16 @@ in the source distribution for its full text.
#include <sys/syscall.h> #include <sys/syscall.h>
const ProcessClass DragonFlyBSDProcess_class = { const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
.super = {
.extends = Class(Process),
.display = Process_display,
.delete = Process_delete,
.compare = DragonFlyBSDProcess_compare
},
.writeField = DragonFlyBSDProcess_writeField,
};
ProcessFieldData Process_fields[] = {
[0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, }, [0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, },
[PID] = { .name = "PID", .title = " PID ", .description = "Process/thread ID", .flags = 0, }, [PID] = { .name = "PID", .title = "PID", .description = "Process/thread ID", .flags = 0, .pidColumn = true, },
[COMM] = { .name = "Command", .title = "Command ", .description = "Command line", .flags = 0, }, [COMM] = { .name = "Command", .title = "Command ", .description = "Command line", .flags = 0, },
[STATE] = { .name = "STATE", .title = "S ", .description = "Process state (S sleeping (<20s), I Idle, Q Queued for Run, R running, D disk, Z zombie, T traced, W paging, B Blocked, A AskedPage, C Core, J Jailed)", .flags = 0, }, [STATE] = { .name = "STATE", .title = "S ", .description = "Process state (S sleeping (<20s), I Idle, Q Queued for Run, R running, D disk, Z zombie, T traced, W paging, B Blocked, A AskedPage, C Core, J Jailed)", .flags = 0, },
[PPID] = { .name = "PPID", .title = " PPID ", .description = "Parent process ID", .flags = 0, }, [PPID] = { .name = "PPID", .title = "PPID", .description = "Parent process ID", .flags = 0, .pidColumn = true, },
[PGRP] = { .name = "PGRP", .title = " PGRP ", .description = "Process group ID", .flags = 0, }, [PGRP] = { .name = "PGRP", .title = "PGRP", .description = "Process group ID", .flags = 0, .pidColumn = true, },
[SESSION] = { .name = "SESSION", .title = " SID ", .description = "Process's session ID", .flags = 0, }, [SESSION] = { .name = "SESSION", .title = "SID", .description = "Process's session ID", .flags = 0, .pidColumn = true, },
[TTY_NR] = { .name = "TTY_NR", .title = " TTY ", .description = "Controlling terminal", .flags = 0, }, [TTY_NR] = { .name = "TTY_NR", .title = " TTY ", .description = "Controlling terminal", .flags = 0, },
[TPGID] = { .name = "TPGID", .title = " TPGID ", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, }, [TPGID] = { .name = "TPGID", .title = "TPGID", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, .pidColumn = true, },
[MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, }, [MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, },
[MAJFLT] = { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, }, [MAJFLT] = { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, },
[PRIORITY] = { .name = "PRIORITY", .title = "PRI ", .description = "Kernel's internal priority for the process", .flags = 0, }, [PRIORITY] = { .name = "PRIORITY", .title = "PRI ", .description = "Kernel's internal priority for the process", .flags = 0, },
@ -53,21 +43,9 @@ ProcessFieldData Process_fields[] = {
[USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, }, [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
[TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, }, [TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, },
[NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, }, [NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, },
[TGID] = { .name = "TGID", .title = " TGID ", .description = "Thread group ID (i.e. process ID)", .flags = 0, }, [TGID] = { .name = "TGID", .title = "TGID", .description = "Thread group ID (i.e. process ID)", .flags = 0, .pidColumn = true, },
[JID] = { .name = "JID", .title = " JID ", .description = "Jail prison ID", .flags = 0, }, [JID] = { .name = "JID", .title = "JID", .description = "Jail prison ID", .flags = 0, .pidColumn = true, },
[JAIL] = { .name = "JAIL", .title = "JAIL ", .description = "Jail prison name", .flags = 0, }, [JAIL] = { .name = "JAIL", .title = "JAIL ", .description = "Jail prison name", .flags = 0, },
[LAST_PROCESSFIELD] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, },
};
ProcessPidColumn Process_pidColumns[] = {
{ .id = JID, .label = "JID" },
{ .id = PID, .label = "PID" },
{ .id = PPID, .label = "PPID" },
{ .id = TPGID, .label = "TPGID" },
{ .id = TGID, .label = "TGID" },
{ .id = PGRP, .label = "PGRP" },
{ .id = SESSION, .label = "SID" },
{ .id = 0, .label = NULL },
}; };
Process* DragonFlyBSDProcess_new(const Settings* settings) { Process* DragonFlyBSDProcess_new(const Settings* settings) {
@ -84,50 +62,35 @@ void Process_delete(Object* cast) {
free(this); free(this);
} }
void DragonFlyBSDProcess_writeField(const Process* this, RichString* str, ProcessField field) { static void DragonFlyBSDProcess_writeField(const Process* this, RichString* str, ProcessField field) {
const DragonFlyBSDProcess* fp = (const DragonFlyBSDProcess*) this; const DragonFlyBSDProcess* fp = (const DragonFlyBSDProcess*) this;
char buffer[256]; buffer[255] = '\0'; char buffer[256]; buffer[255] = '\0';
int attr = CRT_colors[DEFAULT_COLOR]; int attr = CRT_colors[DEFAULT_COLOR];
int n = sizeof(buffer) - 1; size_t n = sizeof(buffer) - 1;
switch ((int) field) { switch (field) {
// add Platform-specific fields here // add Platform-specific fields here
case PID: xSnprintf(buffer, n, Process_pidFormat, (fp->kernel ? -1 : this->pid)); break; case PID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, (fp->kernel ? -1 : this->pid)); break;
case JID: xSnprintf(buffer, n, Process_pidFormat, fp->jid); break; case JID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, fp->jid); break;
case JAIL: { case JAIL: Process_printLeftAlignedField(str, attr, fp->jname, 11); return;
xSnprintf(buffer, n, "%-11s ", fp->jname);
if (buffer[11] != '\0') {
buffer[11] = ' ';
buffer[12] = '\0';
}
break;
}
default: default:
Process_writeField(this, str, field); Process_writeField(this, str, field);
return; return;
} }
RichString_append(str, attr, buffer); RichString_appendWide(str, attr, buffer);
} }
long DragonFlyBSDProcess_compare(const void* v1, const void* v2) { static int DragonFlyBSDProcess_compareByKey(const Process* v1, const Process* v2, ProcessField key) {
const DragonFlyBSDProcess *p1, *p2; const DragonFlyBSDProcess* p1 = (const DragonFlyBSDProcess*)v1;
const Settings *settings = ((const Process*)v1)->settings; const DragonFlyBSDProcess* p2 = (const DragonFlyBSDProcess*)v2;
if (settings->direction == 1) { switch (key) {
p1 = (const DragonFlyBSDProcess*)v1;
p2 = (const DragonFlyBSDProcess*)v2;
} else {
p2 = (const DragonFlyBSDProcess*)v1;
p1 = (const DragonFlyBSDProcess*)v2;
}
switch ((int) settings->sortKey) {
// add Platform-specific fields here // add Platform-specific fields here
case JID: case JID:
return SPACESHIP_NUMBER(p1->jid, p2->jid); return SPACESHIP_NUMBER(p1->jid, p2->jid);
case JAIL: case JAIL:
return SPACESHIP_NULLSTR(p1->jname, p2->jname); return SPACESHIP_NULLSTR(p1->jname, p2->jname);
default: default:
return Process_compare(v1, v2); return Process_compareByKey_Base(v1, v2, key);
} }
} }
@ -140,3 +103,14 @@ bool Process_isThread(const Process* this) {
return (Process_isUserlandThread(this)); return (Process_isUserlandThread(this));
} }
} }
const ProcessClass DragonFlyBSDProcess_class = {
.super = {
.extends = Class(Process),
.display = Process_display,
.delete = Process_delete,
.compare = Process_compare
},
.writeField = DragonFlyBSDProcess_writeField,
.compareByKey = DragonFlyBSDProcess_compareByKey
};

View File

@ -8,13 +8,6 @@ Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
typedef enum DragonFlyBSDProcessFields {
// Add platform-specific fields here, with ids >= 100
JID = 100,
JAIL = 101,
LAST_PROCESSFIELD = 102,
} DragonFlyBSDProcessField;
typedef struct DragonFlyBSDProcess_ { typedef struct DragonFlyBSDProcess_ {
Process super; Process super;
int kernel; int kernel;
@ -29,18 +22,12 @@ typedef struct DragonFlyBSDProcess_ {
extern const ProcessClass DragonFlyBSDProcess_class; extern const ProcessClass DragonFlyBSDProcess_class;
extern ProcessFieldData Process_fields[]; extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD];
extern ProcessPidColumn Process_pidColumns[];
Process* DragonFlyBSDProcess_new(const Settings* settings); Process* DragonFlyBSDProcess_new(const Settings* settings);
void Process_delete(Object* cast); void Process_delete(Object* cast);
void DragonFlyBSDProcess_writeField(const Process* this, RichString* str, ProcessField field);
long DragonFlyBSDProcess_compare(const void* v1, const void* v2);
bool Process_isThread(const Process* this); bool Process_isThread(const Process* this);
#endif #endif

View File

@ -15,7 +15,6 @@ in the source distribution for its full text.
#include <sys/types.h> #include <sys/types.h>
#include <sys/sysctl.h> #include <sys/sysctl.h>
#include <sys/user.h> #include <sys/user.h>
#include <err.h>
#include <fcntl.h> #include <fcntl.h>
#include <limits.h> #include <limits.h>
#include <string.h> #include <string.h>
@ -55,12 +54,9 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, ui
len = 2; sysctlnametomib("hw.physmem", MIB_hw_physmem, &len); len = 2; sysctlnametomib("hw.physmem", MIB_hw_physmem, &len);
len = sizeof(pageSize); len = sizeof(pageSize);
if (sysctlbyname("vm.stats.vm.v_page_size", &pageSize, &len, NULL, 0) == -1) { if (sysctlbyname("vm.stats.vm.v_page_size", &pageSize, &len, NULL, 0) == -1)
pageSize = CRT_pageSize; CRT_fatalError("Cannot get pagesize by sysctl");
pageSizeKb = CRT_pageSizeKB; pageSizeKb = pageSize / ONE_K;
} else {
pageSizeKb = pageSize / ONE_K;
}
// usable page count vm.stats.vm.v_page_count // usable page count vm.stats.vm.v_page_count
// actually usable memory : vm.stats.vm.v_page_count * vm.stats.vm.v_page_size // actually usable memory : vm.stats.vm.v_page_count * vm.stats.vm.v_page_size
@ -115,7 +111,7 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, ui
dfpl->kd = kvm_openfiles(NULL, "/dev/null", NULL, 0, errbuf); dfpl->kd = kvm_openfiles(NULL, "/dev/null", NULL, 0, errbuf);
if (dfpl->kd == NULL) { if (dfpl->kd == NULL) {
errx(1, "kvm_open: %s", errbuf); CRT_fatalError("kvm_openfiles() failed");
} }
return pl; return pl;
@ -265,7 +261,7 @@ static inline void DragonFlyBSDProcessList_scanMemoryInfo(ProcessList* pl) {
pl->usedSwap *= pageSizeKb; pl->usedSwap *= pageSizeKb;
} }
char* DragonFlyBSDProcessList_readProcessName(kvm_t* kd, struct kinfo_proc* kproc, int* basenameEnd) { static char* DragonFlyBSDProcessList_readProcessName(kvm_t* kd, const struct kinfo_proc* kproc, int* basenameEnd) {
char** argv = kvm_getargv(kd, kproc, 0); char** argv = kvm_getargv(kd, kproc, 0);
if (!argv) { if (!argv) {
return xStrdup(kproc->kp_comm); return xStrdup(kproc->kp_comm);
@ -297,25 +293,20 @@ static inline void DragonFlyBSDProcessList_scanJails(DragonFlyBSDProcessList* df
char* nextpos; char* nextpos;
if (sysctlbyname("jail.list", NULL, &len, NULL, 0) == -1) { if (sysctlbyname("jail.list", NULL, &len, NULL, 0) == -1) {
fprintf(stderr, "initial sysctlbyname / jail.list failed\n"); CRT_fatalError("initial sysctlbyname / jail.list failed");
exit(3);
} }
retry: retry:
if (len == 0) if (len == 0)
return; return;
jls = xMalloc(len); jls = xMalloc(len);
if (jls == NULL) {
fprintf(stderr, "xMalloc failed\n");
exit(4);
}
if (sysctlbyname("jail.list", jls, &len, NULL, 0) == -1) { if (sysctlbyname("jail.list", jls, &len, NULL, 0) == -1) {
if (errno == ENOMEM) { if (errno == ENOMEM) {
free(jls); free(jls);
goto retry; goto retry;
} }
fprintf(stderr, "sysctlbyname / jail.list failed\n"); CRT_fatalError("sysctlbyname / jail.list failed");
exit(5);
} }
if (dfpl->jails) { if (dfpl->jails) {
@ -346,7 +337,7 @@ retry:
free(jls); free(jls);
} }
char* DragonFlyBSDProcessList_readJailName(DragonFlyBSDProcessList* dfpl, int jailid) { static char* DragonFlyBSDProcessList_readJailName(DragonFlyBSDProcessList* dfpl, int jailid) {
char* hostname; char* hostname;
char* jname; char* jname;
@ -376,10 +367,10 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
int count = 0; int count = 0;
// TODO Kernel Threads seem to be skipped, need to figure out the correct flag // TODO Kernel Threads seem to be skipped, need to figure out the correct flag
struct kinfo_proc* kprocs = kvm_getprocs(dfpl->kd, KERN_PROC_ALL | (!hideUserlandThreads ? KERN_PROC_FLAG_LWP : 0), 0, &count); const struct kinfo_proc* kprocs = kvm_getprocs(dfpl->kd, KERN_PROC_ALL | (!hideUserlandThreads ? KERN_PROC_FLAG_LWP : 0), 0, &count);
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
struct kinfo_proc* kproc = &kprocs[i]; const struct kinfo_proc* kproc = &kprocs[i];
bool preExisting = false; bool preExisting = false;
bool ATTR_UNUSED isIdleProcess = false; bool ATTR_UNUSED isIdleProcess = false;
@ -433,13 +424,13 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
} }
} }
proc->m_virt = kproc->kp_vm_map_size / pageSize; proc->m_virt = kproc->kp_vm_map_size / ONE_K;
proc->m_resident = kproc->kp_vm_rssize; proc->m_resident = kproc->kp_vm_rssize * pageSizeKb;
proc->nlwp = kproc->kp_nthreads; // number of lwp thread proc->nlwp = kproc->kp_nthreads; // number of lwp thread
proc->time = (kproc->kp_swtime + 5000) / 10000; proc->time = (kproc->kp_swtime + 5000) / 10000;
proc->percent_cpu = 100.0 * ((double)kproc->kp_lwp.kl_pctcpu / (double)kernelFScale); proc->percent_cpu = 100.0 * ((double)kproc->kp_lwp.kl_pctcpu / (double)kernelFScale);
proc->percent_mem = 100.0 * (proc->m_resident * pageSizeKb) / (double)(super->totalMem); proc->percent_mem = 100.0 * proc->m_resident / (double)(super->totalMem);
if (proc->percent_cpu > 0.1) { if (proc->percent_cpu > 0.1) {
// system idle process should own all CPU time left regardless of CPU count // system idle process should own all CPU time left regardless of CPU count

View File

@ -11,16 +11,12 @@ in the source distribution for its full text.
#include <kvm.h> #include <kvm.h>
#include <sys/param.h> #include <sys/param.h>
#include <osreldate.h> #include <osreldate.h>
#include <sys/kinfo.h>
#include <kinfo.h>
#include <sys/jail.h> #include <sys/jail.h>
#include <sys/uio.h> #include <sys/uio.h>
#include <sys/resource.h> #include <sys/resource.h>
#include "Hashtable.h" #include "Hashtable.h"
#include "DragonFlyBSDProcess.h" #include "DragonFlyBSDProcess.h"
#define JAIL_ERRMSGLEN 1024
extern char jail_errmsg[JAIL_ERRMSGLEN];
typedef struct CPUData_ { typedef struct CPUData_ {
double userPercent; double userPercent;
@ -55,10 +51,6 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, ui
void ProcessList_delete(ProcessList* this); void ProcessList_delete(ProcessList* this);
char* DragonFlyBSDProcessList_readProcessName(kvm_t* kd, struct kinfo_proc* kproc, int* basenameEnd);
char* DragonFlyBSDProcessList_readJailName(DragonFlyBSDProcessList* dfpl, int jailid);
void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate); void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate);
#endif #endif

View File

@ -31,9 +31,7 @@ in the source distribution for its full text.
#include <math.h> #include <math.h>
ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 }; const ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
int Platform_numberOfFields = LAST_PROCESSFIELD;
const SignalItem Platform_signals[] = { const SignalItem Platform_signals[] = {
{ .name = " 0 Cancel", .number = 0 }, { .name = " 0 Cancel", .number = 0 },

View File

@ -17,11 +17,8 @@ in the source distribution for its full text.
#include "ProcessLocksScreen.h" #include "ProcessLocksScreen.h"
#include "SignalsPanel.h" #include "SignalsPanel.h"
extern ProcessFieldData Process_fields[];
extern ProcessField Platform_defaultFields[]; extern const ProcessField Platform_defaultFields[];
extern int Platform_numberOfFields;
extern const SignalItem Platform_signals[]; extern const SignalItem Platform_signals[];

View File

@ -0,0 +1,17 @@
#ifndef HEADER_DragonFlyBSDProcessField
#define HEADER_DragonFlyBSDProcessField
/*
htop - dragonflybsd/ProcessField.h
(C) 2020 htop dev team
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#define PLATFORM_PROCESS_FIELDS \
JID = 100, \
JAIL = 101, \
// End of list
#endif /* HEADER_DragonFlyBSDProcessField */

View File

@ -18,16 +18,16 @@ in the source distribution for its full text.
const char* const nodevStr = "nodev"; const char* const nodevStr = "nodev";
ProcessFieldData Process_fields[] = { const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
[0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, }, [0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, },
[PID] = { .name = "PID", .title = " PID ", .description = "Process/thread ID", .flags = 0, }, [PID] = { .name = "PID", .title = "PID", .description = "Process/thread ID", .flags = 0, .pidColumn = true, },
[COMM] = { .name = "Command", .title = "Command ", .description = "Command line", .flags = 0, }, [COMM] = { .name = "Command", .title = "Command ", .description = "Command line", .flags = 0, },
[STATE] = { .name = "STATE", .title = "S ", .description = "Process state (S sleeping, R running, D disk, Z zombie, T traced, W paging)", .flags = 0, }, [STATE] = { .name = "STATE", .title = "S ", .description = "Process state (S sleeping, R running, D disk, Z zombie, T traced, W paging)", .flags = 0, },
[PPID] = { .name = "PPID", .title = " PPID ", .description = "Parent process ID", .flags = 0, }, [PPID] = { .name = "PPID", .title = "PPID", .description = "Parent process ID", .flags = 0, .pidColumn = true, },
[PGRP] = { .name = "PGRP", .title = " PGRP ", .description = "Process group ID", .flags = 0, }, [PGRP] = { .name = "PGRP", .title = "PGRP", .description = "Process group ID", .flags = 0, .pidColumn = true, },
[SESSION] = { .name = "SESSION", .title = " SID ", .description = "Process's session ID", .flags = 0, }, [SESSION] = { .name = "SESSION", .title = "SID", .description = "Process's session ID", .flags = 0, .pidColumn = true, },
[TTY_NR] = { .name = "TTY_NR", .title = " TTY ", .description = "Controlling terminal", .flags = PROCESS_FLAG_FREEBSD_TTY, }, [TTY_NR] = { .name = "TTY_NR", .title = " TTY ", .description = "Controlling terminal", .flags = PROCESS_FLAG_FREEBSD_TTY, },
[TPGID] = { .name = "TPGID", .title = " TPGID ", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, }, [TPGID] = { .name = "TPGID", .title = "TPGID", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, .pidColumn = true, },
[MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, }, [MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, },
[MAJFLT] = { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, }, [MAJFLT] = { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, },
[PRIORITY] = { .name = "PRIORITY", .title = "PRI ", .description = "Kernel's internal priority for the process", .flags = 0, }, [PRIORITY] = { .name = "PRIORITY", .title = "PRI ", .description = "Kernel's internal priority for the process", .flags = 0, },
@ -44,21 +44,9 @@ ProcessFieldData Process_fields[] = {
[USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, }, [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
[TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, }, [TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, },
[NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, }, [NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, },
[TGID] = { .name = "TGID", .title = " TGID ", .description = "Thread group ID (i.e. process ID)", .flags = 0, }, [TGID] = { .name = "TGID", .title = "TGID", .description = "Thread group ID (i.e. process ID)", .flags = 0, .pidColumn = true, },
[JID] = { .name = "JID", .title = " JID ", .description = "Jail prison ID", .flags = 0, }, [JID] = { .name = "JID", .title = "JID", .description = "Jail prison ID", .flags = 0, .pidColumn = true, },
[JAIL] = { .name = "JAIL", .title = "JAIL ", .description = "Jail prison name", .flags = 0, }, [JAIL] = { .name = "JAIL", .title = "JAIL ", .description = "Jail prison name", .flags = 0, },
[LAST_PROCESSFIELD] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, },
};
ProcessPidColumn Process_pidColumns[] = {
{ .id = JID, .label = "JID" },
{ .id = PID, .label = "PID" },
{ .id = PPID, .label = "PPID" },
{ .id = TPGID, .label = "TPGID" },
{ .id = TGID, .label = "TGID" },
{ .id = PGRP, .label = "PGRP" },
{ .id = SESSION, .label = "SID" },
{ .id = 0, .label = NULL },
}; };
Process* FreeBSDProcess_new(const Settings* settings) { Process* FreeBSDProcess_new(const Settings* settings) {
@ -80,17 +68,12 @@ static void FreeBSDProcess_writeField(const Process* this, RichString* str, Proc
char buffer[256]; buffer[255] = '\0'; char buffer[256]; buffer[255] = '\0';
int attr = CRT_colors[DEFAULT_COLOR]; int attr = CRT_colors[DEFAULT_COLOR];
int n = sizeof(buffer) - 1; int n = sizeof(buffer) - 1;
switch ((int) field) { switch (field) {
// add FreeBSD-specific fields here // add FreeBSD-specific fields here
case JID: xSnprintf(buffer, n, Process_pidFormat, fp->jid); break; case JID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, fp->jid); break;
case JAIL: { case JAIL:
xSnprintf(buffer, n, "%-11s ", fp->jname); Process_printLeftAlignedField(str, attr, fp->jname ? fp->jname : "N/A", 11);
if (buffer[11] != '\0') { return;
buffer[11] = ' ';
buffer[12] = '\0';
}
break;
}
case TTY_NR: case TTY_NR:
if (fp->ttyPath) { if (fp->ttyPath) {
if (fp->ttyPath == nodevStr) if (fp->ttyPath == nodevStr)
@ -105,22 +88,14 @@ static void FreeBSDProcess_writeField(const Process* this, RichString* str, Proc
Process_writeField(this, str, field); Process_writeField(this, str, field);
return; return;
} }
RichString_append(str, attr, buffer); RichString_appendWide(str, attr, buffer);
} }
static long FreeBSDProcess_compare(const void* v1, const void* v2) { static int FreeBSDProcess_compareByKey(const Process* v1, const Process* v2, ProcessField key) {
const FreeBSDProcess *p1, *p2; const FreeBSDProcess* p1 = (const FreeBSDProcess*)v1;
const Settings *settings = ((const Process*)v1)->settings; const FreeBSDProcess* p2 = (const FreeBSDProcess*)v2;
if (settings->direction == 1) { switch (key) {
p1 = (const FreeBSDProcess*)v1;
p2 = (const FreeBSDProcess*)v2;
} else {
p2 = (const FreeBSDProcess*)v1;
p1 = (const FreeBSDProcess*)v2;
}
switch ((int) settings->sortKey) {
// add FreeBSD-specific fields here // add FreeBSD-specific fields here
case JID: case JID:
return SPACESHIP_NUMBER(p1->jid, p2->jid); return SPACESHIP_NUMBER(p1->jid, p2->jid);
@ -129,7 +104,7 @@ static long FreeBSDProcess_compare(const void* v1, const void* v2) {
case TTY_NR: case TTY_NR:
return SPACESHIP_NULLSTR(p1->ttyPath, p2->ttyPath); return SPACESHIP_NULLSTR(p1->ttyPath, p2->ttyPath);
default: default:
return Process_compare(v1, v2); return Process_compareByKey_Base(v1, v2, key);
} }
} }
@ -148,7 +123,8 @@ const ProcessClass FreeBSDProcess_class = {
.extends = Class(Process), .extends = Class(Process),
.display = Process_display, .display = Process_display,
.delete = Process_delete, .delete = Process_delete,
.compare = FreeBSDProcess_compare .compare = Process_compare
}, },
.writeField = FreeBSDProcess_writeField, .writeField = FreeBSDProcess_writeField,
.compareByKey = FreeBSDProcess_compareByKey
}; };

View File

@ -18,13 +18,6 @@ in the source distribution for its full text.
extern const char* const nodevStr; extern const char* const nodevStr;
typedef enum FreeBSDProcessFields_ {
// Add platform-specific fields here, with ids >= 100
JID = 100,
JAIL = 101,
LAST_PROCESSFIELD = 102,
} FreeBSDProcessField;
typedef struct FreeBSDProcess_ { typedef struct FreeBSDProcess_ {
Process super; Process super;
int kernel; int kernel;
@ -43,9 +36,7 @@ static inline bool Process_isUserlandThread(const Process* this) {
extern const ProcessClass FreeBSDProcess_class; extern const ProcessClass FreeBSDProcess_class;
extern ProcessFieldData Process_fields[]; extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD];
extern ProcessPidColumn Process_pidColumns[];
Process* FreeBSDProcess_new(const Settings* settings); Process* FreeBSDProcess_new(const Settings* settings);

View File

@ -9,12 +9,10 @@ in the source distribution for its full text.
#include <assert.h> #include <assert.h>
#include <dirent.h> #include <dirent.h>
#include <err.h>
#include <limits.h> #include <limits.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/_iovec.h> #include <sys/_iovec.h>
#include <sys/dirent.h>
#include <sys/errno.h> #include <sys/errno.h>
#include <sys/param.h> // needs to be included before <sys/jail.h> for MAXPATHLEN #include <sys/param.h> // needs to be included before <sys/jail.h> for MAXPATHLEN
#include <sys/jail.h> #include <sys/jail.h>
@ -72,12 +70,9 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, ui
len = 2; sysctlnametomib("hw.physmem", MIB_hw_physmem, &len); len = 2; sysctlnametomib("hw.physmem", MIB_hw_physmem, &len);
len = sizeof(pageSize); len = sizeof(pageSize);
if (sysctlbyname("vm.stats.vm.v_page_size", &pageSize, &len, NULL, 0) == -1) { if (sysctlbyname("vm.stats.vm.v_page_size", &pageSize, &len, NULL, 0) == -1)
pageSize = CRT_pageSize; CRT_fatalError("Cannot get pagesize by sysctl");
pageSizeKb = CRT_pageSize; pageSizeKb = pageSize / ONE_K;
} else {
pageSizeKb = pageSize / ONE_K;
}
// usable page count vm.stats.vm.v_page_count // usable page count vm.stats.vm.v_page_count
// actually usable memory : vm.stats.vm.v_page_count * vm.stats.vm.v_page_size // actually usable memory : vm.stats.vm.v_page_count * vm.stats.vm.v_page_size
@ -149,7 +144,7 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, ui
fpl->kd = kvm_openfiles(NULL, "/dev/null", NULL, 0, errbuf); fpl->kd = kvm_openfiles(NULL, "/dev/null", NULL, 0, errbuf);
if (fpl->kd == NULL) { if (fpl->kd == NULL) {
errx(1, "kvm_open: %s", errbuf); CRT_fatalError("kvm_openfiles() failed");
} }
fpl->ttys = Hashtable_new(20, true); fpl->ttys = Hashtable_new(20, true);
@ -419,7 +414,7 @@ static char* FreeBSDProcessList_readJailName(const struct kinfo_proc* kproc) {
char* jname = NULL; char* jname = NULL;
char jnamebuf[MAXHOSTNAMELEN]; char jnamebuf[MAXHOSTNAMELEN];
if (kproc->ki_jid != 0 ) { if (kproc->ki_jid != 0) {
struct iovec jiov[6]; struct iovec jiov[6];
memset(jnamebuf, 0, sizeof(jnamebuf)); memset(jnamebuf, 0, sizeof(jnamebuf));
@ -526,13 +521,13 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
} }
// from FreeBSD source /src/usr.bin/top/machine.c // from FreeBSD source /src/usr.bin/top/machine.c
proc->m_virt = kproc->ki_size / pageSize; proc->m_virt = kproc->ki_size / ONE_K;
proc->m_resident = kproc->ki_rssize; proc->m_resident = kproc->ki_rssize * pageSizeKb;
proc->nlwp = kproc->ki_numthreads; proc->nlwp = kproc->ki_numthreads;
proc->time = (kproc->ki_runtime + 5000) / 10000; proc->time = (kproc->ki_runtime + 5000) / 10000;
proc->percent_cpu = 100.0 * ((double)kproc->ki_pctcpu / (double)kernelFScale); proc->percent_cpu = 100.0 * ((double)kproc->ki_pctcpu / (double)kernelFScale);
proc->percent_mem = 100.0 * (proc->m_resident * pageSizeKb) / (double)(super->totalMem); proc->percent_mem = 100.0 * proc->m_resident / (double)(super->totalMem);
/* /*
* TODO * TODO

View File

@ -47,9 +47,7 @@ in the source distribution for its full text.
#include "zfs/ZfsCompressedArcMeter.h" #include "zfs/ZfsCompressedArcMeter.h"
ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 }; const ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
int Platform_numberOfFields = LAST_PROCESSFIELD;
const SignalItem Platform_signals[] = { const SignalItem Platform_signals[] = {
{ .name = " 0 Cancel", .number = 0 }, { .name = " 0 Cancel", .number = 0 },

View File

@ -19,11 +19,7 @@ in the source distribution for its full text.
#include "SignalsPanel.h" #include "SignalsPanel.h"
extern ProcessFieldData Process_fields[]; extern const ProcessField Platform_defaultFields[];
extern ProcessField Platform_defaultFields[];
extern int Platform_numberOfFields;
extern const SignalItem Platform_signals[]; extern const SignalItem Platform_signals[];

17
freebsd/ProcessField.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef HEADER_FreeBSDProcessField
#define HEADER_FreeBSDProcessField
/*
htop - freebsd/ProcessField.h
(C) 2020 htop dev team
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#define PLATFORM_PROCESS_FIELDS \
JID = 100, \
JAIL = 101, \
// End of list
#endif /* HEADER_FreeBSDProcessField */

View File

@ -2,11 +2,9 @@
.SH "NAME" .SH "NAME"
htop \- interactive process viewer htop \- interactive process viewer
.SH "SYNOPSIS" .SH "SYNOPSIS"
.LP
.B htop .B htop
.RB [ \-dCFhpustvH ] .RB [ \-dCFhpustvH ]
.SH "DESCRIPTION" .SH "DESCRIPTION"
.LP
.B htop .B htop
is a cross-platform ncurses-based process viewer. is a cross-platform ncurses-based process viewer.
.LP .LP
@ -22,9 +20,7 @@ Tasks related to processes (killing, renicing) can be done without
entering their PIDs. entering their PIDs.
.br .br
.SH "COMMAND-LINE OPTIONS" .SH "COMMAND-LINE OPTIONS"
.LP
Mandatory arguments to long options are mandatory for short options too. Mandatory arguments to long options are mandatory for short options too.
.LP
.TP .TP
\fB\-d \-\-delay=DELAY\fR \fB\-d \-\-delay=DELAY\fR
Delay between updates, in tenths of seconds. If the delay value is Delay between updates, in tenths of seconds. If the delay value is
@ -46,7 +42,8 @@ Display a help message and exit
Show only the given PIDs Show only the given PIDs
.TP .TP
\fB\-s \-\-sort\-key COLUMN\fR \fB\-s \-\-sort\-key COLUMN\fR
Sort by this column (use \-\-sort\-key help for a column list) Sort by this column (use \-\-sort\-key help for a column list).
This will force a list view unless you specify -t at the same time.
.TP .TP
\fB\-u \-\-user=USERNAME\fR \fB\-u \-\-user=USERNAME\fR
Show only the processes of a given user Show only the processes of a given user
@ -61,15 +58,14 @@ Disable support of mouse control
Output version information and exit Output version information and exit
.TP .TP
\fB\-t \-\-tree \fB\-t \-\-tree
Show processes in tree view Show processes in tree view. This can be used to force a tree view when
requesting a sort order with -s.
.TP .TP
\fB\-H \-\-highlight-changes=DELAY\fR \fB\-H \-\-highlight-changes=DELAY\fR
Highlight new and old processes Highlight new and old processes
.SH "INTERACTIVE COMMANDS" .SH "INTERACTIVE COMMANDS"
.LP
The following commands are supported while in The following commands are supported while in
.BR htop : .BR htop :
.LP
.TP 5 .TP 5
.B Up, Alt-k .B Up, Alt-k
Select (highlight) the previous process in the process list. Scroll the list Select (highlight) the previous process in the process list. Scroll the list
@ -141,6 +137,7 @@ select which columns are displayed, in which order.
Incrementally search the command lines of all the displayed processes. The Incrementally search the command lines of all the displayed processes. The
currently selected (highlighted) command will update as you type. While in currently selected (highlighted) command will update as you type. While in
search mode, pressing F3 will cycle through matching occurrences. search mode, pressing F3 will cycle through matching occurrences.
Pressing Shift-F3 will cycle backwards.
Alternatively the search can be started by simply typing the command Alternatively the search can be started by simply typing the command
you are looking for, although for the first character normal key you are looking for, although for the first character normal key
@ -157,11 +154,9 @@ between them as a tree. Toggling the key will switch between tree and
your previously selected sort view. Selecting a sort view will exit your previously selected sort view. Selecting a sort view will exit
tree view. tree view.
.TP .TP
.B F6 .B F6, <, >
On sorted view, select a field for sorting, also accessible through < and >. Selects a field for sorting, also accessible through < and >.
The current sort field is indicated by a highlight in the header. The current sort field is indicated by a highlight in the header.
On tree view, expand or collapse the current subtree. A "+" indicator in the
tree node indicates that it is collapsed.
.TP .TP
.B F7, ] .B F7, ]
Increase the selected process's priority (subtract from 'nice' value). Increase the selected process's priority (subtract from 'nice' value).
@ -192,6 +187,9 @@ Set CPU affinity: mark which CPUs a process is allowed to use.
.B u .B u
Show only processes owned by a specified user. Show only processes owned by a specified user.
.TP .TP
.B N
Sort by PID.
.TP
.B M .B M
Sort by memory usage (top compatibility key). Sort by memory usage (top compatibility key).
.TP .TP
@ -232,7 +230,6 @@ Refresh: redraw screen and recalculate values.
PID search: type in process ID and the selection highlight will be moved to it. PID search: type in process ID and the selection highlight will be moved to it.
.PD .PD
.SH "COLUMNS" .SH "COLUMNS"
.LP
The following columns can display data about each process. A value of '\-' in The following columns can display data about each process. A value of '\-' in
all the rows indicates that a column is unsupported on your system, or all the rows indicates that a column is unsupported on your system, or
currently unimplemented in currently unimplemented in
@ -242,7 +239,6 @@ The names below are the ones used in the
shown in shown in
.BR htop 's .BR htop 's
main screen, it is shown below in parenthesis. main screen, it is shown below in parenthesis.
.LP
.TP 5 .TP 5
.B Command .B Command
The full command line of the process (i.e. program name and arguments). If the The full command line of the process (i.e. program name and arguments). If the
@ -460,8 +456,48 @@ The executable file of the process as reported by the kernel. Requires CAP_SYS_P
.TP .TP
.B All other flags .B All other flags
Currently unsupported (always displays '-'). Currently unsupported (always displays '-').
.SH "EXTERNAL LIBRARIES"
While
.B htop
depends on most of the libraries it uses at build time there are two
noteworthy exceptions to this rule. These exceptions both relate to
data displayed in meters displayed in the header of
.B htop
and were intentionally created as optional runtime dependencies instead.
These exceptions are described below:
.TP
.B libsystemd
The bindings for libsystemd are used in the SystemD meter to determine
the number of active services and the overall system state. Looking for
the functions to determine these information at runtime allows for
builds to support these meters without forcing the package manager
to install these libraries on systems that otherwise don't use systemd.
Summary: no build time dependency, optional runtime dependency on
.B libsystemd
via dynamic loading, with
.B systemctl(1)
fallback.
.TP
.B libsensors
The bindings for libsensors are used for the CPU temperature readings
in the CPU usage meters if displaying the temperature is enabled through
the setup screen. In order for
.B htop
to show these temperatures correctly though, a proper configuration
of libsensors through its usual configuration files is assumed and that
all CPU cores correspond to temperature sensors from the
.B coretemp
driver with core 0 corresponding to a sensor labelled "Core 0". The
package temperature may be given as "Package id 0". If missing it is
inferred as the maximum value from the available per-core readings.
Summary: build time dependency on
.B libsensors(3)
C header files, optional runtime dependency on
.B libsensors(3)
via dynamic loading.
.SH "CONFIG FILE" .SH "CONFIG FILE"
.LP
By default By default
.B htop .B htop
reads its configuration from the XDG-compliant path reads its configuration from the XDG-compliant path
@ -479,7 +515,6 @@ You may override the location of the configuration file using the $HTOPRC
environment variable (so you can have multiple configurations for different environment variable (so you can have multiple configurations for different
machines that share the same home directory, for example). machines that share the same home directory, for example).
.SH "MEMORY SIZES" .SH "MEMORY SIZES"
.LP
Memory sizes in Memory sizes in
.B htop .B htop
are displayed in a human-readable form. are displayed in a human-readable form.
@ -497,7 +532,6 @@ space and make memory size representations consistent throughout
and and
.BR limits.conf (5). .BR limits.conf (5).
.SH "AUTHORS" .SH "AUTHORS"
.LP
.B htop .B htop
was originally developed by Hisham Muhammad. was originally developed by Hisham Muhammad.
Nowadays it is maintained by the community at <htop@groups.io>. Nowadays it is maintained by the community at <htop@groups.io>.

22
htop.c
View File

@ -49,8 +49,8 @@ static void printHelpFlag(void) {
"-H --highlight-changes[=DELAY] Highlight new and old processes\n" "-H --highlight-changes[=DELAY] Highlight new and old processes\n"
"-M --no-mouse Disable the mouse\n" "-M --no-mouse Disable the mouse\n"
"-p --pid=PID[,PID,PID...] Show only the given PIDs\n" "-p --pid=PID[,PID,PID...] Show only the given PIDs\n"
"-s --sort-key=COLUMN Sort by COLUMN (try --sort-key=help for a list)\n" "-s --sort-key=COLUMN Sort by COLUMN in list view (try --sort-key=help for a list)\n"
"-t --tree Show the tree view by default\n" "-t --tree Show the tree view (can be combined with -s)\n"
"-u --user[=USERNAME] Show only processes for a given user (or $USER)\n" "-u --user[=USERNAME] Show only processes for a given user (or $USER)\n"
"-U --no-unicode Do not use unicode but plain ASCII\n" "-U --no-unicode Do not use unicode but plain ASCII\n"
"-V --version Print version info\n" "-V --version Print version info\n"
@ -93,7 +93,7 @@ static CommandLineSettings parseArguments(int argc, char** argv) {
.highlightDelaySecs = -1, .highlightDelaySecs = -1,
}; };
static struct option long_opts[] = const struct option long_opts[] =
{ {
{"help", no_argument, 0, 'h'}, {"help", no_argument, 0, 'h'},
{"version", no_argument, 0, 'V'}, {"version", no_argument, 0, 'V'},
@ -125,14 +125,14 @@ static CommandLineSettings parseArguments(int argc, char** argv) {
case 's': case 's':
assert(optarg); /* please clang analyzer, cause optarg can be NULL in the 'u' case */ assert(optarg); /* please clang analyzer, cause optarg can be NULL in the 'u' case */
if (String_eq(optarg, "help")) { if (String_eq(optarg, "help")) {
for (int j = 1; j < Platform_numberOfFields; j++) { for (int j = 1; j < LAST_PROCESSFIELD; j++) {
const char* name = Process_fields[j].name; const char* name = Process_fields[j].name;
if (name) printf ("%s\n", name); if (name) printf ("%s\n", name);
} }
exit(0); exit(0);
} }
flags.sortKey = 0; flags.sortKey = 0;
for (int j = 1; j < Platform_numberOfFields; j++) { for (int j = 1; j < LAST_PROCESSFIELD; j++) {
if (Process_fields[j].name == NULL) if (Process_fields[j].name == NULL)
continue; continue;
if (String_eq(optarg, Process_fields[j].name)) { if (String_eq(optarg, Process_fields[j].name)) {
@ -204,6 +204,7 @@ static CommandLineSettings parseArguments(int argc, char** argv) {
} }
case 'F': { case 'F': {
assert(optarg); assert(optarg);
free(flags.commFilter);
flags.commFilter = xStrdup(optarg); flags.commFilter = xStrdup(optarg);
break; break;
@ -298,9 +299,12 @@ int main(int argc, char** argv) {
if (flags.highlightDelaySecs != -1) if (flags.highlightDelaySecs != -1)
settings->highlightDelaySecs = flags.highlightDelaySecs; settings->highlightDelaySecs = flags.highlightDelaySecs;
if (flags.sortKey > 0) { if (flags.sortKey > 0) {
settings->sortKey = flags.sortKey; // -t -s <key> means "tree sorted by key"
settings->treeView = false; // -s <key> means "list sorted by key" (previous existing behavior)
settings->direction = 1; if (!flags.treeView) {
settings->treeView = false;
}
Settings_setSortKey(settings, flags.sortKey);
} }
CRT_init(&(settings->delay), settings->colorScheme, flags.allowUnicode); CRT_init(&(settings->delay), settings->colorScheme, flags.allowUnicode);
@ -310,8 +314,6 @@ int main(int argc, char** argv) {
MainPanel_updateTreeFunctions(panel, settings->treeView); MainPanel_updateTreeFunctions(panel, settings->treeView);
ProcessList_printHeader(pl, Panel_getHeader((Panel*)panel));
State state = { State state = {
.settings = settings, .settings = settings,
.ut = ut, .ut = ut,

View File

@ -8,6 +8,8 @@
{ include: ["<bits/getopt_core.h>", "private", "<unistd.h>", "public"] }, { include: ["<bits/getopt_core.h>", "private", "<unistd.h>", "public"] },
{ include: ["<sys/dirent.h>", "private", "<dirent.h>", "public"] },
{ include: ["<sys/signal.h>", "private", "<signal.h>", "public"] }, { include: ["<sys/signal.h>", "private", "<signal.h>", "public"] },
{ include: ["<sys/_stdarg.h>", "private", "<stdarg.h>", "public"] }, { include: ["<sys/_stdarg.h>", "private", "<stdarg.h>", "public"] },

View File

@ -17,7 +17,7 @@ in the source distribution for its full text.
Panel* IOPriorityPanel_new(IOPriority currPrio) { Panel* IOPriorityPanel_new(IOPriority currPrio) {
Panel* this = Panel_new(1, 1, 1, 1, true, Class(ListItem), FunctionBar_newEnterEsc("Set ", "Cancel ")); Panel* this = Panel_new(1, 1, 1, 1, Class(ListItem), true, FunctionBar_newEnterEsc("Set ", "Cancel "));
Panel_setHeader(this, "IO Priority:"); Panel_setHeader(this, "IO Priority:");
Panel_add(this, (Object*) ListItem_new("None (based on nice)", IOPriority_None)); Panel_add(this, (Object*) ListItem_new("None (based on nice)", IOPriority_None));

View File

@ -4,6 +4,7 @@
#include <dlfcn.h> #include <dlfcn.h>
#include <errno.h> #include <errno.h>
#include <math.h>
#include <sensors/sensors.h> #include <sensors/sensors.h>
#include "XUtils.h" #include "XUtils.h"
@ -16,12 +17,19 @@ static int (*sym_sensors_snprintf_chip_name)(char*, size_t, const sensors_chip_n
static const sensors_feature* (*sym_sensors_get_features)(const sensors_chip_name*, int*); static const sensors_feature* (*sym_sensors_get_features)(const sensors_chip_name*, int*);
static const sensors_subfeature* (*sym_sensors_get_subfeature)(const sensors_chip_name*, const sensors_feature*, sensors_subfeature_type); static const sensors_subfeature* (*sym_sensors_get_subfeature)(const sensors_chip_name*, const sensors_feature*, sensors_subfeature_type);
static int (*sym_sensors_get_value)(const sensors_chip_name*, int, double*); static int (*sym_sensors_get_value)(const sensors_chip_name*, int, double*);
static char* (*sym_sensors_get_label)(const sensors_chip_name*, const sensors_feature*);
static void* dlopenHandle = NULL; static void* dlopenHandle = NULL;
int LibSensors_init(FILE* input) { int LibSensors_init(FILE* input) {
if (!dlopenHandle) { if (!dlopenHandle) {
/* Find the unversioned libsensors.so (symlink) and prefer that, but Debian has .so.5 and Fedora .so.4 without
matching symlinks (unless people install the -dev packages) */
dlopenHandle = dlopen("libsensors.so", RTLD_LAZY); dlopenHandle = dlopen("libsensors.so", RTLD_LAZY);
if (!dlopenHandle)
dlopenHandle = dlopen("libsensors.so.5", RTLD_LAZY);
if (!dlopenHandle)
dlopenHandle = dlopen("libsensors.so.4", RTLD_LAZY);
if (!dlopenHandle) if (!dlopenHandle)
goto dlfailure; goto dlfailure;
@ -41,6 +49,7 @@ int LibSensors_init(FILE* input) {
resolve(sensors_get_features); resolve(sensors_get_features);
resolve(sensors_get_subfeature); resolve(sensors_get_subfeature);
resolve(sensors_get_value); resolve(sensors_get_value);
resolve(sensors_get_label);
#undef resolve #undef resolve
} }
@ -64,17 +73,23 @@ void LibSensors_cleanup(void) {
} }
} }
int LibSensors_getCPUTemperatures(CPUData* cpus, int cpuCount) { void LibSensors_getCPUTemperatures(CPUData* cpus, unsigned int cpuCount) {
if (!dlopenHandle) for (unsigned int i = 0; i <= cpuCount; i++)
return -ENOTSUP; cpus[i].temperature = NAN;
int tempCount = 0; if (!dlopenHandle)
return;
unsigned int coreTempCount = 0;
int n = 0; int n = 0;
for (const sensors_chip_name *chip = sym_sensors_get_detected_chips(NULL, &n); chip; chip = sym_sensors_get_detected_chips(NULL, &n)) { for (const sensors_chip_name *chip = sym_sensors_get_detected_chips(NULL, &n); chip; chip = sym_sensors_get_detected_chips(NULL, &n)) {
char buffer[32]; char buffer[32];
sym_sensors_snprintf_chip_name(buffer, sizeof(buffer), chip); sym_sensors_snprintf_chip_name(buffer, sizeof(buffer), chip);
if (!String_startsWith(buffer, "coretemp") && !String_startsWith(buffer, "cpu_thermal")) if (!String_startsWith(buffer, "coretemp") &&
!String_startsWith(buffer, "cpu_thermal") &&
!String_startsWith(buffer, "k10temp") &&
!String_startsWith(buffer, "zenpower"))
continue; continue;
int m = 0; int m = 0;
@ -82,7 +97,27 @@ int LibSensors_getCPUTemperatures(CPUData* cpus, int cpuCount) {
if (feature->type != SENSORS_FEATURE_TEMP) if (feature->type != SENSORS_FEATURE_TEMP)
continue; continue;
if (feature->number > cpuCount) char* label = sym_sensors_get_label(chip, feature);
if (!label)
continue;
unsigned int tempId;
if (String_startsWith(label, "Package ")) {
tempId = 0;
} else if (String_startsWith(label, "temp")) {
/* Raspberry Pi has only temp1 */
tempId = 0;
} else if (String_startsWith(label, "Tdie")) {
tempId = 0;
} else if (String_startsWith(label, "Core ")) {
tempId = 1 + atoi(label + strlen("Core "));
} else {
tempId = UINT_MAX;
}
free(label);
if (tempId > cpuCount)
continue; continue;
const sensors_subfeature *sub_feature = sym_sensors_get_subfeature(chip, feature, SENSORS_SUBFEATURE_TEMP_INPUT); const sensors_subfeature *sub_feature = sym_sensors_get_subfeature(chip, feature, SENSORS_SUBFEATURE_TEMP_INPUT);
@ -92,13 +127,43 @@ int LibSensors_getCPUTemperatures(CPUData* cpus, int cpuCount) {
if (r != 0) if (r != 0)
continue; continue;
cpus[feature->number].temperature = temp; cpus[tempId].temperature = temp;
tempCount++; if (tempId > 0)
coreTempCount++;
} }
} }
} }
return tempCount; const double packageTemp = cpus[0].temperature;
/* Only package temperature - copy to all cpus */
if (coreTempCount == 0 && !isnan(packageTemp)) {
for (unsigned int i = 1; i <= cpuCount; i++)
cpus[i].temperature = packageTemp;
return;
}
/* No package temperature - set to max core temperature */
if (isnan(packageTemp) && coreTempCount != 0) {
double maxTemp = NAN;
for (unsigned int i = 1; i <= cpuCount; i++) {
const double coreTemp = cpus[i].temperature;
if (isnan(coreTemp))
continue;
maxTemp = MAXIMUM(maxTemp, coreTemp);
}
cpus[0].temperature = maxTemp;
}
/* Half the temperatures, probably HT/SMT - copy to second half */
const unsigned int delta = cpuCount / 2;
if (coreTempCount == delta) {
for (unsigned int i = 1; i <= delta; i++)
cpus[i + delta].temperature = cpus[i].temperature;
}
} }
#endif /* HAVE_SENSORS_SENSORS_H */ #endif /* HAVE_SENSORS_SENSORS_H */

View File

@ -11,6 +11,6 @@
int LibSensors_init(FILE* input); int LibSensors_init(FILE* input);
void LibSensors_cleanup(void); void LibSensors_cleanup(void);
int LibSensors_getCPUTemperatures(CPUData* cpus, int cpuCount); void LibSensors_getCPUTemperatures(CPUData* cpus, unsigned int cpuCount);
#endif /* HEADER_LibSensors */ #endif /* HEADER_LibSensors */

View File

@ -24,22 +24,22 @@ in the source distribution for its full text.
/* semi-global */ /* semi-global */
long long btime; int pageSize;
int pageSizeKB;
/* Used to identify kernel threads in Comm and Exe columns */ /* Used to identify kernel threads in Comm and Exe columns */
static const char *const kthreadID = "KTHREAD"; static const char *const kthreadID = "KTHREAD";
ProcessFieldData Process_fields[] = { const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
[0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, }, [0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, },
[PID] = { .name = "PID", .title = " PID ", .description = "Process/thread ID", .flags = 0, }, [PID] = { .name = "PID", .title = "PID", .description = "Process/thread ID", .flags = 0, .pidColumn = true, },
[COMM] = { .name = "Command", .title = "Command ", .description = "Command line", .flags = 0, }, [COMM] = { .name = "Command", .title = "Command ", .description = "Command line", .flags = 0, },
[STATE] = { .name = "STATE", .title = "S ", .description = "Process state (S sleeping, R running, D disk, Z zombie, T traced, W paging, I idle)", .flags = 0, }, [STATE] = { .name = "STATE", .title = "S ", .description = "Process state (S sleeping, R running, D disk, Z zombie, T traced, W paging, I idle)", .flags = 0, },
[PPID] = { .name = "PPID", .title = " PPID ", .description = "Parent process ID", .flags = 0, }, [PPID] = { .name = "PPID", .title = "PPID", .description = "Parent process ID", .flags = 0, .pidColumn = true, },
[PGRP] = { .name = "PGRP", .title = " PGRP ", .description = "Process group ID", .flags = 0, }, [PGRP] = { .name = "PGRP", .title = "PGRP", .description = "Process group ID", .flags = 0, .pidColumn = true, },
[SESSION] = { .name = "SESSION", .title = " SID ", .description = "Process's session ID", .flags = 0, }, [SESSION] = { .name = "SESSION", .title = "SID", .description = "Process's session ID", .flags = 0, .pidColumn = true, },
[TTY_NR] = { .name = "TTY_NR", .title = "TTY ", .description = "Controlling terminal", .flags = 0, }, [TTY_NR] = { .name = "TTY_NR", .title = "TTY ", .description = "Controlling terminal", .flags = 0, },
[TPGID] = { .name = "TPGID", .title = " TPGID ", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, }, [TPGID] = { .name = "TPGID", .title = "TPGID", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, .pidColumn = true, },
[FLAGS] = { .name = "FLAGS", .title = NULL, .description = NULL, .flags = 0, },
[MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, }, [MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, },
[CMINFLT] = { .name = "CMINFLT", .title = " CMINFLT ", .description = "Children processes' minor faults", .flags = 0, }, [CMINFLT] = { .name = "CMINFLT", .title = " CMINFLT ", .description = "Children processes' minor faults", .flags = 0, },
[MAJFLT] = { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, }, [MAJFLT] = { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, },
@ -50,24 +50,7 @@ ProcessFieldData Process_fields[] = {
[CSTIME] = { .name = "CSTIME", .title = " CSTIME+ ", .description = "Children processes' system CPU time", .flags = 0, }, [CSTIME] = { .name = "CSTIME", .title = " CSTIME+ ", .description = "Children processes' system CPU time", .flags = 0, },
[PRIORITY] = { .name = "PRIORITY", .title = "PRI ", .description = "Kernel's internal priority for the process", .flags = 0, }, [PRIORITY] = { .name = "PRIORITY", .title = "PRI ", .description = "Kernel's internal priority for the process", .flags = 0, },
[NICE] = { .name = "NICE", .title = " NI ", .description = "Nice value (the higher the value, the more it lets other processes take priority)", .flags = 0, }, [NICE] = { .name = "NICE", .title = " NI ", .description = "Nice value (the higher the value, the more it lets other processes take priority)", .flags = 0, },
[ITREALVALUE] = { .name = "ITREALVALUE", .title = NULL, .description = NULL, .flags = 0, },
[STARTTIME] = { .name = "STARTTIME", .title = "START ", .description = "Time the process was started", .flags = 0, }, [STARTTIME] = { .name = "STARTTIME", .title = "START ", .description = "Time the process was started", .flags = 0, },
[VSIZE] = { .name = "VSIZE", .title = NULL, .description = NULL, .flags = 0, },
[RSS] = { .name = "RSS", .title = NULL, .description = NULL, .flags = 0, },
[RLIM] = { .name = "RLIM", .title = NULL, .description = NULL, .flags = 0, },
[STARTCODE] = { .name = "STARTCODE", .title = NULL, .description = NULL, .flags = 0, },
[ENDCODE] = { .name = "ENDCODE", .title = NULL, .description = NULL, .flags = 0, },
[STARTSTACK] = { .name = "STARTSTACK", .title = NULL, .description = NULL, .flags = 0, },
[KSTKESP] = { .name = "KSTKESP", .title = NULL, .description = NULL, .flags = 0, },
[KSTKEIP] = { .name = "KSTKEIP", .title = NULL, .description = NULL, .flags = 0, },
[SIGNAL] = { .name = "SIGNAL", .title = NULL, .description = NULL, .flags = 0, },
[BLOCKED] = { .name = "BLOCKED", .title = NULL, .description = NULL, .flags = 0, },
[SSIGIGNORE] = { .name = "SIGIGNORE", .title = NULL, .description = NULL, .flags = 0, },
[SIGCATCH] = { .name = "SIGCATCH", .title = NULL, .description = NULL, .flags = 0, },
[WCHAN] = { .name = "WCHAN", .title = NULL, .description = NULL, .flags = 0, },
[NSWAP] = { .name = "NSWAP", .title = NULL, .description = NULL, .flags = 0, },
[CNSWAP] = { .name = "CNSWAP", .title = NULL, .description = NULL, .flags = 0, },
[EXIT_SIGNAL] = { .name = "EXIT_SIGNAL", .title = NULL, .description = NULL, .flags = 0, },
[PROCESSOR] = { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, }, [PROCESSOR] = { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, },
[M_VIRT] = { .name = "M_VIRT", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, }, [M_VIRT] = { .name = "M_VIRT", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, },
[M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, }, [M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, },
@ -83,10 +66,10 @@ ProcessFieldData Process_fields[] = {
[USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, }, [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
[TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, }, [TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, },
[NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, }, [NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, },
[TGID] = { .name = "TGID", .title = " TGID ", .description = "Thread group ID (i.e. process ID)", .flags = 0, }, [TGID] = { .name = "TGID", .title = "TGID", .description = "Thread group ID (i.e. process ID)", .flags = 0, .pidColumn = true, },
#ifdef HAVE_OPENVZ #ifdef HAVE_OPENVZ
[CTID] = { .name = "CTID", .title = " CTID ", .description = "OpenVZ container ID (a.k.a. virtual environment ID)", .flags = PROCESS_FLAG_LINUX_OPENVZ, }, [CTID] = { .name = "CTID", .title = " CTID ", .description = "OpenVZ container ID (a.k.a. virtual environment ID)", .flags = PROCESS_FLAG_LINUX_OPENVZ, },
[VPID] = { .name = "VPID", .title = " VPID ", .description = "OpenVZ process ID", .flags = PROCESS_FLAG_LINUX_OPENVZ, }, [VPID] = { .name = "VPID", .title = "VPID", .description = "OpenVZ process ID", .flags = PROCESS_FLAG_LINUX_OPENVZ, .pidColumn = true, },
#endif #endif
#ifdef HAVE_VSERVER #ifdef HAVE_VSERVER
[VXID] = { .name = "VXID", .title = " VXID ", .description = "VServer process ID", .flags = PROCESS_FLAG_LINUX_VSERVER, }, [VXID] = { .name = "VXID", .title = " VXID ", .description = "VServer process ID", .flags = PROCESS_FLAG_LINUX_VSERVER, },
@ -105,9 +88,9 @@ ProcessFieldData Process_fields[] = {
[OOM] = { .name = "OOM", .title = " OOM ", .description = "OOM (Out-of-Memory) killer score", .flags = PROCESS_FLAG_LINUX_OOM, }, [OOM] = { .name = "OOM", .title = " OOM ", .description = "OOM (Out-of-Memory) killer score", .flags = PROCESS_FLAG_LINUX_OOM, },
[IO_PRIORITY] = { .name = "IO_PRIORITY", .title = "IO ", .description = "I/O priority", .flags = PROCESS_FLAG_LINUX_IOPRIO, }, [IO_PRIORITY] = { .name = "IO_PRIORITY", .title = "IO ", .description = "I/O priority", .flags = PROCESS_FLAG_LINUX_IOPRIO, },
#ifdef HAVE_DELAYACCT #ifdef HAVE_DELAYACCT
[PERCENT_CPU_DELAY] = { .name = "PERCENT_CPU_DELAY", .title = "CPUD% ", .description = "CPU delay %", .flags = 0, }, [PERCENT_CPU_DELAY] = { .name = "PERCENT_CPU_DELAY", .title = "CPUD% ", .description = "CPU delay %", .flags = PROCESS_FLAG_LINUX_DELAYACCT, },
[PERCENT_IO_DELAY] = { .name = "PERCENT_IO_DELAY", .title = "IOD% ", .description = "Block I/O delay %", .flags = 0, }, [PERCENT_IO_DELAY] = { .name = "PERCENT_IO_DELAY", .title = "IOD% ", .description = "Block I/O delay %", .flags = PROCESS_FLAG_LINUX_DELAYACCT, },
[PERCENT_SWAP_DELAY] = { .name = "PERCENT_SWAP_DELAY", .title = "SWAPD% ", .description = "Swapin delay %", .flags = 0, }, [PERCENT_SWAP_DELAY] = { .name = "PERCENT_SWAP_DELAY", .title = "SWAPD% ", .description = "Swapin delay %", .flags = PROCESS_FLAG_LINUX_DELAYACCT, },
#endif #endif
[M_PSS] = { .name = "M_PSS", .title = " PSS ", .description = "proportional set size, same as M_RESIDENT but each page is divided by the number of processes sharing it.", .flags = PROCESS_FLAG_LINUX_SMAPS, }, [M_PSS] = { .name = "M_PSS", .title = " PSS ", .description = "proportional set size, same as M_RESIDENT but each page is divided by the number of processes sharing it.", .flags = PROCESS_FLAG_LINUX_SMAPS, },
[M_SWAP] = { .name = "M_SWAP", .title = " SWAP ", .description = "Size of the process's swapped pages", .flags = PROCESS_FLAG_LINUX_SMAPS, }, [M_SWAP] = { .name = "M_SWAP", .title = " SWAP ", .description = "Size of the process's swapped pages", .flags = PROCESS_FLAG_LINUX_SMAPS, },
@ -117,20 +100,6 @@ ProcessFieldData Process_fields[] = {
[PROC_COMM] = { .name = "COMM", .title = "COMM ", .description = "comm string of the process from /proc/[pid]/comm", .flags = 0, }, [PROC_COMM] = { .name = "COMM", .title = "COMM ", .description = "comm string of the process from /proc/[pid]/comm", .flags = 0, },
[PROC_EXE] = { .name = "EXE", .title = "EXE ", .description = "Basename of exe of the process from /proc/[pid]/exe", .flags = 0, }, [PROC_EXE] = { .name = "EXE", .title = "EXE ", .description = "Basename of exe of the process from /proc/[pid]/exe", .flags = 0, },
[CWD] = { .name ="CWD", .title = "CWD ", .description = "The current working directory of the process", .flags = PROCESS_FLAG_LINUX_CWD, }, [CWD] = { .name ="CWD", .title = "CWD ", .description = "The current working directory of the process", .flags = PROCESS_FLAG_LINUX_CWD, },
[LAST_PROCESSFIELD] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, },
};
ProcessPidColumn Process_pidColumns[] = {
{ .id = PID, .label = "PID" },
{ .id = PPID, .label = "PPID" },
#ifdef HAVE_OPENVZ
{ .id = VPID, .label = "VPID" },
#endif
{ .id = TPGID, .label = "TPGID" },
{ .id = TGID, .label = "TGID" },
{ .id = PGRP, .label = "PGRP" },
{ .id = SESSION, .label = "SID" },
{ .id = 0, .label = NULL },
}; };
/* This function returns the string displayed in Command column, so that sorting /* This function returns the string displayed in Command column, so that sorting
@ -183,6 +152,11 @@ static int LinuxProcess_effectiveIOPriority(const LinuxProcess* this) {
return this->ioPriority; return this->ioPriority;
} }
#ifdef __ANDROID__
#define SYS_ioprio_get __NR_ioprio_get
#define SYS_ioprio_set __NR_ioprio_set
#endif
IOPriority LinuxProcess_updateIOPriority(LinuxProcess* this) { IOPriority LinuxProcess_updateIOPriority(LinuxProcess* this) {
IOPriority ioprio = 0; IOPriority ioprio = 0;
// Other OSes masquerading as Linux (NetBSD?) don't have this syscall // Other OSes masquerading as Linux (NetBSD?) don't have this syscall
@ -224,12 +198,11 @@ static bool findCommInCmdline(const char *comm, const char *cmdline, int cmdline
/* Try to find procComm in tokenized cmdline - this might in rare cases /* Try to find procComm in tokenized cmdline - this might in rare cases
* mis-identify a string or fail, if comm or cmdline had been unsuitably * mis-identify a string or fail, if comm or cmdline had been unsuitably
* modified by the process */ * modified by the process */
const char *token;
const char *tokenBase; const char *tokenBase;
size_t tokenLen; size_t tokenLen;
const size_t commLen = strlen(comm); const size_t commLen = strlen(comm);
for (token = cmdline + cmdlineBasenameOffset; *token; ) { for (const char *token = cmdline + cmdlineBasenameOffset; *token; ) {
for (tokenBase = token; *token && *token != '\n'; ++token) { for (tokenBase = token; *token && *token != '\n'; ++token) {
if (*token == '/') { if (*token == '/') {
tokenBase = token + 1; tokenBase = token + 1;
@ -394,6 +367,16 @@ void LinuxProcess_makeCommandStr(Process* this) {
char *str = strStart; char *str = strStart;
int cmdlineBasenameOffset = lp->procCmdlineBasenameOffset; int cmdlineBasenameOffset = lp->procCmdlineBasenameOffset;
int cmdlineBasenameEnd = lp->procCmdlineBasenameEnd;
if (!cmdline) {
cmdlineBasenameOffset = 0;
cmdlineBasenameEnd = 0;
cmdline = "(zombie)";
}
assert(cmdlineBasenameOffset >= 0);
assert(cmdlineBasenameOffset <= (int)strlen(cmdline));
if (!showMergedCommand || !procExe || !procComm) { /* fall back to cmdline */ if (!showMergedCommand || !procExe || !procComm) { /* fall back to cmdline */
if (showMergedCommand && !procExe && procComm && strlen(procComm)) { /* Prefix column with comm */ if (showMergedCommand && !procExe && procComm && strlen(procComm)) { /* Prefix column with comm */
@ -411,11 +394,11 @@ void LinuxProcess_makeCommandStr(Process* this) {
if (showProgramPath) { if (showProgramPath) {
(void) stpcpyWithNewlineConversion(str, cmdline); (void) stpcpyWithNewlineConversion(str, cmdline);
mc->baseStart = cmdlineBasenameOffset; mc->baseStart = cmdlineBasenameOffset;
mc->baseEnd = lp->procCmdlineBasenameEnd; mc->baseEnd = cmdlineBasenameEnd;
} else { } else {
(void) stpcpyWithNewlineConversion(str, cmdline + cmdlineBasenameOffset); (void) stpcpyWithNewlineConversion(str, cmdline + cmdlineBasenameOffset);
mc->baseStart = 0; mc->baseStart = 0;
mc->baseEnd = lp->procCmdlineBasenameEnd - cmdlineBasenameOffset; mc->baseEnd = cmdlineBasenameEnd - cmdlineBasenameOffset;
} }
if (mc->sep1) { if (mc->sep1) {
@ -430,6 +413,9 @@ void LinuxProcess_makeCommandStr(Process* this) {
int exeBasenameOffset = lp->procExeBasenameOffset; int exeBasenameOffset = lp->procExeBasenameOffset;
int exeBasenameLen = exeLen - exeBasenameOffset; int exeBasenameLen = exeLen - exeBasenameOffset;
assert(exeBasenameOffset >= 0);
assert(exeBasenameOffset <= (int)strlen(procExe));
/* Start with copying exe */ /* Start with copying exe */
if (showProgramPath) { if (showProgramPath) {
str = stpcpy(str, procExe); str = stpcpy(str, procExe);
@ -536,36 +522,36 @@ static void LinuxProcess_writeCommand(const Process* this, int attr, int baseAtt
if(lp->procExeDeleted) if(lp->procExeDeleted)
baseAttr = CRT_colors[FAILED_READ]; baseAttr = CRT_colors[FAILED_READ];
RichString_append(str, attr, lp->mergedCommand.str); RichString_appendWide(str, attr, lp->mergedCommand.str);
if (lp->mergedCommand.commEnd) { if (lp->mergedCommand.commEnd) {
if (!lp->mergedCommand.separateComm && commStart == baseStart && highlightBaseName) { if (!lp->mergedCommand.separateComm && commStart == baseStart && highlightBaseName) {
/* If it was matched with procExe's basename, make it bold if needed */ /* If it was matched with procExe's basename, make it bold if needed */
if (commEnd > baseEnd) { if (commEnd > baseEnd) {
RichString_setAttrn(str, A_BOLD | baseAttr, baseStart, baseEnd - 1); RichString_setAttrn(str, A_BOLD | baseAttr, baseStart, baseEnd - baseStart);
RichString_setAttrn(str, A_BOLD | commAttr, baseEnd, commEnd - 1); RichString_setAttrn(str, A_BOLD | commAttr, baseEnd, commEnd - baseEnd);
} else if (commEnd < baseEnd) { } else if (commEnd < baseEnd) {
RichString_setAttrn(str, A_BOLD | commAttr, commStart, commEnd - 1); RichString_setAttrn(str, A_BOLD | commAttr, commStart, commEnd - commStart);
RichString_setAttrn(str, A_BOLD | baseAttr, commEnd, baseEnd - 1); RichString_setAttrn(str, A_BOLD | baseAttr, commEnd, baseEnd - commEnd);
} else { } else {
// Actually should be highlighted commAttr, but marked baseAttr to reduce visual noise // Actually should be highlighted commAttr, but marked baseAttr to reduce visual noise
RichString_setAttrn(str, A_BOLD | baseAttr, commStart, commEnd - 1); RichString_setAttrn(str, A_BOLD | baseAttr, commStart, commEnd - commStart);
} }
baseStart = baseEnd; baseStart = baseEnd;
} else { } else {
RichString_setAttrn(str, commAttr, commStart, commEnd - 1); RichString_setAttrn(str, commAttr, commStart, commEnd - commStart);
} }
} }
if (baseStart < baseEnd && highlightBaseName) { if (baseStart < baseEnd && highlightBaseName) {
RichString_setAttrn(str, baseAttr, baseStart, baseEnd - 1); RichString_setAttrn(str, baseAttr, baseStart, baseEnd - baseStart);
} }
if (mc->sep1) if (mc->sep1)
RichString_setAttrn(str, CRT_colors[FAILED_READ], strStart + mc->sep1, strStart + mc->sep1); RichString_setAttrn(str, CRT_colors[FAILED_READ], strStart + mc->sep1, 1);
if (mc->sep2) if (mc->sep2)
RichString_setAttrn(str, CRT_colors[FAILED_READ], strStart + mc->sep2, strStart + mc->sep2); RichString_setAttrn(str, CRT_colors[FAILED_READ], strStart + mc->sep2, 1);
} }
static void LinuxProcess_writeCommandField(const Process *this, RichString *str, char *buffer, int n, int attr) { static void LinuxProcess_writeCommandField(const Process *this, RichString *str, char *buffer, int n, int attr) {
@ -605,10 +591,11 @@ static void LinuxProcess_writeCommandField(const Process *this, RichString *str,
buf = stpcpy(buf, " "); buf = stpcpy(buf, " ");
} }
} }
n -= (buf - buffer); n -= (buf - buffer);
const char* draw = CRT_treeStr[lastItem ? (this->settings->direction == 1 ? TREE_STR_BEND : TREE_STR_TEND) : TREE_STR_RTEE]; const char* draw = CRT_treeStr[lastItem ? TREE_STR_BEND : TREE_STR_RTEE];
xSnprintf(buf, n, "%s%s ", draw, this->showChildren ? CRT_treeStr[TREE_STR_SHUT] : CRT_treeStr[TREE_STR_OPEN] ); 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);
LinuxProcess_writeCommand(this, attr, baseattr, str); LinuxProcess_writeCommand(this, attr, baseattr, str);
} }
} }
@ -619,7 +606,7 @@ static void LinuxProcess_writeField(const Process* this, RichString* str, Proces
char buffer[256]; buffer[255] = '\0'; char buffer[256]; buffer[255] = '\0';
int attr = CRT_colors[DEFAULT_COLOR]; int attr = CRT_colors[DEFAULT_COLOR];
size_t n = sizeof(buffer) - 1; size_t n = sizeof(buffer) - 1;
switch ((int)field) { switch (field) {
case TTY_NR: { case TTY_NR: {
if (lp->ttyDevice) { if (lp->ttyDevice) {
xSnprintf(buffer, n, "%-9s", lp->ttyDevice + 5 /* skip "/dev/" */); xSnprintf(buffer, n, "%-9s", lp->ttyDevice + 5 /* skip "/dev/" */);
@ -631,19 +618,19 @@ static void LinuxProcess_writeField(const Process* this, RichString* str, Proces
} }
case CMINFLT: Process_colorNumber(str, lp->cminflt, coloring); return; case CMINFLT: Process_colorNumber(str, lp->cminflt, coloring); return;
case CMAJFLT: Process_colorNumber(str, lp->cmajflt, coloring); return; case CMAJFLT: Process_colorNumber(str, lp->cmajflt, coloring); return;
case M_DRS: Process_humanNumber(str, lp->m_drs * CRT_pageSizeKB, coloring); return; case M_DRS: Process_humanNumber(str, lp->m_drs * pageSizeKB, coloring); return;
case M_DT: Process_humanNumber(str, lp->m_dt * CRT_pageSizeKB, coloring); return; case M_DT: Process_humanNumber(str, lp->m_dt * pageSizeKB, coloring); return;
case M_LRS: case M_LRS:
if (lp->m_lrs) { if (lp->m_lrs) {
Process_humanNumber(str, lp->m_lrs * CRT_pageSizeKB, coloring); Process_humanNumber(str, lp->m_lrs * pageSizeKB, coloring);
return; return;
} }
attr = CRT_colors[PROCESS_SHADOW]; attr = CRT_colors[PROCESS_SHADOW];
xSnprintf(buffer, n, " N/A "); xSnprintf(buffer, n, " N/A ");
break; break;
case M_TRS: Process_humanNumber(str, lp->m_trs * CRT_pageSizeKB, coloring); return; case M_TRS: Process_humanNumber(str, lp->m_trs * pageSizeKB, coloring); return;
case M_SHARE: Process_humanNumber(str, lp->m_share * CRT_pageSizeKB, coloring); return; case M_SHARE: Process_humanNumber(str, lp->m_share * pageSizeKB, coloring); return;
case M_PSS: Process_humanNumber(str, lp->m_pss, coloring); return; case M_PSS: Process_humanNumber(str, lp->m_pss, coloring); return;
case M_SWAP: Process_humanNumber(str, lp->m_swap, coloring); return; case M_SWAP: Process_humanNumber(str, lp->m_swap, coloring); return;
case M_PSSWP: Process_humanNumber(str, lp->m_psswp, coloring); return; case M_PSSWP: Process_humanNumber(str, lp->m_psswp, coloring); return;
@ -674,7 +661,7 @@ static void LinuxProcess_writeField(const Process* this, RichString* str, Proces
} }
#ifdef HAVE_OPENVZ #ifdef HAVE_OPENVZ
case CTID: xSnprintf(buffer, n, "%-8s ", lp->ctid ? lp->ctid : ""); break; case CTID: xSnprintf(buffer, n, "%-8s ", lp->ctid ? lp->ctid : ""); break;
case VPID: xSnprintf(buffer, n, Process_pidFormat, lp->vpid); break; case VPID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, lp->vpid); break;
#endif #endif
#ifdef HAVE_VSERVER #ifdef HAVE_VSERVER
case VXID: xSnprintf(buffer, n, "%5u ", lp->vxid); break; case VXID: xSnprintf(buffer, n, "%5u ", lp->vxid); break;
@ -720,59 +707,58 @@ static void LinuxProcess_writeField(const Process* this, RichString* str, Proces
return; return;
} }
case PROC_COMM: { case PROC_COMM: {
const char* procComm;
if (lp->procComm) { if (lp->procComm) {
attr = CRT_colors[Process_isUserlandThread(this) ? PROCESS_THREAD_COMM : PROCESS_COMM]; attr = CRT_colors[Process_isUserlandThread(this) ? PROCESS_THREAD_COMM : PROCESS_COMM];
/* 15 being (TASK_COMM_LEN - 1) */ procComm = lp->procComm;
xSnprintf(buffer, n, "%-15.15s ", lp->procComm);
} else { } else {
attr = CRT_colors[PROCESS_SHADOW]; attr = CRT_colors[PROCESS_SHADOW];
xSnprintf(buffer, n, "%-15.15s ", Process_isKernelThread(lp) ? kthreadID : "N/A"); procComm = Process_isKernelThread(lp) ? kthreadID : "N/A";
} }
break; /* 15 being (TASK_COMM_LEN - 1) */
Process_printLeftAlignedField(str, attr, procComm, 15);
return;
} }
case PROC_EXE: { case PROC_EXE: {
const char* procExe;
if (lp->procExe) { if (lp->procExe) {
attr = CRT_colors[Process_isUserlandThread(this) ? PROCESS_THREAD_BASENAME : PROCESS_BASENAME]; attr = CRT_colors[Process_isUserlandThread(this) ? PROCESS_THREAD_BASENAME : PROCESS_BASENAME];
if (lp->procExeDeleted) if (lp->procExeDeleted)
attr = CRT_colors[FAILED_READ]; attr = CRT_colors[FAILED_READ];
xSnprintf(buffer, n, "%-15.15s ", lp->procExe + lp->procExeBasenameOffset); procExe = lp->procExe + lp->procExeBasenameOffset;
} else { } else {
attr = CRT_colors[PROCESS_SHADOW]; attr = CRT_colors[PROCESS_SHADOW];
xSnprintf(buffer, n, "%-15.15s ", Process_isKernelThread(lp) ? kthreadID : "N/A"); procExe = Process_isKernelThread(lp) ? kthreadID : "N/A";
} }
break; Process_printLeftAlignedField(str, attr, procExe, 15);
return;
} }
case CWD: case CWD: {
const char* cwd;
if (!lp->cwd) { if (!lp->cwd) {
xSnprintf(buffer, n, "%-25s ", "N/A");
attr = CRT_colors[PROCESS_SHADOW]; attr = CRT_colors[PROCESS_SHADOW];
cwd = "N/A";
} else if (String_startsWith(lp->cwd, "/proc/") && strstr(lp->cwd, " (deleted)") != NULL) { } else if (String_startsWith(lp->cwd, "/proc/") && strstr(lp->cwd, " (deleted)") != NULL) {
xSnprintf(buffer, n, "%-25s ", "main thread terminated");
attr = CRT_colors[PROCESS_SHADOW]; attr = CRT_colors[PROCESS_SHADOW];
cwd = "main thread terminated";
} else { } else {
xSnprintf(buffer, n, "%-25.25s ", lp->cwd); cwd = lp->cwd;
} }
break; Process_printLeftAlignedField(str, attr, cwd, 25);
return;
}
default: default:
Process_writeField(this, str, field); Process_writeField(this, str, field);
return; return;
} }
RichString_append(str, attr, buffer); RichString_appendWide(str, attr, buffer);
} }
static long LinuxProcess_compare(const void* v1, const void* v2) { static int LinuxProcess_compareByKey(const Process* v1, const Process* v2, ProcessField key) {
const LinuxProcess *p1, *p2; const LinuxProcess* p1 = (const LinuxProcess*)v1;
const Settings *settings = ((const Process*)v1)->settings; const LinuxProcess* p2 = (const LinuxProcess*)v2;
if (settings->direction == 1) { switch (key) {
p1 = (const LinuxProcess*)v1;
p2 = (const LinuxProcess*)v2;
} else {
p2 = (const LinuxProcess*)v1;
p1 = (const LinuxProcess*)v2;
}
switch ((int)settings->sortKey) {
case M_DRS: case M_DRS:
return SPACESHIP_NUMBER(p2->m_drs, p1->m_drs); return SPACESHIP_NUMBER(p2->m_drs, p1->m_drs);
case M_DT: case M_DT:
@ -858,7 +844,7 @@ static long LinuxProcess_compare(const void* v1, const void* v2) {
case CWD: case CWD:
return SPACESHIP_NULLSTR(p1->cwd, p2->cwd); return SPACESHIP_NULLSTR(p1->cwd, p2->cwd);
default: default:
return Process_compare(v1, v2); return Process_compareByKey_Base(v1, v2, key);
} }
} }
@ -871,8 +857,9 @@ const ProcessClass LinuxProcess_class = {
.extends = Class(Process), .extends = Class(Process),
.display = Process_display, .display = Process_display,
.delete = Process_delete, .delete = Process_delete,
.compare = LinuxProcess_compare .compare = Process_compare
}, },
.writeField = LinuxProcess_writeField, .writeField = LinuxProcess_writeField,
.getCommandStr = LinuxProcess_getCommandStr .getCommandStr = LinuxProcess_getCommandStr,
.compareByKey = LinuxProcess_compareByKey
}; };

View File

@ -18,85 +18,18 @@ in the source distribution for its full text.
#include "Settings.h" #include "Settings.h"
#define PROCESS_FLAG_LINUX_IOPRIO 0x00000100 #define PROCESS_FLAG_LINUX_IOPRIO 0x00000100
#define PROCESS_FLAG_LINUX_OPENVZ 0x00000200 #define PROCESS_FLAG_LINUX_OPENVZ 0x00000200
#define PROCESS_FLAG_LINUX_VSERVER 0x00000400 #define PROCESS_FLAG_LINUX_VSERVER 0x00000400
#define PROCESS_FLAG_LINUX_CGROUP 0x00000800 #define PROCESS_FLAG_LINUX_CGROUP 0x00000800
#define PROCESS_FLAG_LINUX_OOM 0x00001000 #define PROCESS_FLAG_LINUX_OOM 0x00001000
#define PROCESS_FLAG_LINUX_SMAPS 0x00002000 #define PROCESS_FLAG_LINUX_SMAPS 0x00002000
#define PROCESS_FLAG_LINUX_CTXT 0x00004000 #define PROCESS_FLAG_LINUX_CTXT 0x00004000
#define PROCESS_FLAG_LINUX_SECATTR 0x00008000 #define PROCESS_FLAG_LINUX_SECATTR 0x00008000
#define PROCESS_FLAG_LINUX_LRS_FIX 0x00010000 #define PROCESS_FLAG_LINUX_LRS_FIX 0x00010000
#define PROCESS_FLAG_LINUX_CWD 0x00020000 #define PROCESS_FLAG_LINUX_CWD 0x00020000
#define PROCESS_FLAG_LINUX_DELAYACCT 0x00040000
typedef enum UnsupportedProcessFields {
FLAGS = 9,
ITREALVALUE = 20,
VSIZE = 22,
RSS = 23,
RLIM = 24,
STARTCODE = 25,
ENDCODE = 26,
STARTSTACK = 27,
KSTKESP = 28,
KSTKEIP = 29,
SIGNAL = 30,
BLOCKED = 31,
SSIGIGNORE = 32,
SIGCATCH = 33,
WCHAN = 34,
NSWAP = 35,
CNSWAP = 36,
EXIT_SIGNAL = 37,
} UnsupportedProcessField;
typedef enum LinuxProcessFields {
CMINFLT = 11,
CMAJFLT = 13,
UTIME = 14,
STIME = 15,
CUTIME = 16,
CSTIME = 17,
M_SHARE = 41,
M_TRS = 42,
M_DRS = 43,
M_LRS = 44,
M_DT = 45,
#ifdef HAVE_OPENVZ
CTID = 100,
VPID = 101,
#endif
#ifdef HAVE_VSERVER
VXID = 102,
#endif
RCHAR = 103,
WCHAR = 104,
SYSCR = 105,
SYSCW = 106,
RBYTES = 107,
WBYTES = 108,
CNCLWB = 109,
IO_READ_RATE = 110,
IO_WRITE_RATE = 111,
IO_RATE = 112,
CGROUP = 113,
OOM = 114,
IO_PRIORITY = 115,
#ifdef HAVE_DELAYACCT
PERCENT_CPU_DELAY = 116,
PERCENT_IO_DELAY = 117,
PERCENT_SWAP_DELAY = 118,
#endif
M_PSS = 119,
M_SWAP = 120,
M_PSSWP = 121,
CTXT = 122,
SECATTR = 123,
PROC_COMM = 124,
PROC_EXE = 125,
CWD = 126,
LAST_PROCESSFIELD = 127,
} LinuxProcessField;
/* LinuxProcessMergedCommand is populated by LinuxProcess_makeCommandStr: It /* LinuxProcessMergedCommand is populated by LinuxProcess_makeCommandStr: It
* contains the merged Command string, and the information needed by * contains the merged Command string, and the information needed by
@ -191,11 +124,11 @@ static inline bool Process_isUserlandThread(const Process* this) {
return this->pid != this->tgid; return this->pid != this->tgid;
} }
extern long long btime; extern int pageSize;
extern ProcessFieldData Process_fields[]; extern int pageSizeKB;
extern ProcessPidColumn Process_pidColumns[]; extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD];
extern const ProcessClass LinuxProcess_class; extern const ProcessClass LinuxProcess_class;

View File

@ -57,6 +57,16 @@ in the source distribution for its full text.
#endif #endif
// CentOS 6's kernel doesn't provide a definition of O_PATH
// based on definition taken from uapi/asm-generic/fcnth.h in Linux kernel tree
#ifndef O_PATH
# define O_PATH 010000000
#endif
static long long btime;
static long jiffy;
static FILE* fopenat(openat_arg_t openatArg, const char* pathname, const char* mode) { static FILE* fopenat(openat_arg_t openatArg, const char* pathname, const char* mode) {
assert(String_eq(mode, "r")); /* only currently supported mode */ assert(String_eq(mode, "r")); /* only currently supported mode */
@ -92,7 +102,7 @@ static void LinuxProcessList_initTtyDrivers(LinuxProcessList* this) {
int numDrivers = 0; int numDrivers = 0;
int allocd = 10; int allocd = 10;
ttyDrivers = xMalloc(sizeof(TtyDriver) * allocd); ttyDrivers = xMallocArray(allocd, sizeof(TtyDriver));
char* at = buf; char* at = buf;
while (*at != '\0') { while (*at != '\0') {
at = strchr(at, ' '); // skip first token at = strchr(at, ' '); // skip first token
@ -126,7 +136,7 @@ static void LinuxProcessList_initTtyDrivers(LinuxProcessList* this) {
numDrivers++; numDrivers++;
if (numDrivers == allocd) { if (numDrivers == allocd) {
allocd += 10; allocd += 10;
ttyDrivers = xRealloc(ttyDrivers, sizeof(TtyDriver) * allocd); ttyDrivers = xReallocArray(ttyDrivers, allocd, sizeof(TtyDriver));
} }
} }
numDrivers++; numDrivers++;
@ -198,39 +208,39 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, ui
ProcessList_init(pl, Class(LinuxProcess), usersTable, pidMatchList, userId); ProcessList_init(pl, Class(LinuxProcess), usersTable, pidMatchList, userId);
LinuxProcessList_initTtyDrivers(this); LinuxProcessList_initTtyDrivers(this);
#ifdef HAVE_DELAYACCT // Initialize page size
LinuxProcessList_initNetlinkSocket(this); pageSize = sysconf(_SC_PAGESIZE);
#endif if (pageSize == -1)
CRT_fatalError("Cannot get pagesize by sysconf(_SC_PAGESIZE)");
pageSizeKB = pageSize / ONE_K;
// Check for /proc/*/smaps_rollup availability (improves smaps parsing speed, Linux 4.14+) // Initialize clock ticks
FILE* file = fopen(PROCDIR "/self/smaps_rollup", "r"); jiffy = sysconf(_SC_CLK_TCK);
if (file != NULL) { if (jiffy == -1)
this->haveSmapsRollup = true; CRT_fatalError("Cannot get clock ticks by sysconf(_SC_CLK_TCK)");
fclose(file);
} else {
this->haveSmapsRollup = false;
}
// Read btime // Test /proc/PID/smaps_rollup availability (faster to parse, Linux 4.14+)
this->haveSmapsRollup = (access(PROCDIR "/self/smaps_rollup", R_OK) == 0);
// Read btime (the kernel boot time, as number of seconds since the epoch)
{ {
FILE* statfile = fopen(PROCSTATFILE, "r"); FILE* statfile = fopen(PROCSTATFILE, "r");
if (statfile == NULL) { if (statfile == NULL)
CRT_fatalError("Cannot open " PROCSTATFILE); CRT_fatalError("Cannot open " PROCSTATFILE);
}
while (true) { while (true) {
char buffer[PROC_LINE_LENGTH + 1]; char buffer[PROC_LINE_LENGTH + 1];
if (fgets(buffer, sizeof(buffer), statfile) == NULL) { if (fgets(buffer, sizeof(buffer), statfile) == NULL)
CRT_fatalError("No btime in " PROCSTATFILE);
} else if (String_startsWith(buffer, "btime ")) {
if (sscanf(buffer, "btime %lld\n", &btime) != 1) {
CRT_fatalError("Failed to parse btime from " PROCSTATFILE);
}
break; break;
} if (String_startsWith(buffer, "btime ") == false)
continue;
if (sscanf(buffer, "btime %lld\n", &btime) == 1)
break;
CRT_fatalError("Failed to parse btime from " PROCSTATFILE);
} }
fclose(statfile); fclose(statfile);
if (!btime)
CRT_fatalError("No btime in " PROCSTATFILE);
} }
// Initialize CPU count // Initialize CPU count
@ -267,16 +277,7 @@ void ProcessList_delete(ProcessList* pl) {
free(this); free(this);
} }
static inline unsigned long long LinuxProcess_adjustTime(unsigned long long t) { static inline unsigned long long LinuxProcessList_adjustTime(unsigned long long t) {
static long jiffy = -1;
if (jiffy == -1) {
errno = 0;
jiffy = sysconf(_SC_CLK_TCK);
if (errno || -1 == jiffy) {
jiffy = -1;
return t; // Assume 100Hz clock
}
}
return t * 100 / jiffy; return t * 100 / jiffy;
} }
@ -329,13 +330,13 @@ static bool LinuxProcessList_readStatFile(Process* process, openat_arg_t procFd,
location += 1; location += 1;
lp->cmajflt = strtoull(location, &location, 10); lp->cmajflt = strtoull(location, &location, 10);
location += 1; location += 1;
lp->utime = LinuxProcess_adjustTime(strtoull(location, &location, 10)); lp->utime = LinuxProcessList_adjustTime(strtoull(location, &location, 10));
location += 1; location += 1;
lp->stime = LinuxProcess_adjustTime(strtoull(location, &location, 10)); lp->stime = LinuxProcessList_adjustTime(strtoull(location, &location, 10));
location += 1; location += 1;
lp->cutime = LinuxProcess_adjustTime(strtoull(location, &location, 10)); lp->cutime = LinuxProcessList_adjustTime(strtoull(location, &location, 10));
location += 1; location += 1;
lp->cstime = LinuxProcess_adjustTime(strtoull(location, &location, 10)); lp->cstime = LinuxProcessList_adjustTime(strtoull(location, &location, 10));
location += 1; location += 1;
process->priority = strtol(location, &location, 10); process->priority = strtol(location, &location, 10);
location += 1; location += 1;
@ -345,7 +346,7 @@ static bool LinuxProcessList_readStatFile(Process* process, openat_arg_t procFd,
location += 1; location += 1;
location = strchr(location, ' ') + 1; location = strchr(location, ' ') + 1;
if (process->starttime_ctime == 0) { if (process->starttime_ctime == 0) {
process->starttime_ctime = btime + LinuxProcess_adjustTime(strtoll(location, &location, 10)) / 100; process->starttime_ctime = btime + LinuxProcessList_adjustTime(strtoll(location, &location, 10)) / 100;
} else { } else {
location = strchr(location, ' ') + 1; location = strchr(location, ' ') + 1;
} }
@ -483,7 +484,7 @@ static inline uint64_t fast_strtoull_hex(char **str, int maxlen) {
return result; return result;
} }
static void LinuxProcessList_calcLibSize_helper(ATTR_UNUSED hkey_t key, void* value, void* data) { static void LinuxProcessList_calcLibSize_helper(ATTR_UNUSED ht_key_t key, void* value, void* data) {
if (!data) if (!data)
return; return;
@ -573,7 +574,7 @@ static uint64_t LinuxProcessList_calcLibSize(openat_arg_t procFd) {
Hashtable_delete(ht); Hashtable_delete(ht);
return total_size / CRT_pageSize; return total_size / pageSize;
} }
static bool LinuxProcessList_readStatmFile(LinuxProcess* process, openat_arg_t procFd, bool performLookup, unsigned long long now) { static bool LinuxProcessList_readStatmFile(LinuxProcess* process, openat_arg_t procFd, bool performLookup, unsigned long long now) {
@ -593,6 +594,9 @@ static bool LinuxProcessList_readStatmFile(LinuxProcess* process, openat_arg_t p
fclose(statmfile); fclose(statmfile);
if (r == 7) { if (r == 7) {
process->super.m_virt *= pageSizeKB;
process->super.m_resident *= pageSizeKB;
if (tmp_m_lrs) { if (tmp_m_lrs) {
process->m_lrs = tmp_m_lrs; process->m_lrs = tmp_m_lrs;
} else if (performLookup) { } else if (performLookup) {
@ -947,12 +951,19 @@ static int handleNetlinkMsg(struct nl_msg* nlmsg, void* linuxProcess) {
static void LinuxProcessList_readDelayAcctData(LinuxProcessList* this, LinuxProcess* process) { static void LinuxProcessList_readDelayAcctData(LinuxProcessList* this, LinuxProcess* process) {
struct nl_msg* msg; struct nl_msg* msg;
if (!this->netlink_socket) {
LinuxProcessList_initNetlinkSocket(this);
if (!this->netlink_socket) {
goto delayacct_failure;
}
}
if (nl_socket_modify_cb(this->netlink_socket, NL_CB_VALID, NL_CB_CUSTOM, handleNetlinkMsg, process) < 0) { if (nl_socket_modify_cb(this->netlink_socket, NL_CB_VALID, NL_CB_CUSTOM, handleNetlinkMsg, process) < 0) {
return; goto delayacct_failure;
} }
if (! (msg = nlmsg_alloc())) { if (! (msg = nlmsg_alloc())) {
return; goto delayacct_failure;
} }
if (! genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, this->netlink_family, 0, NLM_F_REQUEST, TASKSTATS_CMD_GET, TASKSTATS_VERSION)) { if (! genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, this->netlink_family, 0, NLM_F_REQUEST, TASKSTATS_CMD_GET, TASKSTATS_VERSION)) {
@ -964,15 +975,19 @@ static void LinuxProcessList_readDelayAcctData(LinuxProcessList* this, LinuxProc
} }
if (nl_send_sync(this->netlink_socket, msg) < 0) { if (nl_send_sync(this->netlink_socket, msg) < 0) {
process->swapin_delay_percent = NAN; goto delayacct_failure;
process->blkio_delay_percent = NAN;
process->cpu_delay_percent = NAN;
return;
} }
if (nl_recvmsgs_default(this->netlink_socket) < 0) { if (nl_recvmsgs_default(this->netlink_socket) < 0) {
return; goto delayacct_failure;
} }
return;
delayacct_failure:
process->swapin_delay_percent = NAN;
process->blkio_delay_percent = NAN;
process->cpu_delay_percent = NAN;
} }
#endif #endif
@ -1363,9 +1378,9 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_
} }
/* period might be 0 after system sleep */ /* period might be 0 after system sleep */
float percent_cpu = (period < 1e-6) ? 0.0f : ((lp->utime + lp->stime - lasttimes) / period * 100.0); float percent_cpu = (period < 1E-6) ? 0.0F : ((lp->utime + lp->stime - lasttimes) / period * 100.0);
proc->percent_cpu = CLAMP(percent_cpu, 0.0f, cpus * 100.0f); proc->percent_cpu = CLAMP(percent_cpu, 0.0F, cpus * 100.0F);
proc->percent_mem = (proc->m_resident * CRT_pageSizeKB) / (double)(pl->totalMem) * 100.0; proc->percent_mem = proc->m_resident / (double)(pl->totalMem) * 100.0;
if (!preExisting) { if (!preExisting) {
@ -1411,7 +1426,9 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_
} }
#ifdef HAVE_DELAYACCT #ifdef HAVE_DELAYACCT
LinuxProcessList_readDelayAcctData(this, lp); if (settings->flags & PROCESS_FLAG_LINUX_DELAYACCT) {
LinuxProcessList_readDelayAcctData(this, lp);
}
#endif #endif
if (settings->flags & PROCESS_FLAG_LINUX_CGROUP) { if (settings->flags & PROCESS_FLAG_LINUX_CGROUP) {
@ -1785,7 +1802,9 @@ static void scanCPUFreqencyFromCPUinfo(LinuxProcessList* this) {
continue; continue;
} else if ( } else if (
(sscanf(buffer, "cpu MHz : %lf", &frequency) == 1) || (sscanf(buffer, "cpu MHz : %lf", &frequency) == 1) ||
(sscanf(buffer, "cpu MHz: %lf", &frequency) == 1) (sscanf(buffer, "cpu MHz: %lf", &frequency) == 1) ||
(sscanf(buffer, "clock : %lfMHz", &frequency) == 1) ||
(sscanf(buffer, "clock: %lfMHz", &frequency) == 1)
) { ) {
if (cpuid < 0 || cpuid > (cpus - 1)) { if (cpuid < 0 || cpuid > (cpus - 1)) {
continue; continue;
@ -1824,41 +1843,6 @@ static void LinuxProcessList_scanCPUFrequency(LinuxProcessList* this) {
scanCPUFreqencyFromCPUinfo(this); scanCPUFreqencyFromCPUinfo(this);
} }
#ifdef HAVE_SENSORS_SENSORS_H
static void LinuxProcessList_scanCPUTemperature(LinuxProcessList* this) {
const int cpuCount = this->super.cpuCount;
for (int i = 0; i <= cpuCount; i++) {
this->cpus[i].temperature = NAN;
}
int r = LibSensors_getCPUTemperatures(this->cpus, cpuCount);
/* No temperature - nothing to do */
if (r <= 0)
return;
/* Only package temperature - copy to all cpus */
if (r == 1 && !isnan(this->cpus[0].temperature)) {
double packageTemp = this->cpus[0].temperature;
for (int i = 1; i <= cpuCount; i++) {
this->cpus[i].temperature = packageTemp;
}
return;
}
/* Half the temperatures, probably HT/SMT - copy to second half */
if (r >= 2 && (r - 1) == (cpuCount / 2)) {
for (int i = cpuCount / 2 + 1; i <= cpuCount; i++) {
this->cpus[i].temperature = this->cpus[i/2].temperature;
}
return;
}
}
#endif
void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
LinuxProcessList* this = (LinuxProcessList*) super; LinuxProcessList* this = (LinuxProcessList*) super;
const Settings* settings = super->settings; const Settings* settings = super->settings;
@ -1876,7 +1860,7 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
#ifdef HAVE_SENSORS_SENSORS_H #ifdef HAVE_SENSORS_SENSORS_H
if (settings->showCPUTemperature) if (settings->showCPUTemperature)
LinuxProcessList_scanCPUTemperature(this); LibSensors_getCPUTemperatures(this->cpus, this->super.cpuCount);
#endif #endif
// in pause mode only gather global data for meters (CPU/memory/...) // in pause mode only gather global data for meters (CPU/memory/...)

View File

@ -65,9 +65,7 @@ in the source distribution for its full text.
#endif #endif
ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, (int)M_SHARE, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 }; const ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, M_SHARE, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
int Platform_numberOfFields = LAST_PROCESSFIELD;
const SignalItem Platform_signals[] = { const SignalItem Platform_signals[] = {
{ .name = " 0 Cancel", .number = 0 }, { .name = " 0 Cancel", .number = 0 },

View File

@ -18,9 +18,7 @@ in the source distribution for its full text.
#include "ProcessLocksScreen.h" #include "ProcessLocksScreen.h"
#include "SignalsPanel.h" #include "SignalsPanel.h"
extern ProcessField Platform_defaultFields[]; extern const ProcessField Platform_defaultFields[];
extern int Platform_numberOfFields;
extern const SignalItem Platform_signals[]; extern const SignalItem Platform_signals[];

View File

@ -43,18 +43,22 @@ static void PressureStallMeter_updateValues(Meter* this, char* buffer, size_t le
} }
Platform_getPressureStall(file, some, &this->values[0], &this->values[1], &this->values[2]); Platform_getPressureStall(file, some, &this->values[0], &this->values[1], &this->values[2]);
xSnprintf(buffer, len, "%s %s %.2lf%% %.2lf%% %.2lf%%", some ? "some" : "full", file, this->values[0], this->values[1], this->values[2]);
/* only print bar for ten (not sixty and threehundred), cause the sum is meaningless */
this->curItems = 1;
xSnprintf(buffer, len, "%s %s %5.2lf%% %5.2lf%% %5.2lf%%", some ? "some" : "full", file, this->values[0], this->values[1], this->values[2]);
} }
static void PressureStallMeter_display(const Object* cast, RichString* out) { static void PressureStallMeter_display(const Object* cast, RichString* out) {
const Meter* this = (const Meter*)cast; const Meter* this = (const Meter*)cast;
char buffer[20]; char buffer[20];
xSnprintf(buffer, sizeof(buffer), "%.2lf%% ", this->values[0]); xSnprintf(buffer, sizeof(buffer), "%5.2lf%% ", this->values[0]);
RichString_write(out, CRT_colors[PRESSURE_STALL_TEN], buffer); RichString_writeAscii(out, CRT_colors[PRESSURE_STALL_TEN], buffer);
xSnprintf(buffer, sizeof(buffer), "%.2lf%% ", this->values[1]); xSnprintf(buffer, sizeof(buffer), "%5.2lf%% ", this->values[1]);
RichString_append(out, CRT_colors[PRESSURE_STALL_SIXTY], buffer); RichString_appendAscii(out, CRT_colors[PRESSURE_STALL_SIXTY], buffer);
xSnprintf(buffer, sizeof(buffer), "%.2lf%% ", this->values[2]); xSnprintf(buffer, sizeof(buffer), "%5.2lf%% ", this->values[2]);
RichString_append(out, CRT_colors[PRESSURE_STALL_THREEHUNDRED], buffer); RichString_appendAscii(out, CRT_colors[PRESSURE_STALL_THREEHUNDRED], buffer);
} }
const MeterClass PressureStallCPUSomeMeter_class = { const MeterClass PressureStallCPUSomeMeter_class = {

53
linux/ProcessField.h Normal file
View File

@ -0,0 +1,53 @@
#ifndef HEADER_LinuxProcessField
#define HEADER_LinuxProcessField
/*
htop - linux/ProcessField.h
(C) 2020 htop dev team
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#define PLATFORM_PROCESS_FIELDS \
CMINFLT = 11, \
CMAJFLT = 13, \
UTIME = 14, \
STIME = 15, \
CUTIME = 16, \
CSTIME = 17, \
M_SHARE = 41, \
M_TRS = 42, \
M_DRS = 43, \
M_LRS = 44, \
M_DT = 45, \
CTID = 100, \
VPID = 101, \
VXID = 102, \
RCHAR = 103, \
WCHAR = 104, \
SYSCR = 105, \
SYSCW = 106, \
RBYTES = 107, \
WBYTES = 108, \
CNCLWB = 109, \
IO_READ_RATE = 110, \
IO_WRITE_RATE = 111, \
IO_RATE = 112, \
CGROUP = 113, \
OOM = 114, \
IO_PRIORITY = 115, \
PERCENT_CPU_DELAY = 116, \
PERCENT_IO_DELAY = 117, \
PERCENT_SWAP_DELAY = 118, \
M_PSS = 119, \
M_SWAP = 120, \
M_PSSWP = 121, \
CTXT = 122, \
SECATTR = 123, \
PROC_COMM = 124, \
PROC_EXE = 125, \
CWD = 126, \
// End of list
#endif /* HEADER_LinuxProcessField */

View File

@ -35,7 +35,7 @@ static bool hasSELinuxMount(void) {
return false; return false;
} }
if (sfbuf.f_type != SELINUX_MAGIC) { if ((uint32_t)sfbuf.f_type != (uint32_t)SELINUX_MAGIC) {
return false; return false;
} }

View File

@ -267,9 +267,9 @@ static void SystemdMeter_display(ATTR_UNUSED const Object* cast, RichString* out
char buffer[16]; char buffer[16];
int color = (systemState && 0 == strcmp(systemState, "running")) ? METER_VALUE_OK : METER_VALUE_ERROR; int color = (systemState && 0 == strcmp(systemState, "running")) ? METER_VALUE_OK : METER_VALUE_ERROR;
RichString_write(out, CRT_colors[color], systemState ? systemState : "???"); RichString_writeAscii(out, CRT_colors[color], systemState ? systemState : "N/A");
RichString_append(out, CRT_colors[METER_TEXT], " ("); RichString_appendAscii(out, CRT_colors[METER_TEXT], " (");
if (nFailedUnits == INVALID_VALUE) { if (nFailedUnits == INVALID_VALUE) {
buffer[0] = '?'; buffer[0] = '?';
@ -277,9 +277,9 @@ static void SystemdMeter_display(ATTR_UNUSED const Object* cast, RichString* out
} else { } else {
xSnprintf(buffer, sizeof(buffer), "%u", nFailedUnits); xSnprintf(buffer, sizeof(buffer), "%u", nFailedUnits);
} }
RichString_append(out, zeroDigitColor(nFailedUnits), buffer); RichString_appendAscii(out, zeroDigitColor(nFailedUnits), buffer);
RichString_append(out, CRT_colors[METER_TEXT], "/"); RichString_appendAscii(out, CRT_colors[METER_TEXT], "/");
if (nNames == INVALID_VALUE) { if (nNames == INVALID_VALUE) {
buffer[0] = '?'; buffer[0] = '?';
@ -287,9 +287,9 @@ static void SystemdMeter_display(ATTR_UNUSED const Object* cast, RichString* out
} else { } else {
xSnprintf(buffer, sizeof(buffer), "%u", nNames); xSnprintf(buffer, sizeof(buffer), "%u", nNames);
} }
RichString_append(out, valueDigitColor(nNames), buffer); RichString_appendAscii(out, valueDigitColor(nNames), buffer);
RichString_append(out, CRT_colors[METER_TEXT], " failed) ("); RichString_appendAscii(out, CRT_colors[METER_TEXT], " failed) (");
if (nJobs == INVALID_VALUE) { if (nJobs == INVALID_VALUE) {
buffer[0] = '?'; buffer[0] = '?';
@ -297,9 +297,9 @@ static void SystemdMeter_display(ATTR_UNUSED const Object* cast, RichString* out
} else { } else {
xSnprintf(buffer, sizeof(buffer), "%u", nJobs); xSnprintf(buffer, sizeof(buffer), "%u", nJobs);
} }
RichString_append(out, zeroDigitColor(nJobs), buffer); RichString_appendAscii(out, zeroDigitColor(nJobs), buffer);
RichString_append(out, CRT_colors[METER_TEXT], "/"); RichString_appendAscii(out, CRT_colors[METER_TEXT], "/");
if (nInstalledJobs == INVALID_VALUE) { if (nInstalledJobs == INVALID_VALUE) {
buffer[0] = '?'; buffer[0] = '?';
@ -307,9 +307,9 @@ static void SystemdMeter_display(ATTR_UNUSED const Object* cast, RichString* out
} else { } else {
xSnprintf(buffer, sizeof(buffer), "%u", nInstalledJobs); xSnprintf(buffer, sizeof(buffer), "%u", nInstalledJobs);
} }
RichString_append(out, valueDigitColor(nInstalledJobs), buffer); RichString_appendAscii(out, valueDigitColor(nInstalledJobs), buffer);
RichString_append(out, CRT_colors[METER_TEXT], " jobs)"); RichString_appendAscii(out, CRT_colors[METER_TEXT], " jobs)");
} }
static const int SystemdMeter_attributes[] = { static const int SystemdMeter_attributes[] = {

View File

@ -37,17 +37,19 @@ static void ZramMeter_updateValues(Meter* this, char* buffer, size_t size) {
static void ZramMeter_display(const Object* cast, RichString* out) { static void ZramMeter_display(const Object* cast, RichString* out) {
char buffer[50]; char buffer[50];
const Meter* this = (const Meter*)cast; const Meter* this = (const Meter*)cast;
RichString_write(out, CRT_colors[METER_TEXT], ":");
Meter_humanUnit(buffer, this->total, sizeof(buffer));
RichString_append(out, CRT_colors[METER_VALUE], buffer); 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)); Meter_humanUnit(buffer, this->values[0], sizeof(buffer));
RichString_append(out, CRT_colors[METER_TEXT], " used:"); RichString_appendAscii(out, CRT_colors[METER_TEXT], " used:");
RichString_append(out, CRT_colors[METER_VALUE], buffer); RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer);
Meter_humanUnit(buffer, this->values[1], sizeof(buffer)); Meter_humanUnit(buffer, this->values[1], sizeof(buffer));
RichString_append(out, CRT_colors[METER_TEXT], " uncompressed:"); RichString_appendAscii(out, CRT_colors[METER_TEXT], " uncompressed:");
RichString_append(out, CRT_colors[METER_VALUE], buffer); RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer);
} }
const MeterClass ZramMeter_class = { const MeterClass ZramMeter_class = {

View File

@ -16,7 +16,7 @@ in the source distribution for its full text.
#include "XUtils.h" #include "XUtils.h"
ProcessFieldData Process_fields[] = { const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
[0] = { [0] = {
.name = "", .name = "",
.title = NULL, .title = NULL,
@ -25,9 +25,10 @@ ProcessFieldData Process_fields[] = {
}, },
[PID] = { [PID] = {
.name = "PID", .name = "PID",
.title = " PID ", .title = "PID",
.description = "Process/thread ID", .description = "Process/thread ID",
.flags = 0, .flags = 0,
.pidColumn = true,
}, },
[COMM] = { [COMM] = {
.name = "Command", .name = "Command",
@ -43,21 +44,24 @@ ProcessFieldData Process_fields[] = {
}, },
[PPID] = { [PPID] = {
.name = "PPID", .name = "PPID",
.title = " PPID ", .title = "PPID",
.description = "Parent process ID", .description = "Parent process ID",
.flags = 0, .flags = 0,
.pidColumn = true,
}, },
[PGRP] = { [PGRP] = {
.name = "PGRP", .name = "PGRP",
.title = " PGRP ", .title = "PGRP",
.description = "Process group ID", .description = "Process group ID",
.flags = 0, .flags = 0,
.pidColumn = true,
}, },
[SESSION] = { [SESSION] = {
.name = "SESSION", .name = "SESSION",
.title = " SESN ", .title = "SESN",
.description = "Process's session ID", .description = "Process's session ID",
.flags = 0, .flags = 0,
.pidColumn = true,
}, },
[TTY_NR] = { [TTY_NR] = {
.name = "TTY_NR", .name = "TTY_NR",
@ -67,9 +71,10 @@ ProcessFieldData Process_fields[] = {
}, },
[TPGID] = { [TPGID] = {
.name = "TPGID", .name = "TPGID",
.title = " TPGID ", .title = "TPGID",
.description = "Process ID of the fg process group of the controlling terminal", .description = "Process ID of the fg process group of the controlling terminal",
.flags = 0, .flags = 0,
.pidColumn = true,
}, },
[MINFLT] = { [MINFLT] = {
.name = "MINFLT", .name = "MINFLT",
@ -163,26 +168,11 @@ ProcessFieldData Process_fields[] = {
}, },
[TGID] = { [TGID] = {
.name = "TGID", .name = "TGID",
.title = " TGID ", .title = "TGID",
.description = "Thread group ID (i.e. process ID)", .description = "Thread group ID (i.e. process ID)",
.flags = 0, .flags = 0,
.pidColumn = true,
}, },
[LAST_PROCESSFIELD] = {
.name = "*** report bug! ***",
.title = NULL,
.description = NULL,
.flags = 0,
},
};
ProcessPidColumn Process_pidColumns[] = {
{ .id = PID, .label = "PID" },
{ .id = PPID, .label = "PPID" },
{ .id = TPGID, .label = "TPGID" },
{ .id = TGID, .label = "TGID" },
{ .id = PGRP, .label = "PGRP" },
{ .id = SESSION, .label = "SESN" },
{ .id = 0, .label = NULL },
}; };
Process* OpenBSDProcess_new(const Settings* settings) { Process* OpenBSDProcess_new(const Settings* settings) {
@ -209,28 +199,20 @@ static void OpenBSDProcess_writeField(const Process* this, RichString* str, Proc
Process_writeField(this, str, field); Process_writeField(this, str, field);
return; return;
} }
RichString_append(str, attr, buffer); RichString_appendWide(str, attr, buffer);
} }
static long OpenBSDProcess_compare(const void* v1, const void* v2) { static int OpenBSDProcess_compareByKey(const Process* v1, const Process* v2, ProcessField key) {
const OpenBSDProcess *p1, *p2; const OpenBSDProcess* p1 = (const OpenBSDProcess*)v1;
const Settings *settings = ((const Process*)v1)->settings; const OpenBSDProcess* p2 = (const OpenBSDProcess*)v2;
if (settings->direction == 1) {
p1 = (const OpenBSDProcess*)v1;
p2 = (const OpenBSDProcess*)v2;
} else {
p2 = (const OpenBSDProcess*)v1;
p1 = (const OpenBSDProcess*)v2;
}
// remove if actually used // remove if actually used
(void)p1; (void)p2; (void)p1; (void)p2;
switch (settings->sortKey) { switch (key) {
// add OpenBSD-specific fields here // add OpenBSD-specific fields here
default: default:
return Process_compare(v1, v2); return Process_compareByKey_Base(v1, v2, key);
} }
} }
@ -239,9 +221,10 @@ const ProcessClass OpenBSDProcess_class = {
.extends = Class(Process), .extends = Class(Process),
.display = Process_display, .display = Process_display,
.delete = Process_delete, .delete = Process_delete,
.compare = OpenBSDProcess_compare .compare = Process_compare
}, },
.writeField = OpenBSDProcess_writeField, .writeField = OpenBSDProcess_writeField,
.compareByKey = OpenBSDProcess_compareByKey
}; };
bool Process_isThread(const Process* this) { bool Process_isThread(const Process* this) {

View File

@ -15,11 +15,6 @@ in the source distribution for its full text.
#include "Settings.h" #include "Settings.h"
typedef enum OpenBSDProcessFields_ {
// Add platform-specific fields here, with ids >= 100
LAST_PROCESSFIELD = 100,
} OpenBSDProcessField;
typedef struct OpenBSDProcess_ { typedef struct OpenBSDProcess_ {
Process super; Process super;
} OpenBSDProcess; } OpenBSDProcess;
@ -30,9 +25,7 @@ typedef struct OpenBSDProcess_ {
extern const ProcessClass OpenBSDProcess_class; extern const ProcessClass OpenBSDProcess_class;
extern ProcessFieldData Process_fields[]; extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD];
extern ProcessPidColumn Process_pidColumns[];
Process* OpenBSDProcess_new(const Settings* settings); Process* OpenBSDProcess_new(const Settings* settings);

View File

@ -8,7 +8,6 @@ in the source distribution for its full text.
#include "OpenBSDProcessList.h" #include "OpenBSDProcessList.h"
#include <err.h>
#include <kvm.h> #include <kvm.h>
#include <limits.h> #include <limits.h>
#include <stdlib.h> #include <stdlib.h>
@ -34,6 +33,8 @@ in the source distribution for its full text.
static long fscale; static long fscale;
static int pageSize;
static int pageSizeKB;
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) { ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) {
const int mib[] = { CTL_HW, HW_NCPU }; const int mib[] = { CTL_HW, HW_NCPU };
@ -55,9 +56,13 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, ui
size = sizeof(fscale); size = sizeof(fscale);
if (sysctl(fmib, 2, &fscale, &size, NULL, 0) < 0) { if (sysctl(fmib, 2, &fscale, &size, NULL, 0) < 0) {
err(1, "fscale sysctl call failed"); CRT_fatalError("fscale sysctl call failed");
} }
if ((pageSize = sysconf(_SC_PAGESIZE)) == -1)
CRT_fatalError("pagesize sysconf call failed");
pageSizeKB = pageSize / ONE_K;
for (int i = 0; i <= pl->cpuCount; i++) { for (int i = 0; i <= pl->cpuCount; i++) {
CPUData* d = opl->cpus + i; CPUData* d = opl->cpus + i;
d->totalTime = 1; d->totalTime = 1;
@ -66,7 +71,7 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, ui
opl->kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf); opl->kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf);
if (opl->kd == NULL) { if (opl->kd == NULL) {
errx(1, "kvm_open: %s", errbuf); CRT_fatalError("kvm_openfiles() failed");
} }
return pl; return pl;
@ -91,11 +96,11 @@ static void OpenBSDProcessList_scanMemoryInfo(ProcessList* pl) {
size_t size_uvmexp = sizeof(uvmexp); size_t size_uvmexp = sizeof(uvmexp);
if (sysctl(uvmexp_mib, 2, &uvmexp, &size_uvmexp, NULL, 0) < 0) { if (sysctl(uvmexp_mib, 2, &uvmexp, &size_uvmexp, NULL, 0) < 0) {
err(1, "uvmexp sysctl call failed"); CRT_fatalError("uvmexp sysctl call failed");
} }
pl->totalMem = uvmexp.npages * CRT_pageSizeKB; pl->totalMem = uvmexp.npages * pageSizeKB;
pl->usedMem = (uvmexp.npages - uvmexp.free - uvmexp.paging) * CRT_pageSizeKB; pl->usedMem = (uvmexp.npages - uvmexp.free - uvmexp.paging) * pageSizeKB;
// Taken from OpenBSD systat/iostat.c, top/machine.c and uvm_sysctl(9) // Taken from OpenBSD systat/iostat.c, top/machine.c and uvm_sysctl(9)
const int bcache_mib[] = { CTL_VFS, VFS_GENERIC, VFS_BCACHESTAT }; const int bcache_mib[] = { CTL_VFS, VFS_GENERIC, VFS_BCACHESTAT };
@ -103,10 +108,10 @@ static void OpenBSDProcessList_scanMemoryInfo(ProcessList* pl) {
size_t size_bcstats = sizeof(bcstats); size_t size_bcstats = sizeof(bcstats);
if (sysctl(bcache_mib, 3, &bcstats, &size_bcstats, NULL, 0) < 0) { if (sysctl(bcache_mib, 3, &bcstats, &size_bcstats, NULL, 0) < 0) {
err(1, "cannot get vfs.bcachestat"); CRT_fatalError("cannot get vfs.bcachestat");
} }
pl->cachedMem = bcstats.numbufpages * CRT_pageSizeKB; pl->cachedMem = bcstats.numbufpages * pageSizeKB;
/* /*
* Copyright (c) 1994 Thorsten Lockert <tholo@sigmasoft.com> * Copyright (c) 1994 Thorsten Lockert <tholo@sigmasoft.com>
@ -222,9 +227,9 @@ static void OpenBSDProcessList_scanProcs(OpenBSDProcessList* this) {
} }
} }
proc->m_virt = kproc->p_vm_dsize; proc->m_virt = kproc->p_vm_dsize * pageSizeKB;
proc->m_resident = kproc->p_vm_rssize; proc->m_resident = kproc->p_vm_rssize * pageSizeKB;
proc->percent_mem = (proc->m_resident * CRT_pageSizeKB) / (double)(this->super.totalMem) * 100.0; proc->percent_mem = proc->m_resident / (double)(this->super.totalMem) * 100.0;
proc->percent_cpu = CLAMP(getpcpu(kproc), 0.0, this->super.cpuCount * 100.0); proc->percent_cpu = CLAMP(getpcpu(kproc), 0.0, this->super.cpuCount * 100.0);
//proc->nlwp = kproc->p_numthreads; //proc->nlwp = kproc->p_numthreads;
proc->nice = kproc->p_nice - 20; proc->nice = kproc->p_nice - 20;

View File

@ -42,9 +42,7 @@ in the source distribution for its full text.
#include "XUtils.h" #include "XUtils.h"
ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 }; const ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
int Platform_numberOfFields = LAST_PROCESSFIELD;
/* /*
* See /usr/include/sys/signal.h * See /usr/include/sys/signal.h

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