687 Commits
1.0.3 ... 2.2.0

Author SHA1 Message Date
04cc193e3c Bump version to 2.2.0 2018-04-10 10:42:59 -03:00
5b38466eab Update ChangeLog 2018-04-10 10:42:46 -03:00
34dff098c5 Add tree view flag to man page (#777) 2018-04-10 10:36:34 -03:00
e85b072ad0 Require pkg-config as an optional build dependency on Linux (#775)
With this commit:

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

The end result is:

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

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

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

This changes is python2 and python3 compatible

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

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

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

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

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

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

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

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

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

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

Summary of additions:

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

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

Fixes #663. Supersedes pull request #729.

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

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

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

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

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

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

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

Thus, explicitly recognize the idle process state, and sort it below
others.
2018-02-04 16:44:21 +01:00
b27712181a Darwin: disable thread reading due to bug in macOS High Sierra 2018-02-04 08:59:29 +01:00
ad99187680 htop scans /proc to determine how many running tasks exist. Since that
operation is not possible to be conducted in an atomic fashion, task
scheduling effects can lead to a count greater than the number of actual
processors; this is more easily noticed on machines with several CPUs
and under heavy workload.
This patch simply adds an upper bound on cpuCount to guarantee
consistent reports of the number of running tasks at any given time.
2018-01-23 14:11:47 -02:00
b7b66b76a5 Adds support for linux delay accounting (#667)
Adds support for showing columns with linux delay accounting.

This information can be read from the netlink interface, and thus we set up a socket to read from that when initializing the LinuxProcessList (LinuxProcessList_initNetlinkSocket). After that, for each process we call LinuxProcessList_readDelayAcctData, which sends a message thru the socket after setting up a callback to get the answer from the Kernel. That callback sets the process total delay time attribute. We then set the delay percent as the percentage of time process cpu time since last scan.
2017-12-04 00:15:29 -02:00
52831955c7 fix fallthough comments for GCC 7.x
GCC 7.x does some extended checks on fallthough for switch/case
statement. The warning looks like this:

warning: this statement may fall through [-Wimplicit-fallthrough=]

It can be told about implicit fallthough, however it does not
recognize comments within blocks, so move the comments outside.
2017-10-23 14:27:33 -04:00
ef34a83c54 Merge pull request #678 from evelikov/remove-libtool
Remove libtool references
2017-10-13 13:55:43 -07:00
4c1230b03b Remove libtool references
The project builds a single standalone binary.

There are no libraries created - be that static or shared ones.
Thus there's no need for libtool.

Signed-off-by: Emil Velikov <emil.l.velikov@gmail.com>
2017-09-22 16:53:26 +01:00
e3f65c8ec2 Make 'c' key work with threads as well. 2017-09-14 17:10:39 -03:00
b9934ffa08 minor style tweak 2017-09-04 13:53:03 -03:00
65ec0bd6a6 Merge pull request #676 from wangqr/master
Find roots when constructing process tree, fix #587
2017-09-01 19:56:55 -03:00
584a9bceab Find roots when constructing process tree, fix #587 2017-09-01 21:34:05 +08:00
9487bda330 Do not use xSnprintf when the result is used. Fixes #662. 2017-08-01 15:48:43 -07:00
4300a19592 Merge pull request #659 from guoci/patch-1
make script version agnostic
2017-07-28 21:07:54 -03:00
60acda0bce make script version agnostic 2017-07-27 17:02:01 -04:00
09e241fb12 Security review: check results of snprintf.
Calls marked with xSnprintf shouldn't fail.
Abort program cleanly if any of them does.
2017-07-27 16:07:50 -03:00
3975e9ce5c Merge branch 'master' of https://github.com/hishamhm/htop 2017-07-26 16:16:10 -03:00
821d50f0b4 Merge pull request #651 from Explorer09/graph-mode-draw
Round values in graph drawing (instead of implicit truncate)
2017-07-26 16:15:59 -03:00
543d65c6ab Security review: make privilege dropping-restoring optional.
This is/was necessary only on macOS, because you needed root in order
to read the process list. This was never necessary on Linux, and
it also raises security concerns, so now it needs to be enabled
explicitly at build time.
2017-07-26 15:40:55 -03:00
f205f7004c Use regular readdir since readdir_r is deprecated and newer GCC complains. 2017-07-26 15:35:39 -03:00
ef7817c17a Merge pull request #656 from rsaxvc/master
more const usage
2017-07-26 11:24:51 -03:00
33fda93271 Merge pull request #657 from kalbasit/remove_whitespace_from_config_file
Settings: do not emit trailing whitespace
2017-07-25 13:57:55 -03:00
ce0cf3c457 Settings: do not emit trailing whitespace 2017-07-24 16:36:27 -07:00
d5faf64374 Mark some things as const
Several string pointer arrays pointed to const strings
but were not const themselves.

A few various structures and arrays were also marked const.
2017-07-22 22:34:30 -05:00
18b3e5d255 .gitignore vim & nano swp files 2017-07-22 22:34:30 -05:00
143a7de6b2 Round values in graph drawing (instead of implicit truncate) 2017-07-15 22:14:44 +08:00
e940aecfb9 Add "no perm" status when other fields fail due to lack of permission.
Thanks @Sworddragon for the heads up.
See #88.
2017-07-10 20:57:34 -03:00
1a178ad581 Merge branch 'master' of https://github.com/hishamhm/htop 2017-07-10 20:14:53 -03:00
c39f18a7be Add a clear warning about unsupported platforms.
Closes #648.
2017-07-10 20:14:25 -03:00
978c9e1698 Merge pull request #647 from Explorer09/dragonfly-pid-max
DragonFlyBSD PID_MAX is 999999.
2017-07-10 10:19:26 -03:00
e70f447d54 DragonFlyBSD PID_MAX is 999999.
See DragonFlyBSD source "sys/sys/proc.h".

Fixes #646
2017-07-07 20:38:04 +08:00
71785e2ded Set idle I/O prio to 0x6007, like ionice.
As suggested by @wolfgang42 in #100.
2017-07-05 15:20:48 -03:00
e9ecbd05bc Use class value only to display idle I/O priority.
As suggested by @wolfgang42. Fixes #100.
2017-07-05 15:18:02 -03:00
89d15399c5 Merge branch 'master' into fixedgray 2017-06-06 15:27:02 -03:00
e03e45d819 Merge pull request #624 from mklein-de/xterm-256color
make special keys also work with TERM=xterm-256color
2017-06-06 15:23:58 -03:00
45f3769887 make special keys also work with TERM=xterm-256color 2017-06-01 12:52:14 +02:00
2d8dd0b29e Work around the strange behavior of gray. 2017-05-23 14:02:34 -03:00
b1028e03c5 Merge pull request #414 from Explorer09/meters-panel-edit
Mouse-friendly functions bar for meters panel
2017-04-24 23:48:13 -03:00
0c168471b6 F6 is actually used for both actions, depending on context. 2017-04-24 23:38:28 -03:00
68b8efb83f Merge pull request #612 from ryenus/keymap
update keymap in help
2017-04-24 23:35:38 -03:00
418fea33f4 Merge pull request #613 from dkgroot/master
Initial addition of dragonflybsd (based on FreeBSD)
2017-04-24 23:34:27 -03:00
975e22144e Merge pull request #614 from dkgroot/EnhanceMakeHeader
Enh: scripts/MakeHeader script
2017-04-24 23:30:42 -03:00
50f03f5950 Enh: scripts/MakeHeader script
Only write a new .h file if something changed for the header file being created.
This prevents a lot of recompilation during development
2017-04-21 16:39:03 +02:00
cb7a06379d Enh: Add support for backtrace using execinfo 2017-04-21 16:36:19 +02:00
cba695961a Fix: TreeView 2017-04-21 16:34:40 +02:00
49af12e7c6 implement: readJailName
Note: dragonflybsd does not have 'jail_get' like freebsd does.
It does however provide a sysctl "jail.list" which returns a list of all jails.
2017-04-20 15:14:33 +02:00
b258d6e53e Initial addition of dragonflybsd (based on FreeBSD) 2017-04-19 16:19:32 +02:00
e0e84401e4 update keymap in help 2017-04-19 18:20:42 +08:00
5570748dd2 Merge pull request #610 from coypoop/patch-1
Add missing include
2017-03-28 23:56:13 -03:00
3c09082422 Merge pull request #608 from giwhub/giwhub-patch-1
Add Comment[zh_CN] and GenericName[zh_CN] into htop.desktop
2017-03-28 23:24:03 -03:00
53d7c66ac3 Add missing include
sys/time.h for struct timeval
2017-03-25 14:12:26 +03:00
3b1260ac3d Add Comment[zh_CN] and GenericName[zh_CN] into htop.desktop 2017-03-24 01:45:10 +08:00
fa30938247 Merge branch 'master' of https://github.com/hishamhm/htop 2017-02-15 22:49:13 -02:00
bb8dec1582 Cap battery at 100%.
Apparently invalid results can be returned by buggy drivers in old laptops,
as reported by @thukydides. See #596.
2017-02-15 22:47:03 -02:00
aa813c7561 Merge pull request #593 from kamyarrasta/issue/502sessionid
Issue #502 update Session ID column
2017-02-04 21:46:29 -02:00
3f6d1262c0 Issue #502 fix SID colunm header width 2017-02-05 00:10:29 +01:00
84bc00a275 Issue #502 update Session ID column 2017-02-01 00:03:55 +01:00
6141edc74b Merge pull request #584 from EliteTK/buffer-lengths
Replace all uses of sprintf with snprintf
2016-12-30 19:28:38 -02:00
2b5c1b4b13 Replace all uses of sprintf with snprintf
In all the cases where sprintf was being used within htop, snprintf
could have been used. This patch replaces all uses of sprintf with
snprintf which makes sure that if a buffer is too small to hold the
resulting string, the string is simply cut short instead of causing
a buffer overflow which leads to undefined behaviour.

`sizeof(variable)` was used in these cases, as opposed to `sizeof
variable` which is my personal preference because `sizeof(variable)`
was already used in one way or another in other parts of the code.
2016-12-29 19:42:41 +01:00
8af4d9f453 Interpret TTY_NR column on Linux,
translate dev_t to major:minor on other platforms.
Closes #316.
2016-10-01 03:09:04 -03:00
8a147dd5b4 Merge branch 'master' of https://github.com/hishamhm/htop 2016-09-16 13:37:44 -03:00
5df425867c Protect against strange values of SIGRTMIN and SIGRTMAX,
and change alignment of numbers.
2016-09-16 13:37:07 -03:00
46d8f2eef2 Merge pull request #551 from Explorer09/rt-signals
Real-time signals support (kill command)
2016-09-16 13:36:58 -03:00
8fd6228bc1 Merge pull request #551 from Explorer09/rt-signals
Real-time signals support (kill command)
2016-09-16 13:03:46 -03:00
0ce4835f95 Real-time signals support (for kill command)
SignalsPanel_new now fetches SIGRTMIN and SIGRTMAX and generates real-
time signals entries at runtime.

All signals between SIGRTMIN and SIGRTMAX are written in "SIGRTMIN+n"
notation, per discussion in pull request #551.

Signed-off-by: Kang-Che Sung <explorer09 @ gmail.com>
2016-09-14 21:47:24 +08:00
823481ae22 Merge branch 'master' of https://github.com/hishamhm/htop 2016-09-11 23:29:21 -03:00
96157870af Downgrade requirement to Libtool 2.4.0.
Courtesy to OpenWRT environments as requested by @champtar in #540.
2016-09-11 23:28:17 -03:00
5a5a7c8d6b Merge pull request #555 from ivan/master
Fix column misalignment for priority -101 threads
2016-09-06 15:08:26 -03:00
07086fcf77 Fix column misalignment for priority -101 threads
BFS-patched kernels can have kernel threads with priority -101.
This change makes priority -101 display as "RT", just like priority -100.

Related: https://github.com/hishamhm/htop/issues/314
2016-09-06 09:22:38 +00:00
6d4df3d138 Merge branch 'master' of https://github.com/hishamhm/htop 2016-08-30 12:38:12 -03:00
d4a8023b72 Fix typos, by @Gelma.
Closes #546.
2016-08-30 12:37:31 -03:00
99f7ff9bd2 Merge pull request #550 from Explorer09/signals-tweaks
Mark signal tables 'const'
2016-08-30 12:17:20 -03:00
1f3d85b617 Mark signal tables 'const'
Specifically, Platform_signals[] and Platform_numberOfSignals. Both are
not supposed to be mutable. Marking them 'const' puts them into rodata
sections in binary. And for Platform_numberOfSignals, this aids
optimization (aids only Link Time Optimization for now). :)

Signed-off-by: Kang-Che Sung <explorer09@gmail.com>
2016-08-30 20:41:17 +08:00
cceab15b9d free(NULL) is a valid no-op, so let's make String_freeArray(NULL) valid too. 2016-08-24 18:12:35 -03:00
bd5d37f297 Return when reading cmdline fails (e.g. zombie process) 2016-08-24 18:11:10 -03:00
16406ea330 Point users to the official release tarballs. 2016-08-23 15:43:27 -03:00
d3f575264b Merge pull request #537 from Explorer09/string-utils
Optimize Strings_startWith()
2016-08-11 00:44:42 -03:00
bf35921abb Optimize Strings_startWith()
Use strncmp() combined with a strlen() will give better performance
than a strstr in worst case. Especially when the match prefix is a
constant and not a variable.

While we are at it, replace the match() function in linux/Battery.c,
which uses a naive algorithm, with a macro that does better job by
utilizing Strings_startWith().

    $ gcc --version | head -n 1
    gcc (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4
    $ uname -m
    x86_64
    $ size htop.old htop.new
       text   data    bss    dec    hex filename
     137929  15112   3776 156817  26491 htop.old
     137784  15104   3776 156664  263f8 htop.new

Signed-off-by: Kang-Che Sung <explorer09 @ gmail.com>
2016-08-11 10:49:35 +08:00
b269eb24b0 Merge pull request #534 from Explorer09/issue-532
Change scrolling behavior to make it more similar to other applications.
2016-08-09 13:06:33 -03:00
3d9868833e Fix scrolling behavior change caused by 759caf0f
Commit "Make PgDown behavior more usual." 759caf0f8f
silently changes the PageDown scrolling behavior that, instead of
scrolling one window down until the end of the window touches the end
of the list, the window simply repositions itself in a way that the
selected item always become the last item in the new window.

The commit reverts the behavior, and also fixes sanity conditionals
so that the scrollV variable will _never_ become negative or out-of-
bound.

Fixes issue #532. Also keep the problem #480 addressed.

Signed-off-by: Kang-Che Sung <explorer09 @ gmail.com>
2016-08-07 07:49:37 +08:00
3cd0339423 Merge branch 'master' of https://github.com/hishamhm/htop 2016-07-28 11:39:15 -03:00
ef879b4a22 'Follow' only if element is found in search 2016-07-28 11:37:44 -03:00
f161365d72 Merge pull request #526 from Explorer09/bar-tweaks
Let BarMeterMode_characters[] be const array.
2016-07-23 13:48:50 -03:00
6b9b6db655 Let BarMeterMode_characters[] be const array. 2016-07-22 14:58:29 +08:00
f80e577c59 Changelog for htop 2.0.2 2016-07-21 17:12:45 -03:00
81552d4ab5 Preparing release 2.0.2, a minor bugfix release. 2016-07-21 16:54:41 -03:00
0108117d0b Mouse-friendly functions bar for meters panel
Before:
SpaceStyle EnterMove  DelDeleteEscDone  |
~~~~~      ~~~~~      ~~~      ~~~      |
UpUp     DnDown  LtLeft  RtRight EnterConfirmDelDeleteEscDone  |
~~       ~~      ~~      ~~      ~~~~~       ~~~      ~~~      |

After:
SpaceStyle EnterMove                                           DelDeleteF10Done
~~~~~      ~~~~~      ~~                                       ~~~      ~~~
SpaceStyle EnterLock  UpUp    DnDown  <-Left  ->Right          DelDeleteF10Done
~~~~~      ~~~~~      ~~      ~~      ~~      ~~      ~~       ~~~      ~~~

* Align 'Delete' and 'Done' to the right to match functions on other
  screens. (Accidental clicking is avoided as a side benefit.)
* You could change meter type while in moving mode. New bar now hints
  this.
* Two Enter key functions are put in the same place and so mouse clicks
  there act like functions toggle. (The wording change to 'Lock' is
  also to reflect this.)
* '<-' and '->' instead of 'Lt' and 'Rt' abbreviation as the latter is
  not widely seen and arrows shapes are obvious. :)
* 'Esc' key for 'Done' in this context may not be intuitive, comparing
  to 'F10'. While I wish there be a Cancel/Undo function for 'Esc', it
  wouldn't hurt if we write 'F10' for 'Done' on functions bar for now.
2016-07-16 21:13:46 +08:00
a41c9b3925 Reword 'Type' to 'Style' for meters.
Per @hishamhm's suggestion. This is UI change only.
In code the class names and symbols still refer them as "modes".
2016-07-16 21:13:46 +08:00
265d04821a Merge pull request #524 from ricardo0y/cross_compile_with_ncurses_config
Allow to override ncurses*-config path
2016-07-12 10:02:51 -03:00
78b82d0fdc Allow to override ncurses*-config path
This will be used when cross-compiling with ncurses*-config generated for the
target, using constructs like
HTOP_NCURSES_CONFIG_SCRIPT=/path/to/ncurses5-config

Signed-off-by: Ricardo Martincoski <ricardo.martincoski@gmail.com>
2016-07-11 20:17:13 -03:00
7f9c82f28d Refresh whole panel on Ctrl+L in infoscreen.
Closes #520.
2016-06-23 13:25:58 -03:00
7d5ef246f1 Let's keep it simple then! 2016-06-20 12:11:49 -03:00
0fa03322a9 Dynamically adjust the size of line reads
* Dynamically adjust the size of line reads.
* Remove some more uses of fgets with arbitrary sizes.
* Fix reading of lines and width of n column.

Fixes #514.
2016-06-19 18:55:35 -03:00
52f814481c While we're at it, get rid of another sprintf. 2016-06-19 18:30:20 -03:00
b139671cbd Moving left and right needs a full redraw. 2016-06-19 18:05:18 -03:00
adcc944e35 Use "-" as the Ctrl and Alt joiner. 2016-06-17 22:14:07 -03:00
a5ef374bb8 Merge pull request #511 from Explorer09/man-page
Document Alt+{h,j,k,l} and Ctrl+{A,E} into man page
2016-06-17 11:50:15 -03:00
6dd32d3604 Document Alt+{h,j,k,l} and Ctrl+{A,E} into man page
Rewrite the scrolling part in the man page so that each key become clearer on
what it does. Also officially document the Alt+{h,j,k,l} key alternatives and
Ctrl+A, Ctrl+E, '^', '$' keys (see issue #508).
2016-06-17 10:19:19 +08:00
8cd045cc79 Merge branch 'master' of https://github.com/hishamhm/htop 2016-06-15 12:46:34 -03:00
0128d222b9 Added Ctrl+A and Ctrl+E to go to beginning and end of line.
(Also, '^' and '$')
Closes #508.
2016-06-15 12:45:23 -03:00
1a13b4d0f4 Don't store invisible trailing whitespace 2016-06-15 12:41:50 -03:00
9ec41d1b65 Merge pull request #505 from Explorer09/xStrdup
Fix xStrdup debug build failure & allow Clang to use ((nonnull))
2016-06-04 11:45:29 -03:00
c0e37bc5f5 Fix xStrdup debug build failure & allow Clang to use ((nonnull)) 2016-06-03 10:14:27 +08:00
f9c1db514d Merge pull request #504 from Explorer09/xStrdup
Fixes upon xStrdup(NULL) problem
2016-06-02 17:29:14 -03:00
3297616efa Add assert and __attribute__((nonnull)) on xStrdup
__attribute__((nonnull)) will help catching "calling with NULL" mistake on
compile time.

I also convert xStrdup into a macro, that will do assert() inline when the
code is *not* built with -DNDEBUG . For release builds (with -DNDEBUG),
preprocessor trick will ensure that generated code remains the same.
2016-06-02 20:38:24 +08:00
e288f690af Don't check if (!str) in xStrdup
This effectively reverts "Stricter strdup." 4674b4a732

If str is NULL upon the calling of strdup(), it should crash with SIGSEGV.
Just let it crash. Adding the "if (!str) fail();" code serves nothing but
bloat.
2016-06-02 18:30:18 +08:00
ea9dc4ab74 Merge branch 'master' of https://github.com/hishamhm/htop 2016-05-30 15:06:52 -03:00
112db9a609 Handle SIGQUIT. Closes #503. 2016-05-30 15:06:22 -03:00
5db1b0e9a0 Remove extra checks made redundant by XAlloc. 2016-05-30 12:22:33 -03:00
4674b4a732 Stricter strdup. 2016-05-30 12:22:22 -03:00
7ededce9b5 Silence cast warning. 2016-05-30 12:22:07 -03:00
3126cfce6c Merge pull request #500 from Explorer09/meter-tweaks
Remove redundant is-null checks on free(Meter.drawData)
2016-05-27 16:30:45 -03:00
313b3d3752 Remove redundant is-null checks on free(Meter.drawData) 2016-05-27 17:11:54 +08:00
fa0c637c55 Silence warnings about seteuid return value.
Closes #483.
2016-05-25 21:37:07 -03:00
b7ac416634 Fixes #498. 2016-05-25 21:29:06 -03:00
6cc0a8c820 Make sure a pointer fits in the argument! 2016-05-25 16:46:47 -03:00
645057d81a Use set_escdelay() to avoid problems with ESCDELAY as a macro. 2016-05-19 16:09:47 -03:00
6c1be63291 Fail early if libtool is not present. 2016-05-19 16:04:04 -03:00
642ccb49ca Merge pull request #445 from Explorer09/configure-ac
configure.ac fixes
2016-05-19 15:57:31 -03:00
6028e1b4c4 Merge pull request #496 from tcreech/lwp_hack
FreeBSD: fix multithreaded CPU% in process list
2016-05-19 15:44:57 -03:00
95d1984339 Merge pull request #485 from Cubox-/master
Fix FreeBSD CPU bars calculation
2016-05-16 19:19:36 -03:00
c0df404701 Update INSTALL text from autoconf-2.69 2016-05-08 14:35:20 +08:00
b71b07f5e0 Reorder configure macros to avoid "missing script" warning.
3 effects in this commit, with the first being the main one:

1. Fix the "`missing' script is too old or missing" warning. See:
   <https://lists.gnu.org/archive/html/automake/2010-08/msg00108.html>

2. By moving AC_CANONICAL_TARGET down in order, we are now able to
   set the directory for auxiliary scripts. For now it's still './'.
   I added the line "AC_CONFIG_AUX_DIR([.])" to show that the directory
   change is possible.

3. AC_USE_SYSTEM_EXTENSIONS includes checks from AC_PROG_CC, by moving
   the former macro down, we can save size in 'configure' by not
   generating repeated checks.
2016-05-08 14:35:20 +08:00
f0df28a470 Replace deprecated autoconf macros.
AC_HELP_STRING -> AS_HELP_STRING
AC_TRY_COMPILE -> AC_COMPILE_IFELSE([AC_LANG_PROGRAM([...])],...)
AC_CONFIG_HEADER -> AC_CONFIG_HEADERS
AC_PROG_LIBTOOL -> LT_INIT

Note: There might be more deprecated macros that I haven't noticed.
I just wish to avoid painful highlighting from my text editor (gedit)
that complains about them. :)
2016-05-08 14:35:20 +08:00
7d72715a6b Merge pull request #490 from Explorer09/macro-fixes
Fix macro Header_forEachColumn
2016-05-07 14:34:50 -03:00
54621e8b8f Fix macro Header_forEachColumn
The (this_) token was not expanded properly, but the bug was not caught
because all uses of this macro specifies (this_)=this .

Also parenthesize macro tokens to prevent further problems.
2016-05-07 14:57:51 +08:00
572546f806 Auto-follow process after a search.
See #237.
2016-05-05 10:30:06 -03:00
d464be13db Merge branch 'master' of https://github.com/hishamhm/htop 2016-05-04 22:43:03 -03:00
759caf0f8f Make PgDown behavior more usual.
Closes #480.
2016-05-04 22:41:52 -03:00
cdc91b0b33 Merge pull request #472 from jrtc27/hurd
Use Linux backend on the Hurd
2016-05-04 15:42:16 -03:00
1754a1cd48 Merge pull request #473 from fasterit/upstream/typo1
Fix a typo in the man page (Debian Lintian spelling-error-in-manpage)
2016-05-04 15:40:31 -03:00
8e6ffdb0ab Merge pull request #475 from fasterit/upstream/desktop-keywords
Add Keywords entry to .desktop file (Debian Lintian warning desktop-e…
2016-05-04 15:40:16 -03:00
c37be409a9 Improve reproducible builds.
Use a SOURCE_DATA_EPOCH friendly date.
Suggested by @fasterit in #476.
2016-05-04 15:34:49 -03:00
fa1b5d1e2e Fix a small undefined behavior detected by libubsan. 2016-05-04 15:34:22 -03:00
19f0f4db27 Merge pull request #488 from Explorer09/func-naming
Rename Meter.setValues() functions to updateValues()
2016-05-04 15:18:52 -03:00
9dea20e068 Rename Meter.setValues() functions to updateValues()
Rationale (copied from htop issue #471):
The function name "setValues" is misleading. For most OOP (object-
oriented programming) contexts, setXXX functions mean they will change
some member variables of an object into something specified in
function arguments. But in the *Meter_setValues() case, the new values
are not from the arguments, but from a hard-coded source. The caller
is not supposed to change the values[] to anything it likes, but
rather to "update" the values from the source. Hence, updateValues is
a better name for this family of functions.
2016-05-04 13:39:26 +08:00
2ea4bee66d Merge pull request #486 from mmcco/null-check
Remove needless allocation error conditions
2016-04-29 23:01:32 -03:00
1809f4be98 Remove needless allocation error conditions
These allocations were converted to use xMalloc et al. and no longer
need error checks.
2016-04-29 21:10:05 -04:00
5776cb56b4 Revert "Fix FreeBSD CPU% calculation"
This reverts commit f554f08fa9.
2016-04-28 21:42:18 +02:00
74f8b31040 Add Keywords entry to .desktop file (Debian Lintian warning desktop-entry-lacks-keywords-entry)
Debian patch from
https://anonscm.debian.org/cgit/collab-maint/htop.git/tree/debian/patches/002-lintian-warning-fix-desktop-keywords.patch
2016-04-19 19:06:03 +02:00
4d3f483b96 Fix a typo in the man page (Debian Lintian spelling-error-in-manpage)
Debian patch from
https://anonscm.debian.org/cgit/collab-maint/htop.git/tree/debian/patches/001-lintian-warning-fix-man-typo.patch
2016-04-19 18:32:39 +02:00
2de52862a6 Use Linux backend on the Hurd 2016-04-18 23:57:30 +01:00
a9508275cc Use $target_os instead of $target in configure.ac 2016-04-18 23:55:55 +01:00
306c5443ae Update header. 2016-03-31 11:01:23 -03:00
d15555ed2c Merge branch 'master' of https://github.com/hishamhm/htop 2016-03-31 00:19:59 -03:00
d64f2bdd56 If task_for_pid fails, stop trying. 2016-03-31 00:18:42 -03:00
6c2f698a47 Merge pull request #458 from Explorer09/bar-tweaks
BarMeterMode_draw() minor code improvement
2016-03-25 01:57:10 -03:00
7b3c8bc77a BarMeterMode_draw minor code improvement
Removed a loop that sets the bar[] buffer with spaces and merged that
task to the snprintf() call just below. No need for the barOffset
variable. Display behavior is unchanged.

Size comparision (when compiled on Ubuntu 14.04 64-bit):

    $ size htop.old htop.new
       text    data     bss     dec     hex filename
     137312   15112    3776  156200   26228 htop.old
     137216   15112    3776  156104   261c8 htop.new
2016-03-22 14:52:31 +08:00
3a4c0fa2d6 Merge pull request #452 from Explorer09/cpu-meter-tweaks
Assert (Platform_meterTypes[0] == &CPUMeter_class)
2016-03-20 05:02:07 -03:00
328de35623 Assert (Platform_meterTypes[0]==&CPUMeter_class)
Just assume Platform_meterTypes[0] is always &CPUMeter_class for every
platform. This removes a conditional in AvailableMetersPanel_new().

Also adds some comments about the logic here. Without assuming
Platform_meterTypes[0], the (int i=1) clause in this for loop will not
make sense. (I.e. Why not (int i=0)? )

Also replaced a sprintf() call with safer snprintf() in code further
below.
2016-03-19 15:01:13 +08:00
c8a735e471 Merge pull request #444 from Explorer09/meter-maxitems
Explicit "maxItems" property of meters
2016-03-13 20:57:08 -03:00
99fb3070a2 Explicit "maxItems" property of meters
Two changes in this commit:
- All meters now explicitly specify "maxItems" property, even for just
  1 item. (Exception is "container" CPU meter classes, which use
  CUSTOM_METERMODE.)
- "maxItems" being 0 is now allowed. This will let bar meters and graph
  meters render an empty meter.
2016-03-11 10:54:34 +08:00
7d3f67e822 Revert 5c593fae42 (xCalloc)
calloc() allows 'nmemb' or 'size' to be zero, in which case NULL may be
returned. Letting htop die because of either argument being zero doesn't
make sense.

As a side note: As size_t is unsigned, compiler should be able to optimize
conditional (nmemb > 0 && size > 0) to (nmemb && size). This theorically
shouldn't increase code size too much.
2016-03-11 10:43:31 +08:00
3283c6d23c Merge pull request #441 from Explorer09/issue-438
Avoid overlapping key values defined by curses (Real fix).
2016-03-09 02:03:11 -03:00
8a928c8b89 Avoid overlapping key values defined by curses (Real fix).
Real fix for issue #438.
2016-03-09 10:16:34 +08:00
f295a52ed9 Avoid overlapping key values defined by curses.
Closes #438.
2016-03-08 12:23:18 -03:00
e2ccc7b240 Prepare for release 2.0.1. 2016-03-07 18:32:06 -03:00
ef1e62d1fa Merge branch 'juanfra684-openbsd-mem-used' 2016-03-07 15:03:50 -03:00
453105e77a Merge branch 'Sp1l-master' 2016-03-07 16:58:47 -03:00
db80f202f2 Avoid global, as done by @gaod in #387. 2016-03-07 16:58:02 -03:00
98e43816a5 Merge branch 'master' of https://github.com/Sp1l/htop into Sp1l-master 2016-03-07 16:54:38 -03:00
694addceb5 Merge pull request #436 from mmcco/tuneup
Misc. OpenBSD tuneup and improvement
2016-03-07 16:38:39 -03:00
4623394456 Merge pull request #435 from mmcco/freebsd
Improve error reporting on FreeBSD libkvm call
2016-03-07 16:35:59 -03:00
4ad7aa6432 Merge branch 'openbsd-mem-used' of https://github.com/juanfra684/htop into juanfra684-openbsd-mem-used 2016-03-07 15:03:18 -03:00
4b780a3499 A few more OpenBSD fixes
Namely:

 o use malloc where an xCalloc slipped in

 o safeguard against an empty arg list - I don't think it's possible,
   but it would be potentially exploitable

 o we need to initialize the arg string to an empty string because we no
 longer use strlcpy(3)

 o annotate a tricky use of strlcpy(3)'s truncation
2016-03-05 23:38:12 -05:00
b08cb7352e Misc. OpenBSD tuneup and improvement
Including:

 o set *basenameEnd even in error cases (FreeBSD probably needs this)

 o use kvm_openfiles(3) rather than kvm_open(3) so that we can report
   errors (as with FreeBSD)

 o sanify the process argument list creation by using strlcat(3)

 o drop the pageSizeKb variable and use the PAGE_SIZE_KB macro directly,
   as the page size can't change anyway

 o clean up a few macros, add MINIMUM() and MAXIMUM() (should be
   mirrored to FreeBSD)

 o fix some syntax

 o add some useful comments
2016-03-05 23:23:29 -05:00
b886ecc479 Improve error reporting on FreeBSD libkvm call
This involves switching from kvm_open(3) to kvm_openfiles(3). The only
difference is that the latter has saner error reporting (see the man
page for details). We can now fatally report the error rather than just
calling assert(3).
2016-03-05 22:59:39 -05:00
63c55854e5 Merge pull request #388 from mhinz/add-p-to-help
Add "p" to Help
2016-02-29 22:03:45 -03:00
4f1bd232d8 Merge branch 'master' of https://github.com/hishamhm/htop 2016-02-29 21:57:47 -03:00
84a69b1ea4 Release resource when using hwloc 2016-02-29 21:57:27 -03:00
e77811e99b any of these values may wrap 2016-02-29 21:57:03 -03:00
0a4a447d0d Add "p" to Help 2016-03-01 00:57:09 +01:00
cc8375f9ea FreeBSD: use KERN_PROC_PROC with kvm_getprocs to avoid erroneously combining LWPs 2016-02-28 22:41:50 -05:00
03af73bbc7 Merge pull request #427 from tcreech/master
Fix low CPU usage reporting in FreeBSD
2016-02-27 03:04:52 -03:00
0cf3cfa3af Fix low CPU usage reporting in FreeBSD 2016-02-26 23:23:27 -05:00
797bcd0961 Catch invalid IO values due to no permissions.
Display them properly. Not fully convinced of the "no perm" message...
2016-02-20 02:23:26 -02:00
baec4bdcb0 Try to retain last full name of a zombie process.
Once a process goes zombie on Linux, /proc/PID/cmdline
gets empty. So, when we detect it is a zombie we stop
reading this file.
For processes that were zombies before htop started,
there's no way to get the full name.
Closes #49.
2016-02-19 20:51:57 -02:00
a8a5e62760 Force -lgcov harder when running make coverage.
Shouldn't be needed, but I had to make this tweak to make this work again.
2016-02-19 13:40:21 -02:00
f5ddb974a1 Tweaks on the test suite, still problematic. 2016-02-19 13:40:00 -02:00
565101234a Remove old commented code. 2016-02-19 13:39:38 -02:00
d4741d5410 Fix error test — looks like nobody bumped into this one! 2016-02-19 13:39:16 -02:00
9668cd11ed Merge branch 'master' of https://github.com/hishamhm/htop 2016-02-19 13:38:54 -02:00
c23d4f12d1 Fix behavior of ESC key, getting rid of the annoying delay.
Thank you @Explorer09 for the push!
Closes #417.
2016-02-19 13:38:02 -02:00
34431dc7fd Merge branch 'master' of https://github.com/hishamhm/htop 2016-02-18 23:46:36 -02:00
42c4459375 Run through all command line arguments on Darwin.
Also fixes the basename offset for highlighting the basename.
Closes #379.
2016-02-18 23:45:17 -02:00
48254f92e4 Fix behavior of Panel on empty lists.
Closes #370.
2016-02-18 17:45:04 -02:00
5ee6715843 Scan threads for process state information.
Based on: http://stackoverflow.com/questions/6788274/ios-mac-cpu-usage-for-thread
and       e86692e869/ProcessList.c
This should be a fix for #361.
2016-02-18 14:57:09 -02:00
c18309466d Pre-reset 'show' for process.
This simplifies the protocol between the platform-independent
and platform-specific parts. The platform-specific parts
were supposed to re-determine the value of process->show
on each iteration, and the Darwin subsystem wasn't doing that.
Instead of adding the code to the Darwin part, I lifted the
burden of the OS-specific of resetting process->show: now
they can choose to hide a process if they want to (e.g.
detecting kernel threads) but are not required to
(e.g. on Darwin where we're not listing threads separately (yet?)).
Fixes tree view collapsing/expanding on OSX. Closes #416.
2016-02-18 14:32:49 -02:00
fe0ad86e6d Fix tree organization on OSX.
Closes #393.
2016-02-18 14:14:45 -02:00
dc4f145629 Merge pull request #413 from koresar/master
(Regression) Typo in the word "prority" -> "priority"
2016-02-17 10:38:38 -02:00
d43e709364 Typo in the word "priority" 2016-02-17 13:44:07 +11:00
0627a5f0e9 Merge branch 'master' of https://github.com/hishamhm/htop 2016-02-16 14:52:08 -02:00
d10f4f615a Merge pull request #391 from gaod/master
Add MEM% for processes in process list on FreeBSD.
2016-02-16 14:52:02 -02:00
e0c364b9cc Fix reading of io_syscr and io_syscw.
Issue noticed by GCC6 -Wmisleading-indentation.
Thanks @JIghtuse and @Explorer09!
Closes #409.
2016-02-16 14:34:25 -02:00
fd216309d9 Merge pull request #380 from fingolfin/patch-1
Simplify autogen.sh
2016-02-14 21:17:19 -02:00
474d26cea8 Portable affinity using hwloc 2016-02-14 19:57:29 -02:00
471cd60635 Simplify autogen.sh
Make use of the autoreconf tool
2016-02-14 22:36:40 +01:00
f554f08fa9 Fix FreeBSD CPU% calculation 2016-02-14 22:21:11 +01:00
d753996b96 Fix memory percentage display on FreeBSD 2016-02-14 21:48:36 +01:00
b10278318b Merge branch 'master' of https://github.com/hishamhm/htop 2016-02-14 17:47:29 +01:00
35657208d7 Disable the syscall on systems that don't have it.
Got a report in #397 that htop runs in NetBSD
masquerading as Linux and using a compatibility /proc
(like we used to in FreeBSD) and that it builds fine
apart from this syscall.
2016-02-14 12:05:35 -02:00
ae823c375a Make unsupported platform build again.
(Thanks @coypoop at #397 for the heads up!)
2016-02-14 11:45:47 -02:00
1b5025e6f5 Add support for cachedMem and fix usedMem on OpenBSD. 2016-02-14 13:04:18 +01:00
c66ac09a66 Add MEM% for processes in process list on FreeBSD. 2016-02-13 23:24:57 +08:00
0b70439316 Fix buffer reuse. 2016-02-13 02:18:28 -02:00
e9b32eb62f Fix memory accounting in Darwin.
htop currently expects m_size and m_resident in pages (Process.c).
According to the proc_info.h header, the values returned by libproc
are in bytes:
http://www.opensource.apple.com/source/xnu/xnu-1456.1.26/bsd/sys/proc_info.h
Eventually we should change the htop crossplatform API to expect memory
in bytes, but this is the smaller change that should fix it.
Closes #385.
2016-02-13 02:13:57 -02:00
3b9a4b1024 Merge branch 'master' of https://github.com/hishamhm/htop 2016-02-12 23:34:28 -02:00
0e3cf6975f Fix crash when emptying column. Closes #381. 2016-02-12 23:33:53 -02:00
a618f477b2 Merge pull request #387 from gaod/master
Suppress compiler warnings on FreeBSD
2016-02-12 22:47:29 -02:00
cc4267cc5c Suppress compiler warnings. 2016-02-13 02:09:50 +08:00
d0d14da8d9 Fix implicit define isnan() 2016-02-12 14:37:24 +01:00
1bedac1ca2 Merge pull request #363 from myfreeweb/freebsd-memory-fix
Fix process memory on FreeBSD
2016-02-11 17:30:41 -02:00
011bf30d22 Minor tweaks to the README 2016-02-11 17:20:50 -02:00
c56b1a8830 Merge pull request #365 from deric/master
Add travis badge, update README
2016-02-11 17:18:20 -02:00
01edfcf4d4 link to the official website, paypal donate button 2016-02-11 19:17:00 +01:00
d916f6e6f0 remove Debian instructions 2016-02-11 18:58:20 +01:00
e46488463d Merge branch 'master' of https://github.com/hishamhm/htop 2016-02-11 15:24:49 -02:00
8fa9da47de don't mention installing to /usr 2016-02-11 17:32:06 +01:00
d408c74465 add travis badge, code formatting, updated compilation instructions 2016-02-11 14:59:45 +01:00
bb0333e45d fix process memory on FreeBSD 2016-02-11 15:52:39 +03:00
b6f927bffa Merge pull request #359 from myfreeweb/freebsd-battery
Add FreeBSD battery support
2016-02-11 08:46:42 -02:00
f1d1d6e0d6 add FreeBSD battery support 2016-02-11 13:42:37 +03:00
563d5d3ab9 Final touches to ChangeLog 2016-02-10 18:48:39 -02:00
f1f805f29f Support -1 as tpgid 2016-02-10 18:48:04 -02:00
807640e49b Shorten the code using the err() function. 2016-02-03 12:42:25 +00:00
5c593fae42 Be stricter! 2016-02-03 02:02:30 +00:00
d311e85b91 Avoid crash on huge screens.
Fix by @Explorer09 (see discussion on #355).
2016-02-02 23:20:11 +00:00
c148907e7b Try to make threads less confusing. 2016-02-02 16:39:08 +01:00
820c079b14 Initialize default meter modes properly. 2016-02-02 16:15:07 +01:00
f87462d60e Exit on failure! 2016-02-02 16:03:00 +01:00
b2c2b2bfb3 Merge branch 'reuse-comm' 2016-02-02 15:58:50 +01:00
ffcf48fb74 Merge branch 'xalloc' 2016-02-02 15:58:45 +01:00
1cfcc42a8f Reuse comm object if possible, avoid useless repetitions of free+strdup. 2016-02-02 15:56:52 +01:00
b54d2dde40 Check for failure in allocations. 2016-02-02 15:53:02 +01:00
301c346c85 Update generated headers. 2016-02-02 12:11:41 +01:00
21da044fb4 Add generated header. 2016-01-31 17:41:55 +01:00
6434db5b6b Differentiate threads by default. 2016-01-31 12:07:48 +01:00
a1f7f2869e Add allocation tests 2016-01-31 12:01:48 +01:00
dcfcae1ed4 Merge pull request #351 from Explorer09/graph-height
GRAPH_HEIGHT macro and 'dot' variable
2016-01-21 10:43:44 -02:00
040613db33 Change variable 'dot' to avoid division by reciprocal.
(Cherry-picked from d56bcd8e0d8d6a177fc2e40db32fc73ea4588684, the
experimental graph coloring branch)

The variable 'dot' in GraphMeterMode_draw now means "maximum number of
dots per value (column) in graph". The old meaning was "amount of value
that is to be represented by a dot" and was always a fraction. Due to
a limitation in floating point computing, if GRAPH_HEIGHT were not a
power of 2, then rounding errors will occur on numbers like (1.0/3).
(Currently GRAPH_HEIGHT is 4 and so no precision loss.) 'dot' was used
as a divisor, and it's "division by a reciprocal". We change that to
simple multiplication.
2016-01-21 14:06:11 +08:00
d54ab24d97 New macro GRAPH_HEIGHT for Graph Meter height
(Cherry-picked from e93028d7fa0c5f00b5dc3336fd28abaf905cd572, the
experimental graph coloring branch)

Currently GRAPH_HEIGHT=4 . This prevents hard-coding the height of the graph
meters, and allows user to change it at compile-time.
2016-01-21 10:11:54 +08:00
09cf369f2b Merge pull request #349 from Explorer09/clamp-macro
Introduce CLAMP macro. Unify all MAX(l,MIN(h,x)) uses.
2016-01-20 19:31:52 -02:00
6dae8108f8 Introduce CLAMP macro. Unify all MIN(MAX(a,b),c) uses.
With the CLAMP macro replacing the combination of MIN and MAX, we will
have at least two advantages:
1. It's more obvious semantically.
2. There are no more mixes of confusing uses like MIN(MAX(a,b),c) and
   MAX(MIN(a,b),c) and MIN(a,MAX(b,c)) appearing everywhere. We unify
   the 'clamping' with a single macro.
Note that the behavior of this CLAMP macro is different from
the combination `MAX(low,MIN(x,high))`.
* This CLAMP macro expands to two comparisons instead of three from
  MAX and MIN combination. In theory, this makes the code slightly
  smaller, in case that (low) or (high) or both are computed at
  runtime, so that compilers cannot optimize them. (The third
  comparison will matter if (low)>(high); see below.)
* CLAMP has a side effect, that if (low)>(high) it will produce weird
  results. Unlike MIN & MAX which will force either (low) or (high) to
  win. No assertion of ((low)<=(high)) is done in this macro, for now.

This CLAMP macro is implemented like described in glib
<http://developer.gnome.org/glib/stable/glib-Standard-Macros.html>
and does not handle weird uses like CLAMP(a++, low++, high--) .
2016-01-15 20:26:01 +08:00
195f5edbc8 Merge pull request #347 from mklein-de/darwin32+64
Darwin: replace vm_statistics64_* with vm_statistics_*
2016-01-13 21:32:43 -02:00
d312510223 Darwin: replace vm_statistics64_* with vm_statistics_*
Works with:
- Darwin 9.8.0 (OS X 10.5.8) PPC
- Darwin 15.2.0 (OS X 10.11.2) Intel
2016-01-13 20:57:29 +01:00
9c39422c71 Merge pull request #346 from mklein-de/vikeys
vi keys: translate ALT-h/j/k/l to arrow keys
2016-01-13 14:20:33 -02:00
99b947058f vi keys: translate ALT-h/j/k/l to arrow keys 2016-01-12 21:15:04 +01:00
466d4da0c6 refactor *Screen classes, add InfoScreen superclass 2016-01-12 06:00:58 -02:00
faf2860669 Merge branch 'mklein-de-envscreen' 2016-01-12 02:57:58 -02:00
032af1577c Merge branch 'envscreen' of https://github.com/mklein-de/htop into mklein-de-envscreen 2016-01-12 02:55:43 -02:00
d850803eb8 Merge branch 'master' of https://github.com/hishamhm/htop 2016-01-11 20:39:08 -02:00
c6ca311d18 Present IO-Wait as a dot in monochrome. Fixes #345.
Thank you @Explorer09 for the report!
2016-01-11 20:38:10 -02:00
fc61e25f5b Merge pull request #343 from FreedomBen/typo-priority
Fix typo: prority => priority
2016-01-06 22:00:52 -02:00
4b29e8485f Fix typo: prority => priority 2016-01-06 13:49:49 -09:00
b14f89e9d4 drop privileges before reading environment 2016-01-06 22:53:14 +01:00
b10e54cdee Merge remote-tracking branch 'upstream/master' into envscreen 2016-01-06 22:39:57 +01:00
fc4c9757b0 Merge pull request #315 from mklein-de/suid
add some security checks when running SUID root
2016-01-06 18:19:28 -02:00
82db9979b1 Merge pull request #339 from eworm-de/configure
use AC_HELP_STRING for proc dir
2016-01-06 18:15:39 -02:00
4c23a81d72 use AC_HELP_STRING for proc dir 2016-01-05 10:23:08 +01:00
6bc98bb227 Merge pull request #338 from mmcco/opl
Rename variable for consistency
2016-01-04 20:59:57 -02:00
e595f6865e Rename variable for consistency
Suggested by Hisham.
2016-01-04 16:20:51 -05:00
3ad2510fc2 Merge pull request #337 from eworm-de/help
align help output
2016-01-04 18:14:42 -02:00
be6b0c2830 Merge branch 'mmcco-freebsd-free' 2016-01-04 18:09:22 -02:00
f342a9eb83 Merge branch 'freebsd-free' of https://github.com/mmcco/htop into mmcco-freebsd-free 2016-01-04 18:08:51 -02:00
c5b9045f18 Plug leak in FreeBSD backend, as noted by @mmcco in #334. 2016-01-04 18:04:50 -02:00
77cffaacf6 Merge pull request #334 from mmcco/cpu
Plug mem leak, improve CPU enumeration logic
2016-01-04 18:02:36 -02:00
9ca646acbb align help output 2016-01-04 16:02:29 +01:00
61f2d674b0 Remove NULL-checks before free()
These are never necessary when using the standard library.
2016-01-03 16:59:44 -05:00
198592a0f1 Plug mem leak, improve CPU enumeration logic
I think this leak may still exist in the FreeBSD port.
2016-01-03 16:56:33 -05:00
be9edc5d43 Merge branch 'master' of https://github.com/hishamhm/htop 2016-01-03 16:32:48 -02:00
70cd5c2b88 Check range when accessing keys table.
Should fix #321.
2016-01-03 16:31:44 -02:00
86954f9204 Merge pull request #331 from mmcco/fixes
OpenBSD fixes and updates
2016-01-03 16:17:31 -02:00
fe83bc8e68 Merge pull request #332 from mmcco/maintainer
Fix spelling of "maintainer"
2016-01-03 16:12:29 -02:00
918cfd54d6 Fall back to sysctl's command name, and a bugfix
This is what OpenBSD's top(1) does when the libkvm call fails, and it's
a good idea.

This commit also fixes process name construction. The space was being
written one character too far.
2016-01-02 22:05:20 -05:00
3da36bbc61 Use dynamically allocated memory for process names
Even when they're constant, as is the case for zombie processes.
2016-01-02 17:11:23 -05:00
c1b3289219 Check for allocation failure
Pointed out by Michael Reed.
2016-01-02 12:20:40 -05:00
ae5c01f485 Use err() rather then errx() for sysctl()
So that we can see errno. Pointed out by Michael Reed.
2016-01-02 12:17:35 -05:00
7170382706 Fix spelling of "maintainer" 2016-01-02 12:11:26 -05:00
22cfda6332 OpenBSD fixes and updates
I forgot how awful the process name logic was. It was an initial hack to
get it running, and I forgot to clean it up.

I also had to change a few includes and error function uses.
2016-01-02 11:57:53 -05:00
c8cadfb905 Merge branch 'master' of https://github.com/hishamhm/htop 2015-12-23 13:58:35 -02:00
526eca9a19 Merge pull request #320 from etosan/master
major meters update
2015-12-23 13:57:51 -02:00
adaa4de295 Merge pull request #325 from Explorer09/master
Fix text on function bar when moving meters.
2015-12-22 13:24:55 -02:00
71ad9b304d Fix function bar when moving meters.
Before:
[Up]Up [Dn]Down [Lt]Left [Rt]Right [Arrow]Confirm [Enter]Delete [Del]Done

After:
[Up]Up [Dn]Down [Lt]Left [Rt]Right [Enter]Confirm [Del]Delete [Esc]Done
2015-12-22 17:43:31 +08:00
c67e482c67 fixed bug with PPID of parent not being set on first process list scan pass.
this caused htop to show processes as if freebsd kernel was their parent.
on next pass reparenting code took chance to run, and that caused process to jump around.
this fixed behaviour should be the correct one
2015-12-17 08:48:53 +01:00
802e216870 Extend buffer for reading lines from /proc.
Apparently a line longer than 255 chars was spotted in the wild:
http://serverfault.com/questions/577939/linux-ps-htop-show-processes-running-for-hundreds-or-thousands-of-days-though-h#comment676098_577939
2015-12-14 13:27:11 -02:00
e0b6e2eef2 enabled swap meter 2015-12-13 04:16:06 +01:00
9d55c56f26 added Support for memory meter, and slightly adjusted process monitor logic 2015-12-13 04:11:35 +01:00
80f594f314 added CPU% for processes in process list 2015-12-13 01:39:54 +01:00
c2769985cc added cpu monitoring for both single core and smp systems, some notes in process monitor 2015-12-13 00:21:02 +01:00
bc84920b91 added support for effective UID/username change detection 2015-12-11 11:01:24 +01:00
563abbc44b Merge pull request #319 from acatton/master
Redraw the panel when resizing the window during an incremental search
2015-12-10 18:34:57 -02:00
065aab5247 Redraw the panel when resizing the window during an incremental search
KEY_RESIZE wasn't handled by the incremental search. Resulting in this
bug:

  * Set your terminal window to a small size.
  * Press '\' to filter the processes
  * Maximize your terminal window
  * The list of processes didn't resize.

This change fixes this bug.

Thank you Julian Andrews (@julianandrews) for finding this bug.
2015-12-09 23:46:25 -07:00
cccc18dd2f Oops! 2015-12-09 17:34:57 -02:00
84783bd6f0 Fix fopen mode in Settings_read() 2015-12-09 20:34:11 +01:00
db6828617d More thorough checks for ncurses*-config scripts.
See #198.
2015-12-09 17:17:30 -02:00
ab3a7c2fa8 drop privileges before changing process priority or sending signals
- replaces uid check from d18e9a4895
2015-12-07 20:10:09 +01:00
6b1b073ae4 Merge branch 'master' of https://github.com/hishamhm/htop 2015-12-06 19:07:16 -02:00
c23770245e If ncurses*-config script is present, use it in configure
when testing for ncurses library. See #198.
2015-12-06 19:06:23 -02:00
cc23d13f87 Add Platform_getProcessEnv
- currently implemented for darwin and linux
2015-12-03 22:23:40 +01:00
42b08f2233 drop privileges during Settings_read()/Settings_write() 2015-12-02 23:42:10 +01:00
0919ea32f9 'e' displays environment of current process
- uses sysctl(KERN_PROCARGS2) on *BSD
- doesn't work on Linux yet
2015-12-02 23:26:00 +01:00
d18e9a4895 add some security checks when running SUID root
on Darwin, htop needs to run with root privileges to display information
about other users processes. This commit makes running htop SUID root a
bit more safe.
2015-12-02 22:56:01 +01:00
670a2de692 Merge pull request #313 from hishamhm/sreclaimable
Update calculation of used vs. free memory.
2015-11-30 18:27:33 -02:00
96c929f82b Use ncurses*-config scripts bundled with ncurses.
Ensure that all necessary libs and flags are passed along,
such as adding -ltinfo when needed.

Closes #198.
2015-11-30 16:36:22 -02:00
a84aa2e782 Cached memory calculations, take 2.
Thanks to @OmegaPhil for discussion and reviewing.
2015-11-29 23:55:31 -02:00
5bc1f5ed04 Account unreclaimable slab and shmem as used memory,
reclaimable slab as cached memory.

Hopefully this presents a more truthful representation of
available vs. used memory on Linux.
See brndnmtthws/conky#82, #242, #67, #263.
2015-11-28 22:22:00 -02:00
d34645f8f1 Array entries have been oddly flipped here for years. 2015-11-23 03:46:43 -02:00
f02d8f4386 Merge pull request #303 from kaefer/desc-load-averages-meter
match load averages description to what it actually shows
2015-11-23 02:55:27 -02:00
aae02bbfc9 Merge pull request #304 from Sworddragon/master
Manpage update
2015-11-23 02:50:30 -02:00
5521094b1e Merge pull request #309 from hishamhm/travis-ci
Integrate Travis-CI
2015-11-20 15:59:54 -02:00
347141502b Test for unicode-enabled libncurses 2015-11-19 13:26:11 -02:00
0837fc5b62 travis-ci should really detect autogen.sh... 2015-11-19 13:14:20 -02:00
d12084b032 ChangeLog updates. 2015-11-19 13:08:23 -02:00
c24270be0a Test on Linux and OSX 2015-11-19 12:58:00 -02:00
d820d11c80 Add initial .travis.yml 2015-11-19 12:56:26 -02:00
4b83a82dca Minimal stub 'make test' target. 2015-11-19 12:56:01 -02:00
ad5d9c2542 Merge pull request #308 from SaltwaterC/darwin-swap
Add Darwin swap meter
2015-11-19 12:53:47 -02:00
8895f09880 Add Darwin swap meter. 2015-11-16 17:32:22 +00:00
f097bdce8f Fixed/enhanced some entries in the manpage 2015-11-04 12:13:16 +01:00
1e31b63a7e Fixed some typos 2015-11-04 12:09:22 +01:00
33cdaf3ac0 match load averages desc. to what it actually shows 2015-11-04 11:53:17 +01:00
bef00a4fd9 Create directory m4 if it doesn't already exist.
(See #292.)
2015-11-03 02:21:53 -05:00
5e0f1788e6 Fix CRT_fatalError warning. 2015-11-02 10:22:10 -05:00
a7fcbba75a Add missing header, silence warning. Should fix #292. 2015-11-02 10:46:04 -05:00
3cfbcb72c8 Highlight moving meters in a different color. 2015-11-02 10:33:37 -05:00
f187be9296 Fix moving of meters 2015-11-02 10:33:22 -05:00
fd5dd6605a Merge pull request #299 from mmcco/master
OpenBSD port updates and error exit improvements
2015-11-02 08:46:40 -05:00
cd3d2337f8 Replace all err.h function uses with CRT_fatalError(). Failing with
err.h functions corrupts the terminal when using curses.
2015-11-01 13:26:57 -05:00
0fb9a8c389 Remove a debugging print 2015-11-01 13:19:01 -05:00
b669540e4e Merge pull request #298 from patrickmarlier/issue202
Fix a case where the usertime calculation can overflow (see issue #202)
2015-10-26 17:55:13 -04:00
ea8a8b2d6b Merge pull request #296 from hishamhm/darwin-cpu-average
Darwin cpu average
2015-10-24 23:29:47 -04:00
bd93b2e1d7 Initialize variables, silence warnings. 2015-10-24 23:28:29 -04:00
71190654bc Calculate CPU averages on Darwin (See #295). 2015-10-23 13:46:21 -02:00
9c146393a3 Improve prose a bit. 2015-10-19 17:26:53 -02:00
57333d8a47 Merge pull request #280 from eworm-de/man-htoprc
add hint about system configuration file
2015-10-19 17:26:22 -02:00
78f2933e2b Regenerate platform-dependent headers.
Closes #293.
2015-10-19 17:22:54 -02:00
8673a84e5f Remove some trailing whitespace 2015-10-13 11:05:52 -04:00
9f1884c28f Fix generation of openbsd/Platform.h and replace a vestigial dummy CPU utilization value 2015-10-09 14:57:01 -04:00
e906c0dddb Merge pull request #287 from etosan/master
PPID and JID update fix
2015-10-07 20:48:46 -03:00
93f05b459f fixed broken merge, where I forgot to check linux/Platform.c so that it matches upstream 2015-10-06 20:05:55 +02:00
23bf564d73 Fixed reparenting issue. PPID should be updated each refresh as any process can get reparented to either
PID1 or even any other PID (if there are custom reapers in the system).
Similar issue with jails, elevated process can ask kernel to attach itself into any jail at any time,
thus JID and jail name can change each refresh cycle.
2015-10-06 19:50:19 +02:00
214d0cff0d merge with 'upstream/master' 2015-10-06 19:39:12 +02:00
3ea7b34735 attempt to return to upstream/master 2015-10-06 19:39:01 +02:00
cc0fc3655d Add Darwin signals (same as FreeBSD)
https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man3/signal.3.html
2015-10-06 13:03:47 -03:00
56193323ce let's let the user find SIGIOT if the look for it :) 2015-10-06 12:58:53 -03:00
ee0d602a47 Merge branch 'master' of https://github.com/hishamhm/htop 2015-10-06 12:52:48 -03:00
125c23ef23 Fix build in FreeBSD, and make sure SIGTERM is always the default. 2015-10-06 12:50:31 -03:00
384c92f7e4 Merge pull request #286 from mmcco/master
Add OpenBSD signals
2015-10-06 12:42:13 -03:00
c4eb99f264 Add header file reference for OpenBSD signals 2015-10-06 11:32:40 -04:00
77f12bbecd Add OpenBSD signals 2015-10-06 11:25:16 -04:00
2379835910 Added platform dependent DEFAULT_SIGNAL define, for now for:
FreeBSD
Linux
Other platforms will have it undefined for now.
2015-10-06 14:04:22 +02:00
86417e4157 Unless I move signal definitions into the comment used for header generation,
htop fails to compile with:

```text
SignalsPanel.c:32:49: error: use of undeclared identifier 'Platform_signals'
      Panel_set(this, i, (Object*) ListItem_new(Platform_signals[i].name, Platform_signals[i].number));
                                                ^
1 error generated.
*** Error code 1
```
2015-10-06 12:46:37 +02:00
7859857fdd add hint about system configuration file 2015-10-06 08:18:56 +02:00
3fe2f3e28e Move list of signals to platform-specific code.
Implementations for Linux (tested) and FreeBSD (still untested, thanks to @etosan for providing the table).
Darwin and OpenBSD(ping @mmcco) builds should be broken now, pending their own tables.
2015-10-06 03:02:49 -03:00
fbb4c49edd gcc warning cleanups. 2015-10-05 11:27:44 -03:00
bf276a0993 Merge pull request #274 from mmcco/master
Cleanup and initial OpenBSD support
2015-10-05 11:22:50 -03:00
5c2b0a6bbc Merge pull request #283 from eworm-de/editorconfig
add editorconfig file to give hints to editors
2015-10-05 11:19:59 -03:00
1cda2d7397 add editorconfig file to give hints to editors
We use an unusual indent of three spaces. Let's give editors a hint
about that.
2015-10-05 11:13:17 +02:00
68c3270be8 Merge pull request #281 from etosan/master
Added preliminary attempt at jails support on FreeBSD - JID and JAIL (name) columns
2015-09-30 22:28:40 -03:00
8c00fa4582 Added preliminary attempt at jails support on FreeBSD - JID and JAIL (name) columns, somewhat more correct kernel "thread" detection.
Seems FreeBSD kernel can spawn both kernel processes (what is what htop currently sees) and kernel threads.
For now let's consider kernel processes kernel "threads".
2015-09-30 22:04:26 +02:00
2d1507ad5a Merge pull request #275 from trebmuh/patch-1
Update htop.desktop (FR l10n)
2015-09-21 14:17:21 -03:00
6a21d2f3a6 Fix enumeratoin of on-CPU processes in OpenBSD 2015-09-19 12:45:22 -04:00
ad1a0ad08d Replace some remaining tabs 2015-09-19 12:21:22 -04:00
571cbc0aa1 Change more fprintf(stderr, ...); exit(...); to err[x](...). Tweak a few existing ones and fix some style. 2015-09-19 12:15:26 -04:00
e2bbd5cfa4 Change some tabs to three spaces 2015-09-19 12:08:34 -04:00
c5d725daf9 Update htop.desktop
fix
2015-09-19 00:12:41 +02:00
9c14fa6fad Merge pull request #273 from kaefer/fix-glibtoolize-detection
Mac build fixes
2015-09-18 15:41:41 -03:00
a9a5a539cf (Very) initial working OpenBSD port 2015-09-18 00:46:48 -04:00
b0e074f6b6 Update htop.desktop
This one is adding FR localisation to the desktop file.
Cheers.
2015-09-17 20:04:18 +02:00
02727bda81 ignore which errors 2015-09-17 08:44:34 +02:00
445222e48c Clean up some needless malloc casts, convert some mallocs to callocs, and fix some style 2015-09-16 23:42:36 -04:00
b37d4f172f Fix a case where the usertime calculation can overflow (see issue #202) 2015-09-14 22:51:14 +02:00
40525d85bc removing unnecessary include, which on top breaks compiling 2015-09-14 16:18:51 +02:00
661a4cec4a fix glibtoolize detection 2015-09-14 10:03:17 +02:00
1d805b36b4 Bring changelog up-to-date :) 2015-09-11 16:29:19 -03:00
8e81119c33 Merge branch 'master' of https://github.com/hishamhm/htop 2015-09-11 13:42:04 -03:00
0c2ccde306 Detect when libtoolize is called glibtoolize.
It's the case on Darwin, and might be in other BSDs as well.
As referred in #268.
Supersedes PR #269.
2015-09-11 13:39:06 -03:00
a92f803903 Merge pull request #268 from jweyrich/darwin-fixes
Darwin fixes - duplicate & conflicting decls
2015-09-10 16:41:53 -03:00
e52c070ef5 Remove conflicting declarations. 2015-09-10 10:46:44 -03:00
ffacac14a9 Remove duplicate declaration. 2015-09-10 10:45:22 -03:00
229d005851 Merge pull request #262 from eworm-de/calloc
fix calloc() calls
2015-09-08 14:43:33 -03:00
e8970b6f32 fix calloc() calls
* size_t nmemb (number of elements) first, then size_t size
* do not assume char is size 1 but use sizeof()
* allocate for char, not pointer to char (found by Michael McConville,
  fixes #261)
2015-09-07 07:52:39 +02:00
2df36ee2f2 Merge pull request #254 from eworm-de/kilobyte-precision
kilobytes is the smallest unit and never has precision
2015-08-29 19:45:47 -03:00
bf456972b7 kilobytes is the smallest unit and never has precision
Signed-off-by: Christian Hesse <mail@eworm.de>
2015-08-29 21:22:46 +02:00
a859e9204c Merge pull request #253 from eworm-de/clock
get a useful tolal for clock meter
2015-08-29 16:18:51 -03:00
08392ce5d3 Merge pull request #255 from eworm-de/missing-blank
add missing blank
2015-08-29 16:14:00 -03:00
ba3504fa26 add missing blank 2015-08-28 11:29:33 +02:00
804bd37dca get a useful tolal for clock meter 2015-08-28 10:15:00 +02:00
8a2dd83efa call for action! 2015-08-27 23:44:58 -03:00
a008cb73a4 Updated README 2015-08-27 23:44:15 -03:00
7985724933 Fixes for color glitches in ncurses ABI6.
Could no longer reproduce #244 after these fixes.
2015-08-27 21:45:02 -03:00
bdadd45a88 Fix indentation. 2015-08-27 21:37:06 -03:00
bde3406add Merge branch 'master' of https://github.com/hishamhm/htop 2015-08-27 19:14:45 -03:00
3c4326b450 Don't select last item when clicking past the end of the panel. 2015-08-27 19:14:25 -03:00
6082db2121 Improve feedback when moving meters. 2015-08-27 19:14:13 -03:00
f6c31eeaf5 Handle KEY_RECLICK events generated by ScreenManager 2015-08-27 18:43:22 -03:00
f6d48c172c Merge pull request #247 from eworm-de/dual-meters-margin
remove extra space in dual meters without margins
2015-08-27 18:42:36 -03:00
696e36cb45 remove extra space in dual meters without margins 2015-08-27 22:40:35 +02:00
f585fc9825 Merge pull request #208 from eworm-de/dynamic-unit
Dynamic unit
2015-08-27 17:33:24 -03:00
79356dc125 Merge pull request #250 from eworm-de/ascii-blank
use ASCII blank even for graphical meter in UTF-8 mode
2015-08-27 17:32:33 -03:00
cf47f4fca1 use dynamic units for text display
Signed-off-by: Christian Hesse <mail@eworm.de>
2015-08-27 06:13:27 +02:00
6f58fbc5dd make units more dynamic
Signed-off-by: Christian Hesse <mail@eworm.de>
2015-08-27 06:13:27 +02:00
f3a9f5406b Merge pull request #248 from eworm-de/wctype
include wctype.h for iswprint()
2015-08-26 23:42:32 -03:00
93b811a051 use ASCII blank even for graphical meter in UTF-8 mode
If the terminal has no font with braille characters we see... Nothing
useful. Use an ASCII blank at least, so we have an idea about what's
going on.
2015-08-25 17:40:36 +02:00
f10cbaa812 include wctype.h for iswprint() 2015-08-25 15:23:50 +02:00
8f07868fef A more portable version of the note suggested in #113.
Closes #113.
2015-08-20 02:17:11 -03:00
0507cd38f7 Merge pull request #43 from ErkiDerLoony/master
Fix tree view if userland threads are hidden.
2015-08-20 02:15:04 -03:00
544c7efa6b Omit non-printable characters with widechar curses.
Closes PR #111.
2015-08-20 02:07:24 -03:00
c7387fac15 Fix mouse click on meters setup function bar 2015-08-20 01:34:52 -03:00
4597014ea3 Standardize variable name. 2015-08-20 01:27:07 -03:00
f019f4cd9e Merge pull request #221 from eworm-de/define-array-size
use a define for graphical meter array size
2015-08-20 01:26:38 -03:00
ea18a50091 Merge pull request #134 from jeffgarrett/pid-selection
Interpret command line PIDs as PIDs, not TIDs
2015-08-20 01:20:21 -03:00
c9cab824e9 Extra checks. 2015-08-20 01:13:20 -03:00
5e4f1e46cc Reduce scope of variables. 2015-08-20 01:12:34 -03:00
9428010121 Make column width calculation dynamic.
Closes #228.
2015-08-20 00:32:47 -03:00
8bd603cb68 Make Unicode strings safe for ncurses 6 ABI.
Closes #241.
2015-08-20 00:31:48 -03:00
78be8201dc Add expand/collapse on additional clicks! 2015-08-19 19:09:54 -03:00
b003636958 Support for NCurses 6.0 and mouse wheel 2015-08-19 18:55:24 -03:00
a2a34e89a1 simplify Meter loop
* Use MIN() and MAX() to make sure values are inside bounds. This should
  fix an issue where Meters were missing dots at the bottom.
* Remove variable 'level' and calculate on the fly.
2015-08-19 22:42:34 +02:00
d8e23bb084 remove UTF-8 code when compiling with --disable-unicode 2015-08-19 22:42:34 +02:00
9e67b6585e make arrays one dimensional
With more dimensional arrays we have to define the array size. Use
one dimensional arrays to be more flexible.
Additionally this allows to shrink array size for ASCII.
2015-08-19 22:42:34 +02:00
3e93f9b852 Fix comment. 2015-08-19 13:58:29 -03:00
f70649a178 Standardize indentation. 2015-08-19 13:56:46 -03:00
907f8298a0 CPU per process implemented 2015-08-19 13:52:57 -03:00
57ab332d5a Fix the thread counts 2015-08-19 13:52:38 -03:00
6463ea2956 Fixed CPU updating 2015-08-19 13:52:18 -03:00
7f3faa276a Static CPU meter 2015-08-19 13:52:04 -03:00
43ef703f03 Start supporting actual data 2015-08-19 13:51:49 -03:00
70e7c8db59 Added darwin with working battery meter 2015-08-19 13:47:26 -03:00
feb7a01fd3 Make unsupported run 2015-08-19 13:46:41 -03:00
5e602f18d5 Rename String to StringUtils.
Fixes building on case-insensitive filesystems where String.h gets confused with <string.h>.

From d734dacea0a10d0465dad4e95b3421511e7da112 Mon Sep 17 00:00:00 2001
From: David Hunt <dhunt@iolanthe.attlocal.net>
Date: Sat, 11 Jul 2015 20:56:31 -0500
Subject: [PATCH 1/8] Rename String to StringUtils
2015-08-19 13:45:20 -03:00
f2c053a884 Add translations for desktop file.
Translations obtained by @glixx from Mandriva Linux.

Closes #227.
2015-08-12 18:05:46 -03:00
0ebe688d24 Avoid future confusions with how default values are set. 2015-08-12 17:29:32 -03:00
e1e3ffad19 Ensure default matches classic htop behavior. 2015-08-12 17:24:41 -03:00
4d44c35519 Merge pull request #148 from nckx/display-basename
[PATCH] New setting: "Show program path"
2015-08-12 17:16:50 -03:00
4e135bb6b6 Merge pull request #218 from eworm-de/remove-autoconf-files
remove autoconf files, generate by autogen.sh
2015-08-12 17:13:22 -03:00
2ed83d6902 Merge branch 'master' of https://github.com/hishamhm/htop 2015-08-12 17:11:28 -03:00
041fa9ffa6 Update Settings.h 2015-08-12 17:11:07 -03:00
c34b574073 Merge pull request #223 from eworm-de/langinfo
simplify UTF-8 detection
2015-08-12 17:11:03 -03:00
e42d78007e Merge pull request #230 from maksqwe/cstime_fix
Fix sort by cstime
2015-08-12 17:08:26 -03:00
fc0e44662c Add shortcut key 'p' to toggle full program paths. 2015-08-07 14:20:14 +02:00
293eec4265 New setting: "Show program path"
Add a setting to hide all but the last component from the programme
path, leaving only the "basename". Makes htop more usable on smaller
screens, or systems with longer than average paths. Off by default.

"Highlight program basename" will still be respected, to further
visually separate process names from their arguments.
2015-08-07 14:20:14 +02:00
c33d32e66b Merge pull request #235 from peter-warhzner/patch-1
Fix typo in comment
2015-08-04 16:36:26 -03:00
6f6f0e36ad Fix typo in comment 2015-08-04 18:48:34 +05:00
cea591181d initialize locale for LC_CTYPE only
htop uses scanf functions to parse values from proc filesystem. This
breaks when initializing locale for LC_NUMERIC because of unexpected
commas. So initialize locale for LC_CTYPE only.
2015-07-29 11:28:15 +02:00
1bdee6b6ba Fix sort by cstime 2015-07-23 14:24:39 +03:00
77df258636 remove duplicate code 2015-07-16 08:17:12 +02:00
1728483aa2 simplify UTF-8 detection 2015-07-16 08:12:48 +02:00
016dbbe6a4 initialize locale
This has two effects:

* The locale may have impact on string formatting. So depending on the
  locale we may end up with different decimal point.
* We can use nl_langinfo() for UTF-8 detection.
2015-07-16 08:08:18 +02:00
ff49f558b2 remove autoconf files, generate by autogen.sh 2015-07-15 09:30:38 +02:00
e5f810e0a2 Merge pull request #226 from eworm-de/nice
paint PROCESS_LOW_PRIORITY in green
2015-07-14 23:16:11 -03:00
a588c6d179 paint PROCESS_LOW_PRIORITY in green
... and thus make it use a different color than PROCESS_HIGH_PRIORITY.
2015-07-14 12:43:38 +02:00
b1aea7f748 Merge pull request #207 from eworm-de/settings
write header length (number of columns) back to configuration
2015-06-16 16:13:50 -03:00
5f60486002 Merge pull request #211 from eworm-de/empty-meter
ignore enter, delete and space on empty panel
2015-06-16 16:12:00 -03:00
67a88ae11d Merge pull request #205 from EliteTK/issue-201
Change all displayed memory size specifiers to use GNU Coreutils style size specifiers
2015-06-16 16:09:32 -03:00
b796362e90 ignore enter, delete and space on empty panel 2015-06-15 11:09:32 +02:00
a804f1f848 write header length (number of columns) back to configuration 2015-06-12 01:35:12 +02:00
03826fbc54 Added information about memory sizes to man page
The man page now contains the section "MEMORY SIZES" which talks about
the convention used for representing memory sizes and why it was chosen.
2015-06-09 00:11:43 +01:00
16d8cc7c38 Changed MemoryMeter and SwapMeter to use short memory sizes
The MemoryMeter and SwapMeter now use the short GNU Coreutils style
format to represent memory sizes.
2015-06-09 00:08:06 +01:00
abe165fe5c Merge pull request #194 from eworm-de/warnings
fix compiler warnings
2015-06-08 15:26:24 -03:00
2351fdea31 Merge pull request #195 from eworm-de/gitignore
add */.dirstamp to .gitignore
2015-06-08 15:24:55 -03:00
75d0eda6f9 Merge pull request #196 from sherpya/master
added missing defines for android
2015-06-08 15:23:12 -03:00
d7c3dc748f Merge pull request #203 from Lance0312/fix-freebsd-compilation-errors
Fix several FreeBSD compilation errors
2015-06-08 15:22:10 -03:00
5a5dc71770 Cast FreeBSDProcess_new to Process_New
`Process_new_fn` had been renamed to `Process_New` in
d880def0e9
2015-06-07 17:31:05 +08:00
1efa544e1b Re-run MakeHeader.py on freebsd/FreeBSDProcess.c
Several functions and struct had changed in
b291fba02b
2015-06-07 17:30:55 +08:00
a8e1c1c63f added missing defines for android 2015-05-20 04:30:11 +02:00
867dece8fe add */.dirstamp to .gitignore 2015-05-15 11:38:16 +02:00
08829cbc3b fix compiler warnings
gcc gives warnings like this:

warning: ignoring return value of ‘fscanf’, declared with attribute
warn_unused_result

Assign value to a variable, cast to (void) to discard it.
2015-05-15 11:33:25 +02:00
f1e8a074f2 store cpuCount in an attribute 2015-05-13 15:13:55 -03:00
f4f6d54ffd Fix compilation of OpenVZ support.
Closes #185.
Closes #190.
2015-05-13 15:00:58 -03:00
64ecba2583 update bar when clicking header. 2015-04-09 16:02:14 -03:00
ac8b934498 Fix failing stuff. 2015-04-09 15:56:43 -03:00
01737ed66b Reset to the default scheme, not the last one. 2015-04-09 15:44:26 -03:00
2f45008477 Enable OOM support unconditionally on Linux.
Read OOM data only if column is enabled.
Make sort ordering more consistent. Closes #182.
2015-04-09 15:41:21 -03:00
b291fba02b Fixes to use platform-specific compare routines. 2015-04-09 15:40:46 -03:00
dc4576d327 Fix saving new color scheme. 2015-04-09 15:19:31 -03:00
b1f934c5d5 Visual tweaks: change color when following, add Broken Gray theme. 2015-04-09 15:17:20 -03:00
01a29f0267 84.9% test coverage in the wip branch! 2015-04-08 21:27:52 -03:00
094ddc4051 This assert is no longer valid. 2015-04-08 21:27:36 -03:00
d4256d6300 Keep following status when no key is pressed. 2015-04-08 21:25:31 -03:00
fde3457235 Make test more robust. 2015-04-08 19:07:41 -03:00
86d5750451 Merge branch 'master' into wip 2015-04-08 18:47:01 -03:00
e3d1ca0d20 83.9% test coverage! 2015-04-02 03:33:12 -03:00
4e29cad4fe Merge branch 'master' into wip 2015-04-02 02:13:18 -03:00
e5b08447fe Better support for testing under wip branch 2015-04-02 02:13:07 -03:00
d880def0e9 Merge branch 'master' into wip
Conflicts:
	Process.c
	Process.h
	htop.c
	linux/LinuxProcess.c
	linux/LinuxProcess.h
	test_spec.lua
2015-04-02 01:57:37 -03:00
4315e0c2a0 78.8% test coverage! 2015-04-02 01:44:44 -03:00
1179906e95 Add test, change env variable name 2015-04-02 01:41:52 -03:00
cb8ac6b0f1 Ignore coverage files. 2015-03-31 23:24:06 -03:00
a7236559a7 81.1% test coverage! 2015-03-31 23:23:40 -03:00
4c24a9b462 Fixes to subclassing Process. 2015-03-31 23:23:10 -03:00
5320bab202 Cleanup unused field. 2015-03-31 22:31:06 -03:00
849adf6155 78.8% test coverage! 2015-03-30 01:52:39 -03:00
99e58b3200 Add test, change env variable name 2015-03-28 19:36:06 -03:00
4eee71051b Add coverage testing rules 2015-03-28 19:25:48 -03:00
2939a84ea3 Beginnings of a test suite! 2015-03-27 16:30:02 -03:00
63aa02c7a9 Beginnings of a test suite! 2015-03-27 16:29:09 -03:00
80ef119fe7 Add coverage testing rules 2015-03-25 17:14:40 -03:00
70abaadbaf Fix NULL-termination of array for later use by String_freeArray. 2015-03-25 16:36:42 -03:00
a93db5234c handle clicks on panel header line 2015-03-24 23:12:43 -03:00
38fd1bfaba Add a test plan.
A list of functionality to go through and test before releases.
2015-03-23 19:25:57 -03:00
7cb8cb05fb Simplify constructors. 2015-03-23 19:24:34 -03:00
8a11281c67 Merge branch 'master' into wip
Conflicts:
	Panel.c
	htop.c
	htop.h
2015-03-23 18:16:47 -03:00
b1b3f57976 Tempus fugit.
Conflicts:
	htop.c
	htop.h
2015-03-23 18:15:56 -03:00
54f8d8154b Do not trust isalpha(c) for values > 255.
Fixes #174.

Conflicts:
	Panel.c
2015-03-23 18:14:35 -03:00
ade7993fcb incremental search and filter reintegrated! 2015-03-23 17:04:53 -03:00
d0c72c3fb2 Move FunctionBar inside Panel 2015-03-23 15:26:56 -03:00
442a0d33e4 Do not trust isalpha(c) for values > 255.
Fixes #174.
2015-03-22 22:56:28 -03:00
1084a3ff8f Working on integration of IncSet in new branch. 2015-03-22 22:39:33 -03:00
09c7152990 Refactor state control variables. 2015-03-22 02:50:40 -03:00
1e0c530a46 Tempus fugit. 2015-03-21 16:52:54 -03:00
4e064e0db7 Build fixes to resync with FreeBSD changes. 2015-03-16 23:03:40 -03:00
272e2d9b34 Major advances in FreeBSD port. 2015-03-16 23:02:03 -03:00
9ff5d2b243 Fix saving of header states, motion in Setup screen. 2015-03-16 23:01:21 -03:00
14a177800b Remove spurious UTF-8 char from ASCII mode. 2015-03-16 22:58:56 -03:00
59914bfd5b Rename Setup_run to Action_runSetup. 2015-03-16 22:58:23 -03:00
7fd4af80ff Linux build fixes. 2015-03-16 03:25:43 -03:00
5c8b83405b Merge branch 'master' into wip
Conflicts:
	Process.c
	Process.h
	linux/LinuxProcess.c
	linux/LinuxProcess.h
	linux/LinuxProcessList.c
	unsupported/Platform.c
	unsupported/Platform.h
2015-03-16 03:22:33 -03:00
ddbb71d1c8 Add files to unsupported platform. 2015-03-16 03:16:18 -03:00
adbfe3c3f1 Get FreeBSD tree to compile again with latest changes. 2015-03-16 03:14:20 -03:00
40f3391a3c Merge branch 'wip' of https://github.com/hishamhm/htop into freebsd 2015-03-16 01:45:05 -03:00
be1700cf94 Isolate portable and Linux-specific process fields. 2015-03-16 01:43:04 -03:00
a44a64d0ee Merge branch 'wip' of https://github.com/hishamhm/htop into freebsd 2015-03-15 23:03:43 -03:00
bc928d7f47 Add MainPanel.c 2015-03-15 23:03:26 -03:00
967b6569a1 Merge branch 'wip' of https://github.com/hishamhm/htop into freebsd 2015-03-15 22:57:01 -03:00
d2acffa59a Merge branch 'wip' of https://github.com/hishamhm/htop into freebsd
Conflicts:
	htop.c
	unsupported/Platform.h
2015-03-15 22:53:10 -03:00
e3fe3962cb Move more Linux-specific code into Linux subdir. 2015-03-15 20:29:13 -03:00
e7d6eb6a82 Fix deletion of processes. Closes #172.
Conflicts:
	linux/LinuxProcess.c
2015-03-08 19:52:28 -03:00
a4b03e8875 Improve reading of cgroups. 2015-03-08 19:47:49 -03:00
39a725abc2 Fix deletion of processes. Closes #172. 2015-03-08 19:45:56 -03:00
0a184b769f Merge fixes 2015-02-23 03:53:36 -03:00
50000d808e Silence warnings reported in #70. 2015-02-23 03:34:06 -03:00
8b5b73825e Oops, remove test code committed by accident. 2015-02-23 01:14:20 -03:00
9780c312f4 Fix allocation of processes. Closes #166.
Conflicts:
	Process.c
	Process.h
	ProcessList.c
	ScreenManager.c
	linux/LinuxProcessList.c
2015-02-23 01:13:40 -03:00
cce2202a1f Work on fixing build of "unsupported" platform 2015-02-23 01:02:27 -03:00
5ca9b8e002 Work on fixing build of "unsupported" platform 2015-02-23 00:59:24 -03:00
6f868b00c0 Fix allocation of processes. Closes #166. 2015-02-20 14:52:10 -02:00
f97d1bc54a Merge branch 'master' into wip 2015-02-04 12:19:52 -02:00
aed9b9d18e Test if -Wextra is supported. Closes #155. 2015-02-04 12:19:04 -02:00
cc26974129 Match IO field descriptions with new labels. 2015-02-04 11:47:00 -02:00
574c3decb5 Move graph according to configured CRT_delay. 2015-02-04 11:42:54 -02:00
4a26ac05b0 Let's see if responsiveness is still good using the full CRT_delay
in ncurses. CPU usage goes down noticeably.
(Also, add missing comma, sorry.)
2015-02-04 11:41:53 -02:00
cfc773f7ee Use UTF-8 only when using UTF-8 :-) 2015-02-04 11:41:02 -02:00
6302b3e593 Make sure these are initialized. 2015-02-04 11:40:44 -02:00
d7c843c23b Complete cursor-based movement of headers. 2015-02-03 22:32:07 +01:00
14bd77c5f4 Add longer descriptions to available meters. 2015-02-03 22:31:44 +01:00
f5f29ceeb9 Make the implementation of color schemes declarative. 2015-02-03 22:30:05 +01:00
c3a7b2f134 Merge branch 'master' into wip 2015-01-23 11:23:44 -02:00
83a829a286 Remove bogus assertion. Closes #159. 2015-01-23 11:22:55 -02:00
c2108e5a48 Another mega-patch for the refactoring process.
Kinda runs, but functionality from the original main loop
is still missing. Patience.
2015-01-23 03:08:21 -02:00
3383d8e556 Sorry about the mega-patch.
This is a work-in-progress, code is currently broken.
(Some actions, and notably, the header, are missing.)
2015-01-21 23:27:31 -02:00
36b7832884 Fix initialization of cpuData structure. Closes #159. 2015-01-19 16:24:56 -02:00
c29e53c5d5 Add a stub for the battery meter. 2014-11-27 21:07:42 -02:00
d4c26a3932 Merge branch 'master' into freebsd 2014-11-27 21:05:30 -02:00
d20160cb4f Merge branch 'master' of https://github.com/hishamhm/htop 2014-11-27 21:05:20 -02:00
430c7c9a9b Move platform-dependent parts of Linux battery meter. 2014-11-27 21:04:57 -02:00
f4c49ff92e "get max pid" for FreeBSD 2014-11-27 20:18:01 -02:00
da0956d1cd Merge branch 'master' into freebsd 2014-11-27 20:10:53 -02:00
03ca7da85f Merge branch 'master' of https://github.com/hishamhm/htop 2014-11-27 20:10:42 -02:00
b4f6b11092 Move "get max pid" code into platform specific area. 2014-11-27 20:10:23 -02:00
a89758094f check /proc only if using /proc 2014-11-27 20:04:53 -02:00
6d92d7f73d Load averages for FreeBSD! 2014-11-27 20:03:29 -02:00
96e03739c4 Merge branch 'master' into freebsd 2014-11-27 19:58:35 -02:00
5578a316f0 Merge branch 'master' of https://github.com/hishamhm/htop 2014-11-27 19:58:07 -02:00
529095607c Isolate cross-platform code for load average. 2014-11-27 19:57:24 -02:00
e748401588 Build fix. 2014-11-27 19:46:01 -02:00
3ba3d6fa6f Add uptime calculation code. 2014-11-27 19:44:20 -02:00
1dd165c14b Merge branch 'master' into freebsd 2014-11-27 19:41:31 -02:00
ca03094bb2 Make UptimeMeter cross-platform again. 2014-11-27 19:41:14 -02:00
2dcdd87658 Move UptimeMeter back into cross-platform code.
We'll just isolate the platform-dependent bit.
2014-11-27 19:34:47 -02:00
a9f05c2a8b Uptime meter for FreeBSD.
This will produce too much replicated code.
I think I'll use a lighter abstraction in things like this.
2014-11-27 19:33:37 -02:00
79662291fa Merge branch 'master' into freebsd 2014-11-27 19:19:54 -02:00
357e7a3243 Move UptimeMeter into platform-dependent area.
Set up environment to move other meters.
2014-11-27 19:18:14 -02:00
b192af006f Decouple Header from Meter classes.
(Yes, that's dynamic typing!)
2014-11-27 18:38:52 -02:00
0aa485cf47 Reading swap data! 2014-11-27 18:31:39 -02:00
d8f2b5abf7 Merge branch 'master' into freebsd 2014-11-27 17:49:05 -02:00
cda6bdd56b Add ProcessList_delete to the variable interface. 2014-11-27 17:48:38 -02:00
28712f22b1 Reading first bits of data! 2014-11-27 17:44:55 -02:00
8a2e235a48 Merge branch 'master' into freebsd 2014-11-27 16:32:32 -02:00
ff4d1b466f Build fixes. 2014-11-27 16:31:42 -02:00
6afacee50d A little refactoring 2014-11-27 16:28:32 -02:00
8915b29395 Beginnings of FreeBSD port! 2014-11-27 16:27:34 -02:00
a75161f862 Get Linux "emulation" working again for our FreeBSD folks using linprocfs. 2014-11-24 20:11:33 -02:00
aaaaf063a1 Builds on Linux again! 2014-11-24 19:22:50 -02:00
26422af608 Files moved and added for supporting separate platforms. 2014-11-24 18:55:49 -02:00
eb229d9aef Changes for supporting separate platform subdirectories. 2014-11-24 18:55:03 -02:00
1eda099d06 Another portability tweak. 2014-11-19 23:27:21 -02:00
9faf4938b8 Refactored key handlers.
Made the logic more modular, hopefully easier to follow,
and removed repeated code.
Plus, some optimization in RichString code.
2014-11-19 23:17:52 -02:00
300af4b829 Minor reorganization. 2014-11-19 23:17:16 -02:00
66ec872fc6 Merge pull request #137 from Kayvlim/master
Added new color for 'D' state.
2014-10-13 23:01:37 -03:00
b2e94d626d Fixed a wrong attribution. 2014-10-14 02:48:17 +01:00
64e0d9452d Added new color for 'D' state. 2014-10-14 02:30:17 +01:00
ab0bceb550 Interpret command line PIDs as PIDs, not TIDs 2014-10-08 11:52:56 -05:00
0e8a02367e Merge branch 'master' of https://github.com/hishamhm/htop 2014-06-04 16:49:33 -03:00
eb196f8ba4 Get two data points for meters.
Ensure one mini-reading-cycle runs before getting the UI active
so we have two data points to get better meter measurements from
the get-go.
2014-06-04 16:45:55 -03:00
e4882ed9fb Merge pull request #95 from Sworddragon/master
FLT columns for the manpage
2014-05-23 22:45:54 -03:00
0fc388f2ee Added columns for the page faults in the manpage 2014-05-17 20:22:30 +02:00
0341cb3b5f Minor fixes for the manpage 2014-05-17 20:20:58 +02:00
56cf323ccf Merge pull request #93 from Sworddragon/master
Manpage update
2014-05-15 12:00:16 -03:00
afe0c8cb45 Reordered columns in the manpage 2014-05-15 10:19:37 +02:00
bbd62c1929 Added columns and added/fixed aliases in the manpage 2014-05-15 10:19:22 +02:00
9ba8e2ed05 Merge pull request #86 from stefanbeller/gitignore
Add a .gitignore file
2014-05-06 11:56:00 -03:00
4eeb9d5e64 Add a .gitignore file
This file tells git to ignore the files generated via
./autogen.sh && ./configure && make
2014-05-06 16:21:15 +02:00
8a1c304bed Merge branch 'master' of https://github.com/hishamhm/htop 2014-05-05 23:19:07 -03:00
b7de9bd0f5 Don't stop refreshing if clock was adjusted. 2014-05-05 23:18:14 -03:00
f0e2a0e771 Merge pull request #82 from yardenac/processlist
pass processlist flags to readOpenVZData (fixes build error)
2014-05-04 18:52:11 -03:00
yar
730ab0c9a8 pass processlist flags to readOpenVZData (fixes build error) 2014-05-04 09:43:45 -07:00
ff6cddfd16 Document $HTOPRC. Closes #73. 2014-05-03 18:14:08 -03:00
da16de5a86 Update changelog. 2014-05-03 18:09:31 -03:00
4939468df0 Fix crash when scrolling in a filter view with no processes. Closes #76. 2014-05-03 18:08:46 -03:00
7f92c58916 Avoid weird behavior when exiting help pressing keys such as F2. 2014-05-03 18:08:12 -03:00
f54a37b4a9 Update values for fields whose columns may appear later. Fixes #80. 2014-05-03 17:51:19 -03:00
c3e66b2d81 Make VPID use PID-width format. Closes #79. 2014-05-03 17:26:11 -03:00
ebd5de37ac Fix behavior of htop -d 1. Closes #72. 2014-04-28 17:55:52 -03:00
78d09f955a Compare with long, for 64-bit systems 2014-04-25 19:41:23 -03:00
d7e5020169 Merge branch 'master' of https://github.com/hishamhm/htop 2014-04-25 19:37:41 -03:00
ea191b53bd Fix time-based sorting 2014-04-25 19:37:07 -03:00
88c3758051 Now, when you disable a filter (F4 → Esc), it will go back to unfiltered mode
but the current process will remain selected (so you can filter, choose and
then go back to unfiltered mode to see the children of that process).

Inspired by feedback at http://www.reddit.com/r/linux/comments/23wpc7/announcing_htop_103_with_new_features_bugfixes/ch1paos
2014-04-25 16:05:25 -03:00
c8be3701b8 Fix tree view if userland threads are hidden. 2014-04-11 13:45:24 +02:00
170 changed files with 15496 additions and 5072 deletions

16
.editorconfig Normal file
View File

@ -0,0 +1,16 @@
# EditorConfig configuration for htop
# http://EditorConfig.org
# top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file, utf-8 charset
[*]
end_of_line = lf
insert_final_newline = true
charset = utf-8
# match C source and header files, set indent to three spaces
[*.{c,h}]
indent_style = space
indent_size = 3

41
.gitignore vendored Normal file
View File

@ -0,0 +1,41 @@
# the binary:
htop
# all object files
*.o
# skip all backups
*.bak
*~
.*.sw?
# skip coverage files
*.gcda
*/*.gcda
*.gcno
*/*.gcno
*.h.gch
*/.dirstamp
.deps/
Makefile
Makefile.in
aclocal.m4
autom4te.cache/
compile
config.guess
config.h
config.h.in
config.log
config.status
config.cache
config.sub
configure
depcomp
htop.1
install-sh
libtool
ltmain.sh
m4/
missing
stamp-h1

11
.travis.yml Normal file
View File

@ -0,0 +1,11 @@
language: c
compiler:
- clang
- gcc
os:
- linux
- osx
script: ./autogen.sh && ./configure && make

600
Action.c Normal file
View File

@ -0,0 +1,600 @@
/*
htop - Action.c
(C) 2015 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "config.h"
#include "Action.h"
#include "Affinity.h"
#include "AffinityPanel.h"
#include "CategoriesPanel.h"
#include "CRT.h"
#include "EnvScreen.h"
#include "MainPanel.h"
#include "OpenFilesScreen.h"
#include "Process.h"
#include "ScreenManager.h"
#include "SignalsPanel.h"
#include "StringUtils.h"
#include "TraceScreen.h"
#include "Platform.h"
#include <ctype.h>
#include <math.h>
#include <pwd.h>
#include <stdlib.h>
#include <stdbool.h>
#include <sys/param.h>
#include <sys/time.h>
/*{
#include "IncSet.h"
#include "Settings.h"
#include "Header.h"
#include "UsersTable.h"
#include "ProcessList.h"
#include "Panel.h"
typedef enum {
HTOP_OK = 0x00,
HTOP_REFRESH = 0x01,
HTOP_RECALCULATE = 0x03, // implies HTOP_REFRESH
HTOP_SAVE_SETTINGS = 0x04,
HTOP_KEEP_FOLLOWING = 0x08,
HTOP_QUIT = 0x10,
HTOP_REDRAW_BAR = 0x20,
HTOP_UPDATE_PANELHDR = 0x41, // implies HTOP_REFRESH
} Htop_Reaction;
typedef Htop_Reaction (*Htop_Action)();
typedef struct State_ {
Settings* settings;
UsersTable* ut;
ProcessList* pl;
Panel* panel;
Header* header;
} State;
}*/
Object* Action_pickFromVector(State* st, Panel* list, int x) {
Panel* panel = st->panel;
Header* header = st->header;
Settings* settings = st->settings;
int y = panel->y;
ScreenManager* scr = ScreenManager_new(0, header->height, 0, -1, HORIZONTAL, header, settings, false);
scr->allowFocusChange = false;
ScreenManager_add(scr, list, x - 1);
ScreenManager_add(scr, panel, -1);
Panel* panelFocus;
int ch;
bool unfollow = false;
int pid = MainPanel_selectedPid((MainPanel*)panel);
if (header->pl->following == -1) {
header->pl->following = pid;
unfollow = true;
}
ScreenManager_run(scr, &panelFocus, &ch);
if (unfollow) {
header->pl->following = -1;
}
ScreenManager_delete(scr);
Panel_move(panel, 0, y);
Panel_resize(panel, COLS, LINES-y-1);
if (panelFocus == list && ch == 13) {
Process* selected = (Process*)Panel_getSelected(panel);
if (selected && selected->pid == pid)
return Panel_getSelected(list);
else
beep();
}
return NULL;
}
// ----------------------------------------
static void Action_runSetup(Settings* settings, const Header* header, ProcessList* pl) {
ScreenManager* scr = ScreenManager_new(0, header->height, 0, -1, HORIZONTAL, header, settings, true);
CategoriesPanel* panelCategories = CategoriesPanel_new(scr, settings, (Header*) header, pl);
ScreenManager_add(scr, (Panel*) panelCategories, 16);
CategoriesPanel_makeMetersPage(panelCategories);
Panel* panelFocus;
int ch;
ScreenManager_run(scr, &panelFocus, &ch);
ScreenManager_delete(scr);
if (settings->changed) {
Header_writeBackToSettings(header);
}
}
static bool changePriority(MainPanel* panel, int delta) {
bool anyTagged;
bool ok = MainPanel_foreachProcess(panel, (MainPanel_ForeachProcessFn) Process_changePriorityBy, (Arg){ .i = delta }, &anyTagged);
if (!ok)
beep();
return anyTagged;
}
static void addUserToVector(int key, void* userCast, void* panelCast) {
char* user = (char*) userCast;
Panel* panel = (Panel*) panelCast;
Panel_add(panel, (Object*) ListItem_new(user, key));
}
bool Action_setUserOnly(const char* userName, uid_t* userId) {
struct passwd* user = getpwnam(userName);
if (user) {
*userId = user->pw_uid;
return true;
}
*userId = -1;
return false;
}
static void tagAllChildren(Panel* panel, Process* parent) {
parent->tag = true;
pid_t ppid = parent->pid;
for (int i = 0; i < Panel_size(panel); i++) {
Process* p = (Process*) Panel_get(panel, i);
if (!p->tag && Process_isChildOf(p, ppid)) {
tagAllChildren(panel, p);
}
}
}
static bool expandCollapse(Panel* panel) {
Process* p = (Process*) Panel_getSelected(panel);
if (!p) return false;
p->showChildren = !p->showChildren;
return true;
}
static bool collapseIntoParent(Panel* panel) {
Process* p = (Process*) Panel_getSelected(panel);
if (!p) return false;
pid_t ppid = Process_getParentPid(p);
for (int i = 0; i < Panel_size(panel); i++) {
Process* q = (Process*) Panel_get(panel, i);
if (q->pid == ppid) {
q->showChildren = false;
Panel_setSelected(panel, i);
return true;
}
}
return false;
}
Htop_Reaction Action_setSortKey(Settings* settings, ProcessField sortKey) {
settings->sortKey = sortKey;
settings->direction = 1;
settings->treeView = false;
return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_UPDATE_PANELHDR | HTOP_KEEP_FOLLOWING;
}
static Htop_Reaction sortBy(State* st) {
Htop_Reaction reaction = HTOP_OK;
Panel* sortPanel = Panel_new(0, 0, 0, 0, true, Class(ListItem), FunctionBar_newEnterEsc("Sort ", "Cancel "));
Panel_setHeader(sortPanel, "Sort by");
ProcessField* fields = st->settings->fields;
for (int i = 0; fields[i]; i++) {
char* name = String_trim(Process_fields[fields[i]].name);
Panel_add(sortPanel, (Object*) ListItem_new(name, fields[i]));
if (fields[i] == st->settings->sortKey)
Panel_setSelected(sortPanel, i);
free(name);
}
ListItem* field = (ListItem*) Action_pickFromVector(st, sortPanel, 15);
if (field) {
reaction |= Action_setSortKey(st->settings, field->key);
}
Object_delete(sortPanel);
return reaction | HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
}
// ----------------------------------------
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) {
return Action_setSortKey(st->settings, PERCENT_MEM);
}
static Htop_Reaction actionSortByCPU(State* st) {
return Action_setSortKey(st->settings, PERCENT_CPU);
}
static Htop_Reaction actionSortByTime(State* st) {
return Action_setSortKey(st->settings, TIME);
}
static Htop_Reaction actionToggleKernelThreads(State* st) {
st->settings->hideKernelThreads = !st->settings->hideKernelThreads;
return HTOP_RECALCULATE | HTOP_SAVE_SETTINGS;
}
static Htop_Reaction actionToggleUserlandThreads(State* st) {
st->settings->hideUserlandThreads = !st->settings->hideUserlandThreads;
st->settings->hideThreads = st->settings->hideUserlandThreads;
return HTOP_RECALCULATE | HTOP_SAVE_SETTINGS;
}
static Htop_Reaction actionToggleProgramPath(State* st) {
st->settings->showProgramPath = !st->settings->showProgramPath;
return HTOP_REFRESH | HTOP_SAVE_SETTINGS;
}
static Htop_Reaction actionToggleTreeView(State* st) {
st->settings->treeView = !st->settings->treeView;
if (st->settings->treeView) st->settings->direction = 1;
ProcessList_expandTree(st->pl);
return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
}
static Htop_Reaction actionIncFilter(State* st) {
IncSet* inc = ((MainPanel*)st->panel)->inc;
IncSet_activate(inc, INC_FILTER, st->panel);
st->pl->incFilter = IncSet_filter(inc);
return HTOP_REFRESH | HTOP_KEEP_FOLLOWING;
}
static Htop_Reaction actionIncSearch(State* st) {
IncSet_activate(((MainPanel*)st->panel)->inc, INC_SEARCH, st->panel);
return HTOP_REFRESH | HTOP_KEEP_FOLLOWING;
}
static Htop_Reaction actionHigherPriority(State* st) {
bool changed = changePriority((MainPanel*)st->panel, -1);
return changed ? HTOP_REFRESH : HTOP_OK;
}
static Htop_Reaction actionLowerPriority(State* st) {
bool changed = changePriority((MainPanel*)st->panel, 1);
return changed ? HTOP_REFRESH : HTOP_OK;
}
static Htop_Reaction actionInvertSortOrder(State* st) {
Settings_invertSortOrder(st->settings);
return HTOP_REFRESH | HTOP_SAVE_SETTINGS;
}
static Htop_Reaction actionSetSortColumn(State* st) {
return sortBy(st);
}
static Htop_Reaction actionExpandOrCollapse(State* st) {
bool changed = expandCollapse(st->panel);
return changed ? HTOP_RECALCULATE : HTOP_OK;
}
static Htop_Reaction actionCollapseIntoParent(State* st) {
if (!st->settings->treeView) {
return HTOP_OK;
}
bool changed = collapseIntoParent(st->panel);
return changed ? HTOP_RECALCULATE : HTOP_OK;
}
static Htop_Reaction actionExpandCollapseOrSortColumn(State* st) {
return st->settings->treeView ? actionExpandOrCollapse(st) : actionSetSortColumn(st);
}
static Htop_Reaction actionQuit() {
return HTOP_QUIT;
}
static Htop_Reaction actionSetAffinity(State* st) {
if (st->pl->cpuCount == 1)
return HTOP_OK;
#if (HAVE_LIBHWLOC || HAVE_LINUX_AFFINITY)
Panel* panel = st->panel;
Process* p = (Process*) Panel_getSelected(panel);
if (!p) return HTOP_OK;
Affinity* affinity = Affinity_get(p, st->pl);
if (!affinity) return HTOP_OK;
Panel* affinityPanel = AffinityPanel_new(st->pl, affinity);
Affinity_delete(affinity);
void* set = Action_pickFromVector(st, affinityPanel, 15);
if (set) {
Affinity* affinity = AffinityPanel_getAffinity(affinityPanel, st->pl);
bool ok = MainPanel_foreachProcess((MainPanel*)panel, (MainPanel_ForeachProcessFn) Affinity_set, (Arg){ .v = affinity }, NULL);
if (!ok) beep();
Affinity_delete(affinity);
}
Panel_delete((Object*)affinityPanel);
#endif
return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
}
static Htop_Reaction actionKill(State* st) {
Panel* signalsPanel = (Panel*) SignalsPanel_new();
ListItem* sgn = (ListItem*) Action_pickFromVector(st, signalsPanel, 15);
if (sgn) {
if (sgn->key != 0) {
Panel_setHeader(st->panel, "Sending...");
Panel_draw(st->panel, true);
refresh();
MainPanel_foreachProcess((MainPanel*)st->panel, (MainPanel_ForeachProcessFn) Process_sendSignal, (Arg){ .i = sgn->key }, NULL);
napms(500);
}
}
Panel_delete((Object*)signalsPanel);
return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
}
static Htop_Reaction actionFilterByUser(State* st) {
Panel* usersPanel = Panel_new(0, 0, 0, 0, true, Class(ListItem), FunctionBar_newEnterEsc("Show ", "Cancel "));
Panel_setHeader(usersPanel, "Show processes of:");
UsersTable_foreach(st->ut, addUserToVector, usersPanel);
Vector_insertionSort(usersPanel->items);
ListItem* allUsers = ListItem_new("All users", -1);
Panel_insert(usersPanel, 0, (Object*) allUsers);
ListItem* picked = (ListItem*) Action_pickFromVector(st, usersPanel, 20);
if (picked) {
if (picked == allUsers) {
st->pl->userId = -1;
} else {
Action_setUserOnly(ListItem_getRef(picked), &(st->pl->userId));
}
}
Panel_delete((Object*)usersPanel);
return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
}
Htop_Reaction Action_follow(State* st) {
st->pl->following = MainPanel_selectedPid((MainPanel*)st->panel);
Panel_setSelectionColor(st->panel, CRT_colors[PANEL_SELECTION_FOLLOW]);
return HTOP_KEEP_FOLLOWING;
}
static Htop_Reaction actionSetup(State* st) {
Action_runSetup(st->settings, st->header, st->pl);
// TODO: shouldn't need this, colors should be dynamic
int headerHeight = Header_calculateHeight(st->header);
Panel_move(st->panel, 0, headerHeight);
Panel_resize(st->panel, COLS, LINES-headerHeight-1);
return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
}
static Htop_Reaction actionLsof(State* st) {
Process* p = (Process*) Panel_getSelected(st->panel);
if (!p) return HTOP_OK;
OpenFilesScreen* ofs = OpenFilesScreen_new(p);
InfoScreen_run((InfoScreen*)ofs);
OpenFilesScreen_delete((Object*)ofs);
clear();
CRT_enableDelay();
return HTOP_REFRESH | HTOP_REDRAW_BAR;
}
static Htop_Reaction actionStrace(State* st) {
Process* p = (Process*) Panel_getSelected(st->panel);
if (!p) return HTOP_OK;
TraceScreen* ts = TraceScreen_new(p);
bool ok = TraceScreen_forkTracer(ts);
if (ok) {
InfoScreen_run((InfoScreen*)ts);
}
TraceScreen_delete((Object*)ts);
clear();
CRT_enableDelay();
return HTOP_REFRESH | HTOP_REDRAW_BAR;
}
static Htop_Reaction actionTag(State* st) {
Process* p = (Process*) Panel_getSelected(st->panel);
if (!p) return HTOP_OK;
Process_toggleTag(p);
Panel_onKey(st->panel, KEY_DOWN);
return HTOP_OK;
}
static Htop_Reaction actionRedraw() {
clear();
return HTOP_REFRESH | HTOP_REDRAW_BAR;
}
static const struct { const char* key; const char* info; } helpLeft[] = {
{ .key = " Arrows: ", .info = "scroll process list" },
{ .key = " Digits: ", .info = "incremental PID search" },
{ .key = " F3 /: ", .info = "incremental name search" },
{ .key = " F4 \\: ",.info = "incremental name filtering" },
{ .key = " F5 t: ", .info = "tree view" },
{ .key = " p: ", .info = "toggle program path" },
{ .key = " u: ", .info = "show processes of a single user" },
{ .key = " H: ", .info = "hide/show user process threads" },
{ .key = " K: ", .info = "hide/show kernel threads" },
{ .key = " F: ", .info = "cursor follows process" },
{ .key = " F6 + -: ", .info = "expand/collapse tree" },
{ .key = " P M T: ", .info = "sort by CPU%, MEM% or TIME" },
{ .key = " I: ", .info = "invert sort order" },
{ .key = " F6 > .: ", .info = "select sort column" },
{ .key = NULL, .info = NULL }
};
static const struct { const char* key; const char* info; } helpRight[] = {
{ .key = " Space: ", .info = "tag process" },
{ .key = " c: ", .info = "tag process and its children" },
{ .key = " U: ", .info = "untag all processes" },
{ .key = " F9 k: ", .info = "kill process/tagged processes" },
{ .key = " F7 ]: ", .info = "higher priority (root only)" },
{ .key = " F8 [: ", .info = "lower priority (+ nice)" },
#if (HAVE_LIBHWLOC || HAVE_LINUX_AFFINITY)
{ .key = " a: ", .info = "set CPU affinity" },
#endif
{ .key = " e: ", .info = "show process environment" },
{ .key = " i: ", .info = "set IO priority" },
{ .key = " l: ", .info = "list open files with lsof" },
{ .key = " s: ", .info = "trace syscalls with strace" },
{ .key = " ", .info = "" },
{ .key = " F2 C S: ", .info = "setup" },
{ .key = " F1 h: ", .info = "show this help screen" },
{ .key = " F10 q: ", .info = "quit" },
{ .key = NULL, .info = NULL }
};
static Htop_Reaction actionHelp(State* st) {
Settings* settings = st->settings;
clear();
attrset(CRT_colors[HELP_BOLD]);
for (int i = 0; i < LINES-1; i++)
mvhline(i, 0, ' ', COLS);
mvaddstr(0, 0, "htop " VERSION " - " COPYRIGHT);
mvaddstr(1, 0, "Released under the GNU GPL. See 'man' page for more info.");
attrset(CRT_colors[DEFAULT_COLOR]);
mvaddstr(3, 0, "CPU usage bar: ");
#define addattrstr(a,s) attrset(a);addstr(s)
addattrstr(CRT_colors[BAR_BORDER], "[");
if (settings->detailedCPUTime) {
addattrstr(CRT_colors[CPU_NICE_TEXT], "low"); addstr("/");
addattrstr(CRT_colors[CPU_NORMAL], "normal"); addstr("/");
addattrstr(CRT_colors[CPU_KERNEL], "kernel"); addstr("/");
addattrstr(CRT_colors[CPU_IRQ], "irq"); addstr("/");
addattrstr(CRT_colors[CPU_SOFTIRQ], "soft-irq"); addstr("/");
addattrstr(CRT_colors[CPU_STEAL], "steal"); addstr("/");
addattrstr(CRT_colors[CPU_GUEST], "guest"); addstr("/");
addattrstr(CRT_colors[CPU_IOWAIT], "io-wait");
addattrstr(CRT_colors[BAR_SHADOW], " used%");
} else {
addattrstr(CRT_colors[CPU_NICE_TEXT], "low-priority"); addstr("/");
addattrstr(CRT_colors[CPU_NORMAL], "normal"); addstr("/");
addattrstr(CRT_colors[CPU_KERNEL], "kernel"); addstr("/");
addattrstr(CRT_colors[CPU_STEAL], "virtualiz");
addattrstr(CRT_colors[BAR_SHADOW], " used%");
}
addattrstr(CRT_colors[BAR_BORDER], "]");
attrset(CRT_colors[DEFAULT_COLOR]);
mvaddstr(4, 0, "Memory bar: ");
addattrstr(CRT_colors[BAR_BORDER], "[");
addattrstr(CRT_colors[MEMORY_USED], "used"); addstr("/");
addattrstr(CRT_colors[MEMORY_BUFFERS_TEXT], "buffers"); addstr("/");
addattrstr(CRT_colors[MEMORY_CACHE], "cache");
addattrstr(CRT_colors[BAR_SHADOW], " used/total");
addattrstr(CRT_colors[BAR_BORDER], "]");
attrset(CRT_colors[DEFAULT_COLOR]);
mvaddstr(5, 0, "Swap bar: ");
addattrstr(CRT_colors[BAR_BORDER], "[");
addattrstr(CRT_colors[SWAP], "used");
addattrstr(CRT_colors[BAR_SHADOW], " used/total");
addattrstr(CRT_colors[BAR_BORDER], "]");
attrset(CRT_colors[DEFAULT_COLOR]);
mvaddstr(6,0, "Type and layout of header meters are configurable in the setup screen.");
if (CRT_colorScheme == COLORSCHEME_MONOCHROME) {
mvaddstr(7, 0, "In monochrome, meters display as different chars, in order: |#*@$%&.");
}
mvaddstr( 8, 0, " Status: R: running; S: sleeping; T: traced/stopped; Z: zombie; D: disk sleep");
for (int i = 0; helpLeft[i].info; i++) { mvaddstr(9+i, 9, helpLeft[i].info); }
for (int i = 0; helpRight[i].info; i++) { mvaddstr(9+i, 49, helpRight[i].info); }
attrset(CRT_colors[HELP_BOLD]);
for (int i = 0; helpLeft[i].key; i++) { mvaddstr(9+i, 0, helpLeft[i].key); }
for (int i = 0; helpRight[i].key; i++) { mvaddstr(9+i, 40, helpRight[i].key); }
attrset(CRT_colors[PROCESS_THREAD]);
mvaddstr(16, 32, "threads");
mvaddstr(17, 26, "threads");
attrset(CRT_colors[DEFAULT_COLOR]);
attrset(CRT_colors[HELP_BOLD]);
mvaddstr(23,0, "Press any key to return.");
attrset(CRT_colors[DEFAULT_COLOR]);
refresh();
CRT_readKey();
clear();
return HTOP_RECALCULATE | HTOP_REDRAW_BAR;
}
static Htop_Reaction actionUntagAll(State* st) {
for (int i = 0; i < Panel_size(st->panel); i++) {
Process* p = (Process*) Panel_get(st->panel, i);
p->tag = false;
}
return HTOP_REFRESH;
}
static Htop_Reaction actionTagAllChildren(State* st) {
Process* p = (Process*) Panel_getSelected(st->panel);
if (!p) return HTOP_OK;
tagAllChildren(st->panel, p);
return HTOP_OK;
}
static Htop_Reaction actionShowEnvScreen(State* st) {
Process* p = (Process*) Panel_getSelected(st->panel);
if (!p) return HTOP_OK;
EnvScreen* es = EnvScreen_new(p);
InfoScreen_run((InfoScreen*)es);
EnvScreen_delete((Object*)es);
clear();
CRT_enableDelay();
return HTOP_REFRESH | HTOP_REDRAW_BAR;
}
void Action_setBindings(Htop_Action* keys) {
keys[KEY_RESIZE] = actionResize;
keys['M'] = actionSortByMemory;
keys['T'] = actionSortByTime;
keys['P'] = actionSortByCPU;
keys['H'] = actionToggleUserlandThreads;
keys['K'] = actionToggleKernelThreads;
keys['p'] = actionToggleProgramPath;
keys['t'] = actionToggleTreeView;
keys[KEY_F(5)] = actionToggleTreeView;
keys[KEY_F(4)] = actionIncFilter;
keys['\\'] = actionIncFilter;
keys[KEY_F(3)] = actionIncSearch;
keys['/'] = actionIncSearch;
keys[']'] = actionHigherPriority;
keys[KEY_F(7)] = actionHigherPriority;
keys['['] = actionLowerPriority;
keys[KEY_F(8)] = actionLowerPriority;
keys['I'] = actionInvertSortOrder;
keys[KEY_F(6)] = actionExpandCollapseOrSortColumn;
keys[KEY_F(18)] = actionExpandCollapseOrSortColumn;
keys['<'] = actionSetSortColumn;
keys[','] = actionSetSortColumn;
keys['>'] = actionSetSortColumn;
keys['.'] = actionSetSortColumn;
keys[KEY_F(10)] = actionQuit;
keys['q'] = actionQuit;
keys['a'] = actionSetAffinity;
keys[KEY_F(9)] = actionKill;
keys['k'] = actionKill;
keys[KEY_RECLICK] = actionExpandOrCollapse;
keys['+'] = actionExpandOrCollapse;
keys['='] = actionExpandOrCollapse;
keys['-'] = actionExpandOrCollapse;
keys['\177'] = actionCollapseIntoParent;
keys['u'] = actionFilterByUser;
keys['F'] = Action_follow;
keys['S'] = actionSetup;
keys['C'] = actionSetup;
keys[KEY_F(2)] = actionSetup;
keys['l'] = actionLsof;
keys['s'] = actionStrace;
keys[' '] = actionTag;
keys['\014'] = actionRedraw; // Ctrl+L
keys[KEY_F(1)] = actionHelp;
keys['h'] = actionHelp;
keys['?'] = actionHelp;
keys['U'] = actionUntagAll;
keys['c'] = actionTagAllChildren;
keys['e'] = actionShowEnvScreen;
}

58
Action.h Normal file
View File

@ -0,0 +1,58 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_Action
#define HEADER_Action
/*
htop - Action.h
(C) 2015 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "IncSet.h"
#include "Settings.h"
#include "Header.h"
#include "UsersTable.h"
#include "ProcessList.h"
#include "Panel.h"
typedef enum {
HTOP_OK = 0x00,
HTOP_REFRESH = 0x01,
HTOP_RECALCULATE = 0x03, // implies HTOP_REFRESH
HTOP_SAVE_SETTINGS = 0x04,
HTOP_KEEP_FOLLOWING = 0x08,
HTOP_QUIT = 0x10,
HTOP_REDRAW_BAR = 0x20,
HTOP_UPDATE_PANELHDR = 0x41, // implies HTOP_REFRESH
} Htop_Reaction;
typedef Htop_Reaction (*Htop_Action)();
typedef struct State_ {
Settings* settings;
UsersTable* ut;
ProcessList* pl;
Panel* panel;
Header* header;
} State;
Object* Action_pickFromVector(State* st, Panel* list, int x);
// ----------------------------------------
bool Action_setUserOnly(const char* userName, uid_t* userId);
Htop_Reaction Action_setSortKey(Settings* settings, ProcessField sortKey);
// ----------------------------------------
Htop_Reaction Action_follow(State* st);
void Action_setBindings(Htop_Action* keys);
#endif

View File

@ -9,9 +9,23 @@ in the source distribution for its full text.
#include <stdlib.h> #include <stdlib.h>
#ifdef HAVE_LIBHWLOC
#include <hwloc.h>
#if __linux__
#define HTOP_HWLOC_CPUBIND_FLAG HWLOC_CPUBIND_THREAD
#else
#define HTOP_HWLOC_CPUBIND_FLAG HWLOC_CPUBIND_PROCESS
#endif
#elif HAVE_LINUX_AFFINITY
#include <sched.h>
#endif
/*{ /*{
#include "Process.h"
#include "ProcessList.h"
typedef struct Affinity_ { typedef struct Affinity_ {
ProcessList* pl;
int size; int size;
int used; int used;
int* cpus; int* cpus;
@ -19,10 +33,11 @@ typedef struct Affinity_ {
}*/ }*/
Affinity* Affinity_new() { Affinity* Affinity_new(ProcessList* pl) {
Affinity* this = calloc(1, sizeof(Affinity)); Affinity* this = xCalloc(1, sizeof(Affinity));
this->size = 8; this->size = 8;
this->cpus = calloc(this->size, sizeof(int)); this->cpus = xCalloc(this->size, sizeof(int));
this->pl = pl;
return this; return this;
} }
@ -34,9 +49,68 @@ void Affinity_delete(Affinity* this) {
void Affinity_add(Affinity* this, int id) { void Affinity_add(Affinity* this, int id) {
if (this->used == this->size) { if (this->used == this->size) {
this->size *= 2; this->size *= 2;
this->cpus = realloc(this->cpus, sizeof(int) * this->size); this->cpus = xRealloc(this->cpus, sizeof(int) * this->size);
} }
this->cpus[this->used] = id; this->cpus[this->used] = id;
this->used++; this->used++;
} }
#ifdef HAVE_LIBHWLOC
Affinity* Affinity_get(Process* proc, ProcessList* pl) {
hwloc_cpuset_t cpuset = hwloc_bitmap_alloc();
bool ok = (hwloc_get_proc_cpubind(pl->topology, proc->pid, cpuset, HTOP_HWLOC_CPUBIND_FLAG) == 0);
Affinity* affinity = NULL;
if (ok) {
affinity = Affinity_new(pl);
if (hwloc_bitmap_last(cpuset) == -1) {
for (int i = 0; i < pl->cpuCount; i++) {
Affinity_add(affinity, i);
}
} else {
unsigned int id;
hwloc_bitmap_foreach_begin(id, cpuset);
Affinity_add(affinity, id);
hwloc_bitmap_foreach_end();
}
}
hwloc_bitmap_free(cpuset);
return affinity;
}
bool Affinity_set(Process* proc, Affinity* this) {
hwloc_cpuset_t cpuset = hwloc_bitmap_alloc();
for (int i = 0; i < this->used; i++) {
hwloc_bitmap_set(cpuset, this->cpus[i]);
}
bool ok = (hwloc_set_proc_cpubind(this->pl->topology, proc->pid, cpuset, HTOP_HWLOC_CPUBIND_FLAG) == 0);
hwloc_bitmap_free(cpuset);
return ok;
}
#elif HAVE_LINUX_AFFINITY
Affinity* Affinity_get(Process* proc, ProcessList* pl) {
cpu_set_t cpuset;
bool ok = (sched_getaffinity(proc->pid, sizeof(cpu_set_t), &cpuset) == 0);
if (!ok) return NULL;
Affinity* affinity = Affinity_new(pl);
for (int i = 0; i < pl->cpuCount; i++) {
if (CPU_ISSET(i, &cpuset))
Affinity_add(affinity, i);
}
return affinity;
}
bool Affinity_set(Process* proc, Affinity* this) {
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
for (int i = 0; i < this->used; i++) {
CPU_SET(this->cpus[i], &cpuset);
}
bool ok = (sched_setaffinity(proc->pid, sizeof(unsigned long), &cpuset) == 0);
return ok;
}
#endif

View File

@ -9,19 +9,44 @@ Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#ifdef HAVE_LIBHWLOC
#if __linux__
#define HTOP_HWLOC_CPUBIND_FLAG HWLOC_CPUBIND_THREAD
#else
#define HTOP_HWLOC_CPUBIND_FLAG HWLOC_CPUBIND_PROCESS
#endif
#elif HAVE_LINUX_AFFINITY
#endif
#include "Process.h"
#include "ProcessList.h"
typedef struct Affinity_ { typedef struct Affinity_ {
ProcessList* pl;
int size; int size;
int used; int used;
int* cpus; int* cpus;
} Affinity; } Affinity;
Affinity* Affinity_new(); Affinity* Affinity_new(ProcessList* pl);
void Affinity_delete(Affinity* this); void Affinity_delete(Affinity* this);
void Affinity_add(Affinity* this, int id); void Affinity_add(Affinity* this, int id);
#ifdef HAVE_LIBHWLOC
Affinity* Affinity_get(Process* proc, ProcessList* pl);
bool Affinity_set(Process* proc, Affinity* this);
#elif HAVE_LINUX_AFFINITY
Affinity* Affinity_get(Process* proc, ProcessList* pl);
bool Affinity_set(Process* proc, Affinity* this);
#endif
#endif #endif

View File

@ -6,6 +6,7 @@ in the source distribution for its full text.
*/ */
#include "AffinityPanel.h" #include "AffinityPanel.h"
#include "CRT.h"
#include "CheckItem.h" #include "CheckItem.h"
@ -23,6 +24,7 @@ static HandlerResult AffinityPanel_eventHandler(Panel* this, int ch) {
CheckItem* selected = (CheckItem*) Panel_getSelected(this); CheckItem* selected = (CheckItem*) Panel_getSelected(this);
switch(ch) { switch(ch) {
case KEY_MOUSE: case KEY_MOUSE:
case KEY_RECLICK:
case ' ': case ' ':
CheckItem_set(selected, ! (CheckItem_get(selected)) ); CheckItem_set(selected, ! (CheckItem_get(selected)) );
return HANDLED; return HANDLED;
@ -43,14 +45,14 @@ PanelClass AffinityPanel_class = {
}; };
Panel* AffinityPanel_new(ProcessList* pl, Affinity* affinity) { Panel* AffinityPanel_new(ProcessList* pl, Affinity* affinity) {
Panel* this = Panel_new(1, 1, 1, 1, true, Class(CheckItem)); Panel* this = Panel_new(1, 1, 1, 1, true, Class(CheckItem), FunctionBar_newEnterEsc("Set ", "Cancel "));
Object_setClass(this, Class(AffinityPanel)); Object_setClass(this, Class(AffinityPanel));
Panel_setHeader(this, "Use CPUs:"); Panel_setHeader(this, "Use CPUs:");
int curCpu = 0; int curCpu = 0;
for (int i = 0; i < pl->cpuCount; i++) { for (int i = 0; i < pl->cpuCount; i++) {
char number[10]; char number[10];
snprintf(number, 9, "%d", ProcessList_cpuId(pl, i)); xSnprintf(number, 9, "%d", Settings_cpuId(pl->settings, i));
bool mode; bool mode;
if (curCpu < affinity->used && affinity->cpus[curCpu] == i) { if (curCpu < affinity->used && affinity->cpus[curCpu] == i) {
mode = true; mode = true;
@ -58,13 +60,13 @@ Panel* AffinityPanel_new(ProcessList* pl, Affinity* affinity) {
} else { } else {
mode = false; mode = false;
} }
Panel_add(this, (Object*) CheckItem_new(strdup(number), NULL, mode)); Panel_add(this, (Object*) CheckItem_newByVal(xStrdup(number), mode));
} }
return this; return this;
} }
Affinity* AffinityPanel_getAffinity(Panel* this) { Affinity* AffinityPanel_getAffinity(Panel* this, ProcessList* pl) {
Affinity* affinity = Affinity_new(); Affinity* affinity = Affinity_new(pl);
int size = Panel_size(this); int size = Panel_size(this);
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
if (CheckItem_get((CheckItem*)Panel_get(this, i))) if (CheckItem_get((CheckItem*)Panel_get(this, i)))

View File

@ -18,6 +18,6 @@ extern PanelClass AffinityPanel_class;
Panel* AffinityPanel_new(ProcessList* pl, Affinity* affinity); Panel* AffinityPanel_new(ProcessList* pl, Affinity* affinity);
Affinity* AffinityPanel_getAffinity(Panel* this); Affinity* AffinityPanel_getAffinity(Panel* this, ProcessList* pl);
#endif #endif

View File

@ -6,6 +6,7 @@ in the source distribution for its full text.
*/ */
#include "AvailableColumnsPanel.h" #include "AvailableColumnsPanel.h"
#include "Platform.h"
#include "Header.h" #include "Header.h"
#include "ColumnsPanel.h" #include "ColumnsPanel.h"
@ -13,22 +14,20 @@ in the source distribution for its full text.
#include <assert.h> #include <assert.h>
#include <stdlib.h> #include <stdlib.h>
#include <ctype.h> #include <ctype.h>
#include <string.h>
/*{ /*{
#include "Panel.h" #include "Panel.h"
#include "Settings.h"
#include "ScreenManager.h"
typedef struct AvailableColumnsPanel_ { typedef struct AvailableColumnsPanel_ {
Panel super; Panel super;
Panel* columns; Panel* columns;
Settings* settings;
ScreenManager* scr;
} AvailableColumnsPanel; } AvailableColumnsPanel;
}*/ }*/
static const char* const AvailableColumnsFunctions[] = {" ", " ", " ", " ", "Add ", " ", " ", " ", " ", "Done ", NULL};
static void AvailableColumnsPanel_delete(Object* object) { static void AvailableColumnsPanel_delete(Object* object) {
Panel* super = (Panel*) object; Panel* super = (Panel*) object;
AvailableColumnsPanel* this = (AvailableColumnsPanel*) object; AvailableColumnsPanel* this = (AvailableColumnsPanel*) object;
@ -38,7 +37,7 @@ static void AvailableColumnsPanel_delete(Object* object) {
static HandlerResult AvailableColumnsPanel_eventHandler(Panel* super, int ch) { static HandlerResult AvailableColumnsPanel_eventHandler(Panel* super, int ch) {
AvailableColumnsPanel* this = (AvailableColumnsPanel*) super; AvailableColumnsPanel* this = (AvailableColumnsPanel*) super;
char* text = ((ListItem*) Panel_getSelected(super))->value; int key = ((ListItem*) Panel_getSelected(super))->key;
HandlerResult result = IGNORED; HandlerResult result = IGNORED;
switch(ch) { switch(ch) {
@ -47,7 +46,7 @@ static HandlerResult AvailableColumnsPanel_eventHandler(Panel* super, int ch) {
case KEY_F(5): case KEY_F(5):
{ {
int at = Panel_getSelectedIndex(this->columns); int at = Panel_getSelectedIndex(this->columns);
Panel_insert(this->columns, at, (Object*) ListItem_new(text, 0)); Panel_insert(this->columns, at, (Object*) ListItem_new(Process_fields[key].name, key));
Panel_setSelected(this->columns, at+1); Panel_setSelected(this->columns, at+1);
ColumnsPanel_update(this->columns); ColumnsPanel_update(this->columns);
result = HANDLED; result = HANDLED;
@ -55,7 +54,7 @@ static HandlerResult AvailableColumnsPanel_eventHandler(Panel* super, int ch) {
} }
default: default:
{ {
if (isalpha(ch)) if (ch < 255 && isalpha(ch))
result = Panel_selectByTyping(super, ch); result = Panel_selectByTyping(super, ch);
break; break;
} }
@ -71,19 +70,20 @@ PanelClass AvailableColumnsPanel_class = {
.eventHandler = AvailableColumnsPanel_eventHandler .eventHandler = AvailableColumnsPanel_eventHandler
}; };
AvailableColumnsPanel* AvailableColumnsPanel_new(Settings* settings, Panel* columns, ScreenManager* scr) { AvailableColumnsPanel* AvailableColumnsPanel_new(Panel* columns) {
AvailableColumnsPanel* this = AllocThis(AvailableColumnsPanel); AvailableColumnsPanel* this = AllocThis(AvailableColumnsPanel);
Panel* super = (Panel*) this; Panel* super = (Panel*) this;
Panel_init(super, 1, 1, 1, 1, Class(ListItem), true); FunctionBar* fuBar = FunctionBar_new(AvailableColumnsFunctions, NULL, NULL);
Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar);
this->settings = settings;
this->scr = scr;
Panel_setHeader(super, "Available Columns"); Panel_setHeader(super, "Available Columns");
for (int i = 1; i < LAST_PROCESSFIELD; i++) { for (int i = 1; i < Platform_numberOfFields; i++) {
if (i != COMM) if (i != COMM && Process_fields[i].description) {
Panel_add(super, (Object*) ListItem_new(Process_fieldNames[i], 0)); char description[256];
xSnprintf(description, sizeof(description), "%s - %s", Process_fields[i].name, Process_fields[i].description);
Panel_add(super, (Object*) ListItem_new(description, i));
}
} }
this->columns = columns; this->columns = columns;
return this; return this;

View File

@ -10,20 +10,15 @@ in the source distribution for its full text.
*/ */
#include "Panel.h" #include "Panel.h"
#include "Settings.h"
#include "ScreenManager.h"
typedef struct AvailableColumnsPanel_ { typedef struct AvailableColumnsPanel_ {
Panel super; Panel super;
Panel* columns; Panel* columns;
Settings* settings;
ScreenManager* scr;
} AvailableColumnsPanel; } AvailableColumnsPanel;
extern PanelClass AvailableColumnsPanel_class; extern PanelClass AvailableColumnsPanel_class;
AvailableColumnsPanel* AvailableColumnsPanel_new(Settings* settings, Panel* columns, ScreenManager* scr); AvailableColumnsPanel* AvailableColumnsPanel_new(Panel* columns);
#endif #endif

View File

@ -6,10 +6,12 @@ in the source distribution for its full text.
*/ */
#include "AvailableMetersPanel.h" #include "AvailableMetersPanel.h"
#include "MetersPanel.h"
#include "CPUMeter.h" #include "CPUMeter.h"
#include "Header.h" #include "Header.h"
#include "ListItem.h" #include "ListItem.h"
#include "Platform.h"
#include <assert.h> #include <assert.h>
#include <stdlib.h> #include <stdlib.h>
@ -18,14 +20,16 @@ in the source distribution for its full text.
#include "Settings.h" #include "Settings.h"
#include "Panel.h" #include "Panel.h"
#include "ScreenManager.h" #include "ScreenManager.h"
#include "ProcessList.h"
typedef struct AvailableMetersPanel_ { typedef struct AvailableMetersPanel_ {
Panel super; Panel super;
ScreenManager* scr;
Settings* settings; Settings* settings;
Header* header;
Panel* leftPanel; Panel* leftPanel;
Panel* rightPanel; Panel* rightPanel;
ScreenManager* scr;
} AvailableMetersPanel; } AvailableMetersPanel;
}*/ }*/
@ -37,39 +41,48 @@ static void AvailableMetersPanel_delete(Object* object) {
free(this); free(this);
} }
static inline void AvailableMetersPanel_addHeader(Header* header, Panel* panel, MeterClass* type, int param, HeaderSide side) { static inline void AvailableMetersPanel_addMeter(Header* header, Panel* panel, MeterClass* type, int param, int column) {
Meter* meter = (Meter*) Header_addMeter(header, type, param, side); Meter* meter = (Meter*) Header_addMeterByClass(header, type, param, column);
Panel_add(panel, (Object*) Meter_toListItem(meter)); Panel_add(panel, (Object*) Meter_toListItem(meter, false));
Panel_setSelected(panel, Panel_size(panel) - 1);
MetersPanel_setMoving((MetersPanel*)panel, true);
FunctionBar_draw(panel->currentBar, NULL);
} }
static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) { static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) {
AvailableMetersPanel* this = (AvailableMetersPanel*) super; AvailableMetersPanel* this = (AvailableMetersPanel*) super;
Header* header = this->settings->header; Header* header = this->header;
ListItem* selected = (ListItem*) Panel_getSelected(super); ListItem* selected = (ListItem*) Panel_getSelected(super);
int param = selected->key & 0xff; int param = selected->key & 0xff;
int type = selected->key >> 16; int type = selected->key >> 16;
HandlerResult result = IGNORED; HandlerResult result = IGNORED;
bool update = false;
switch(ch) { switch(ch) {
case KEY_F(5): case KEY_F(5):
case 'l': case 'l':
case 'L': case 'L':
{ {
AvailableMetersPanel_addHeader(header, this->leftPanel, Meter_types[type], param, LEFT_HEADER); AvailableMetersPanel_addMeter(header, this->leftPanel, Platform_meterTypes[type], param, 0);
result = HANDLED; result = HANDLED;
update = true;
break; break;
} }
case 0x0a:
case 0x0d:
case KEY_ENTER:
case KEY_F(6): case KEY_F(6):
case 'r': case 'r':
case 'R': case 'R':
{ {
AvailableMetersPanel_addHeader(header, this->rightPanel, Meter_types[type], param, RIGHT_HEADER); AvailableMetersPanel_addMeter(header, this->rightPanel, Platform_meterTypes[type], param, 1);
result = HANDLED; result = (KEY_LEFT << 16) | SYNTH_KEY;
update = true;
break; break;
} }
} }
if (result == HANDLED) { if (update) {
this->settings->changed = true; this->settings->changed = true;
Header_calculateHeight(header); Header_calculateHeight(header);
Header_draw(header); Header_draw(header);
@ -86,30 +99,35 @@ PanelClass AvailableMetersPanel_class = {
.eventHandler = AvailableMetersPanel_eventHandler .eventHandler = AvailableMetersPanel_eventHandler
}; };
AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Panel* leftMeters, Panel* rightMeters, ScreenManager* scr) { AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* header, Panel* leftMeters, Panel* rightMeters, ScreenManager* scr, ProcessList* pl) {
AvailableMetersPanel* this = AllocThis(AvailableMetersPanel); AvailableMetersPanel* this = AllocThis(AvailableMetersPanel);
Panel* super = (Panel*) this; Panel* super = (Panel*) this;
Panel_init(super, 1, 1, 1, 1, Class(ListItem), true); FunctionBar* fuBar = FunctionBar_newEnterEsc("Add ", "Done ");
Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar);
this->settings = settings; this->settings = settings;
this->header = header;
this->leftPanel = leftMeters; this->leftPanel = leftMeters;
this->rightPanel = rightMeters; this->rightPanel = rightMeters;
this->scr = scr; this->scr = scr;
Panel_setHeader(super, "Available meters"); Panel_setHeader(super, "Available meters");
for (int i = 1; Meter_types[i]; i++) { // Platform_meterTypes[0] should be always (&CPUMeter_class), which we will
MeterClass* type = Meter_types[i]; // handle separately in the code below.
if (type != &CPUMeter_class) { for (int i = 1; Platform_meterTypes[i]; i++) {
Panel_add(super, (Object*) ListItem_new(type->uiName, i << 16)); MeterClass* type = Platform_meterTypes[i];
} assert(type != &CPUMeter_class);
const char* label = type->description ? type->description : type->uiName;
Panel_add(super, (Object*) ListItem_new(label, i << 16));
} }
// Handle (&CPUMeter_class)
MeterClass* type = &CPUMeter_class; MeterClass* type = &CPUMeter_class;
int cpus = settings->pl->cpuCount; int cpus = pl->cpuCount;
if (cpus > 1) { if (cpus > 1) {
Panel_add(super, (Object*) ListItem_new("CPU average", 0)); Panel_add(super, (Object*) ListItem_new("CPU average", 0));
for (int i = 1; i <= cpus; i++) { for (int i = 1; i <= cpus; i++) {
char buffer[50]; char buffer[50];
sprintf(buffer, "%s %d", type->uiName, i); xSnprintf(buffer, 50, "%s %d", type->uiName, i);
Panel_add(super, (Object*) ListItem_new(buffer, i)); Panel_add(super, (Object*) ListItem_new(buffer, i));
} }
} else { } else {

View File

@ -12,19 +12,21 @@ in the source distribution for its full text.
#include "Settings.h" #include "Settings.h"
#include "Panel.h" #include "Panel.h"
#include "ScreenManager.h" #include "ScreenManager.h"
#include "ProcessList.h"
typedef struct AvailableMetersPanel_ { typedef struct AvailableMetersPanel_ {
Panel super; Panel super;
ScreenManager* scr;
Settings* settings; Settings* settings;
Header* header;
Panel* leftPanel; Panel* leftPanel;
Panel* rightPanel; Panel* rightPanel;
ScreenManager* scr;
} AvailableMetersPanel; } AvailableMetersPanel;
extern PanelClass AvailableMetersPanel_class; extern PanelClass AvailableMetersPanel_class;
AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Panel* leftMeters, Panel* rightMeters, ScreenManager* scr); AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* header, Panel* leftMeters, Panel* rightMeters, ScreenManager* scr, ProcessList* pl);
#endif #endif

View File

@ -9,14 +9,14 @@ This meter written by Ian P. Hands (iphands@gmail.com, ihands@redhat.com).
#include "BatteryMeter.h" #include "BatteryMeter.h"
#include "Battery.h"
#include "ProcessList.h" #include "ProcessList.h"
#include "CRT.h" #include "CRT.h"
#include "String.h" #include "StringUtils.h"
#include "Platform.h"
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <dirent.h>
#include <unistd.h>
/*{ /*{
#include "Meter.h" #include "Meter.h"
@ -26,273 +26,22 @@ typedef enum ACPresence_ {
AC_PRESENT, AC_PRESENT,
AC_ERROR AC_ERROR
} ACPresence; } ACPresence;
}*/ }*/
int BatteryMeter_attributes[] = { int BatteryMeter_attributes[] = {
BATTERY BATTERY
}; };
static unsigned long int parseUevent(FILE * file, const char *key) { static void BatteryMeter_updateValues(Meter * this, char *buffer, int len) {
char line[100]; ACPresence isOnAC;
unsigned long int dValue = 0; double percent;
char* saveptr;
while (fgets(line, sizeof line, file)) { Battery_getData(&percent, &isOnAC);
if (strncmp(line, key, strlen(key)) == 0) {
char *value;
strtok_r(line, "=", &saveptr);
value = strtok_r(NULL, "=", &saveptr);
dValue = atoi(value);
break;
}
}
return dValue;
}
static unsigned long int parseBatInfo(const char *fileName, const unsigned short int lineNum, const unsigned short int wordNum) { if (percent == -1) {
const char batteryPath[] = PROCDIR "/acpi/battery/"; this->values[0] = 0;
DIR* batteryDir = opendir(batteryPath); xSnprintf(buffer, len, "n/a");
if (!batteryDir) return;
return 0;
#define MAX_BATTERIES 64
char* batteries[MAX_BATTERIES];
unsigned int nBatteries = 0;
memset(batteries, 0, MAX_BATTERIES * sizeof(char*));
struct dirent result;
struct dirent* dirEntry;
while (nBatteries < MAX_BATTERIES) {
int err = readdir_r(batteryDir, &result, &dirEntry);
if (err || !dirEntry)
break;
char* entryName = dirEntry->d_name;
if (strncmp(entryName, "BAT", 3))
continue;
batteries[nBatteries] = strdup(entryName);
nBatteries++;
}
closedir(batteryDir);
unsigned long int total = 0;
for (unsigned int i = 0; i < nBatteries; i++) {
char infoPath[30];
snprintf(infoPath, sizeof infoPath, "%s%s/%s", batteryPath, batteries[i], fileName);
FILE* file = fopen(infoPath, "r");
if (!file) {
break;
}
char line[50] = "";
for (unsigned short int i = 0; i < lineNum; i++) {
char* ok = fgets(line, sizeof line, file);
if (!ok) break;
}
fclose(file);
char *foundNumStr = String_getToken(line, wordNum);
const unsigned long int foundNum = atoi(foundNumStr);
free(foundNumStr);
total += foundNum;
}
for (unsigned int i = 0; i < nBatteries; i++) {
free(batteries[i]);
}
return total;
}
static ACPresence procAcpiCheck() {
ACPresence isOn = AC_ERROR;
const char *power_supplyPath = PROCDIR "/acpi/ac_adapter";
DIR *power_supplyDir = opendir(power_supplyPath);
if (!power_supplyDir) {
return AC_ERROR;
}
struct dirent result;
struct dirent* dirEntry;
for (;;) {
int err = readdir_r((DIR *) power_supplyDir, &result, &dirEntry);
if (err || !dirEntry)
break;
char* entryName = (char *) dirEntry->d_name;
if (entryName[0] != 'A')
continue;
char statePath[50];
snprintf((char *) statePath, sizeof statePath, "%s/%s/state", power_supplyPath, entryName);
FILE* file = fopen(statePath, "r");
if (!file) {
isOn = AC_ERROR;
continue;
}
char line[100];
fgets(line, sizeof line, file);
line[sizeof(line) - 1] = '\0';
fclose(file);
const char *isOnline = String_getToken(line, 2);
if (strcmp(isOnline, "on-line") == 0) {
isOn = AC_PRESENT;
} else {
isOn = AC_ABSENT;
}
free((char *) isOnline);
if (isOn == AC_PRESENT) {
break;
}
}
if (power_supplyDir)
closedir(power_supplyDir);
return isOn;
}
static ACPresence sysCheck() {
ACPresence isOn = AC_ERROR;
const char *power_supplyPath = "/sys/class/power_supply";
DIR *power_supplyDir = opendir(power_supplyPath);
if (!power_supplyDir) {
return AC_ERROR;
}
struct dirent result;
struct dirent* dirEntry;
for (;;) {
int err = readdir_r((DIR *) power_supplyDir, &result, &dirEntry);
if (err || !dirEntry)
break;
char* entryName = (char *) dirEntry->d_name;
if (strncmp(entryName, "A", 1)) {
continue;
}
char onlinePath[50];
snprintf((char *) onlinePath, sizeof onlinePath, "%s/%s/online", power_supplyPath, entryName);
FILE* file = fopen(onlinePath, "r");
if (!file) {
isOn = AC_ERROR;
} else {
isOn = (fgetc(file) - '0');
fclose(file);
if (isOn == AC_PRESENT) {
// If any AC adapter is being used then stop
break;
}
}
}
if (power_supplyDir)
closedir(power_supplyDir);
return isOn;
}
static ACPresence chkIsOnline() {
if (access(PROCDIR "/acpi/ac_adapter", F_OK) == 0) {
return procAcpiCheck();
} else if (access("/sys/class/power_supply", F_OK) == 0) {
return sysCheck();
} else {
return AC_ERROR;
}
}
static double getProcBatData() {
const unsigned long int totalFull = parseBatInfo("info", 3, 4);
if (totalFull == 0)
return 0;
const unsigned long int totalRemain = parseBatInfo("state", 5, 3);
if (totalRemain == 0)
return 0;
return totalRemain * 100.0 / (double) totalFull;
}
static double getSysBatData() {
const char *power_supplyPath = "/sys/class/power_supply/";
DIR *power_supplyDir = opendir(power_supplyPath);
if (!power_supplyDir)
return 0;
unsigned long int totalFull = 0;
unsigned long int totalRemain = 0;
struct dirent result;
struct dirent* dirEntry;
for (;;) {
int err = readdir_r((DIR *) power_supplyDir, &result, &dirEntry);
if (err || !dirEntry)
break;
char* entryName = (char *) dirEntry->d_name;
if (strncmp(entryName, "BAT", 3)) {
continue;
}
const char ueventPath[50];
snprintf((char *) ueventPath, sizeof ueventPath, "%s%s/uevent", power_supplyPath, entryName);
FILE *file;
if ((file = fopen(ueventPath, "r")) == NULL) {
closedir(power_supplyDir);
return 0;
}
if ((totalFull += parseUevent(file, "POWER_SUPPLY_ENERGY_FULL="))) {
totalRemain += parseUevent(file, "POWER_SUPPLY_ENERGY_NOW=");
} else {
//reset file pointer
if (fseek(file, 0, SEEK_SET) < 0) {
closedir(power_supplyDir);
fclose(file);
return 0;
}
}
//Some systems have it as CHARGE instead of ENERGY.
if ((totalFull += parseUevent(file, "POWER_SUPPLY_CHARGE_FULL="))) {
totalRemain += parseUevent(file, "POWER_SUPPLY_CHARGE_NOW=");
} else {
//reset file pointer
if (fseek(file, 0, SEEK_SET) < 0) {
closedir(power_supplyDir);
fclose(file);
return 0;
}
}
fclose(file);
}
const double percent = totalFull > 0 ? ((double) totalRemain * 100) / (double) totalFull : 0;
closedir(power_supplyDir);
return percent;
}
static void BatteryMeter_setValues(Meter * this, char *buffer, int len) {
double percent = getProcBatData();
if (percent == 0) {
percent = getSysBatData();
if (percent == 0) {
snprintf(buffer, len, "n/a");
return;
}
} }
this->values[0] = percent; this->values[0] = percent;
@ -308,14 +57,12 @@ static void BatteryMeter_setValues(Meter * this, char *buffer, int len) {
onBatteryText = "%.1f%%(bat)"; onBatteryText = "%.1f%%(bat)";
} }
ACPresence isOnLine = chkIsOnline(); if (isOnAC == AC_PRESENT) {
xSnprintf(buffer, len, onAcText, percent);
if (isOnLine == AC_PRESENT) { } else if (isOnAC == AC_ABSENT) {
snprintf(buffer, len, onAcText, percent); xSnprintf(buffer, len, onBatteryText, percent);
} else if (isOnLine == AC_ABSENT) {
snprintf(buffer, len, onBatteryText, percent);
} else { } else {
snprintf(buffer, len, unknownText, percent); xSnprintf(buffer, len, unknownText, percent);
} }
return; return;
@ -326,8 +73,9 @@ MeterClass BatteryMeter_class = {
.extends = Class(Meter), .extends = Class(Meter),
.delete = Meter_delete .delete = Meter_delete
}, },
.setValues = BatteryMeter_setValues, .updateValues = BatteryMeter_updateValues,
.defaultMode = TEXT_METERMODE, .defaultMode = TEXT_METERMODE,
.maxItems = 1,
.total = 100.0, .total = 100.0,
.attributes = BatteryMeter_attributes, .attributes = BatteryMeter_attributes,
.name = "Battery", .name = "Battery",

View File

@ -19,7 +19,6 @@ typedef enum ACPresence_ {
AC_ERROR AC_ERROR
} ACPresence; } ACPresence;
extern int BatteryMeter_attributes[]; extern int BatteryMeter_attributes[];
extern MeterClass BatteryMeter_class; extern MeterClass BatteryMeter_class;

56
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,56 @@
Contributing Guide
==================
Hello, and thank you so much for taking your time to contribute in any way to
htop! There are many ways to contribute, and I'll try to list them below. The
support from the free software community has been amazing over the years and
it is the number one thing that keeps me going, maintaining and improving
something that started as a tiny pet project back in 2004 and that nowadays is
a piece of software used all over the world, in both reality [and
fiction!](http://hisham.hm/htop/index.php?page=sightings). Cheers!
-- Hisham Muhammad
Bug Reports
-----------
Bug reports should be posted in the [Github issue
tracker](http://github.com/hishamhm/htop/issues). (I reply to them all, but I
usually do it in batches! :) ) Bug reports are extremely important since it's
impossible for me to test htop in every possible system, distribution and
scenario. Your feedback is what keeps the tool stable and always improving!
Thank you!
Pull Requests
-------------
Code contributions are most welcome! Just [fork the
repo](http://github.com/hishamhm/htop) and send a [pull
request](https://github.com/hishamhm/htop/pulls). Help is especially
appreciated for support of platforms other than Linux. If proposing new
features, please be mindful that htop is a system tool that needs to keep a
small footprint and perform well on systems under stress -- so unfortunately I
can't accept every new feature proposed, as I need to keep the tool slim and
maintainable. Great ideas backed by a PR are always carefully considered for
inclusion, though! Also, PRs containing bug fixes and portability tweaks are a
no-brainer, please send those in!
Feature Requests
----------------
Back when htop was hosted in SourceForge, there used to be separate Bug
Tracker and Feature Request pages. These go all lumped together under "Issues"
in Github, which is a bit confusing. For this reason, I close Feature Requests
and file them with the [`feature
request`](https://github.com/hishamhm/htop/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3A%22feature+request%22+)
label, where they remain accessible, but not mixed with actual bug reports.
This doesn't mean I'm dismissing or ignoring feature requests right away! It's
just an organizational issue (with Github, really!).
Donations
---------
If you like htop, feel free to [buy the author a
beer](http://hisham.hm/htop/index.php?page=donate). :-)

View File

@ -8,7 +8,8 @@ in the source distribution for its full text.
#include "CPUMeter.h" #include "CPUMeter.h"
#include "CRT.h" #include "CRT.h"
#include "ProcessList.h" #include "Settings.h"
#include "Platform.h"
#include <assert.h> #include <assert.h>
#include <stdlib.h> #include <stdlib.h>
@ -17,6 +18,19 @@ in the source distribution for its full text.
/*{ /*{
#include "Meter.h" #include "Meter.h"
typedef enum {
CPU_METER_NICE = 0,
CPU_METER_NORMAL = 1,
CPU_METER_KERNEL = 2,
CPU_METER_IRQ = 3,
CPU_METER_SOFTIRQ = 4,
CPU_METER_STEAL = 5,
CPU_METER_GUEST = 6,
CPU_METER_IOWAIT = 7,
CPU_METER_ITEMCOUNT = 8, // number of entries in this enum
} CPUMeterValues;
}*/ }*/
int CPUMeter_attributes[] = { int CPUMeter_attributes[] = {
@ -34,48 +48,22 @@ static void CPUMeter_init(Meter* this) {
int cpu = this->param; int cpu = this->param;
if (this->pl->cpuCount > 1) { if (this->pl->cpuCount > 1) {
char caption[10]; char caption[10];
sprintf(caption, "%-3d", ProcessList_cpuId(this->pl, cpu - 1)); xSnprintf(caption, sizeof(caption), "%-3d", Settings_cpuId(this->pl->settings, cpu - 1));
Meter_setCaption(this, caption); Meter_setCaption(this, caption);
} }
if (this->param == 0) if (this->param == 0)
Meter_setCaption(this, "Avg"); Meter_setCaption(this, "Avg");
} }
static void CPUMeter_setValues(Meter* this, char* buffer, int size) { static void CPUMeter_updateValues(Meter* this, char* buffer, int size) {
ProcessList* pl = this->pl;
int cpu = this->param; int cpu = this->param;
if (cpu > this->pl->cpuCount) { if (cpu > this->pl->cpuCount) {
snprintf(buffer, size, "absent"); xSnprintf(buffer, size, "absent");
return; return;
} }
CPUData* cpuData = &(pl->cpus[cpu]); memset(this->values, 0, sizeof(double) * CPU_METER_ITEMCOUNT);
double total = (double) ( cpuData->totalPeriod == 0 ? 1 : cpuData->totalPeriod); double percent = Platform_setCPUValues(this, cpu);
double percent; xSnprintf(buffer, size, "%5.1f%%", percent);
double* v = this->values;
v[0] = cpuData->nicePeriod / total * 100.0;
v[1] = cpuData->userPeriod / total * 100.0;
if (pl->detailedCPUTime) {
v[2] = cpuData->systemPeriod / total * 100.0;
v[3] = cpuData->irqPeriod / total * 100.0;
v[4] = cpuData->softIrqPeriod / total * 100.0;
v[5] = cpuData->stealPeriod / total * 100.0;
v[6] = cpuData->guestPeriod / total * 100.0;
v[7] = cpuData->ioWaitPeriod / total * 100.0;
Meter_setItems(this, 8);
if (pl->accountGuestInCPUMeter) {
percent = v[0]+v[1]+v[2]+v[3]+v[4]+v[5]+v[6];
} else {
percent = v[0]+v[1]+v[2]+v[3]+v[4];
}
} else {
v[2] = cpuData->systemAllPeriod / total * 100.0;
v[3] = (cpuData->stealPeriod + cpuData->guestPeriod) / total * 100.0;
Meter_setItems(this, 4);
percent = v[0]+v[1]+v[2]+v[3];
}
percent = MIN(100.0, MAX(0.0, percent));
if (isnan(percent)) percent = 0.0;
snprintf(buffer, size, "%5.1f%%", percent);
} }
static void CPUMeter_display(Object* cast, RichString* out) { static void CPUMeter_display(Object* cast, RichString* out) {
@ -86,44 +74,44 @@ static void CPUMeter_display(Object* cast, RichString* out) {
RichString_append(out, CRT_colors[METER_TEXT], "absent"); RichString_append(out, CRT_colors[METER_TEXT], "absent");
return; return;
} }
sprintf(buffer, "%5.1f%% ", this->values[1]); xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_NORMAL]);
RichString_append(out, CRT_colors[METER_TEXT], ":"); RichString_append(out, CRT_colors[METER_TEXT], ":");
RichString_append(out, CRT_colors[CPU_NORMAL], buffer); RichString_append(out, CRT_colors[CPU_NORMAL], buffer);
if (this->pl->detailedCPUTime) { if (this->pl->settings->detailedCPUTime) {
sprintf(buffer, "%5.1f%% ", this->values[2]); xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_KERNEL]);
RichString_append(out, CRT_colors[METER_TEXT], "sy:"); RichString_append(out, CRT_colors[METER_TEXT], "sy:");
RichString_append(out, CRT_colors[CPU_KERNEL], buffer); RichString_append(out, CRT_colors[CPU_KERNEL], buffer);
sprintf(buffer, "%5.1f%% ", this->values[0]); xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_NICE]);
RichString_append(out, CRT_colors[METER_TEXT], "ni:"); RichString_append(out, CRT_colors[METER_TEXT], "ni:");
RichString_append(out, CRT_colors[CPU_NICE_TEXT], buffer); RichString_append(out, CRT_colors[CPU_NICE_TEXT], buffer);
sprintf(buffer, "%5.1f%% ", this->values[3]); xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_IRQ]);
RichString_append(out, CRT_colors[METER_TEXT], "hi:"); RichString_append(out, CRT_colors[METER_TEXT], "hi:");
RichString_append(out, CRT_colors[CPU_IRQ], buffer); RichString_append(out, CRT_colors[CPU_IRQ], buffer);
sprintf(buffer, "%5.1f%% ", this->values[4]); xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_SOFTIRQ]);
RichString_append(out, CRT_colors[METER_TEXT], "si:"); RichString_append(out, CRT_colors[METER_TEXT], "si:");
RichString_append(out, CRT_colors[CPU_SOFTIRQ], buffer); RichString_append(out, CRT_colors[CPU_SOFTIRQ], buffer);
if (this->values[5]) { if (this->values[CPU_METER_STEAL]) {
sprintf(buffer, "%5.1f%% ", this->values[5]); xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_STEAL]);
RichString_append(out, CRT_colors[METER_TEXT], "st:"); RichString_append(out, CRT_colors[METER_TEXT], "st:");
RichString_append(out, CRT_colors[CPU_STEAL], buffer); RichString_append(out, CRT_colors[CPU_STEAL], buffer);
} }
if (this->values[6]) { if (this->values[CPU_METER_GUEST]) {
sprintf(buffer, "%5.1f%% ", this->values[6]); xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_GUEST]);
RichString_append(out, CRT_colors[METER_TEXT], "gu:"); RichString_append(out, CRT_colors[METER_TEXT], "gu:");
RichString_append(out, CRT_colors[CPU_GUEST], buffer); RichString_append(out, CRT_colors[CPU_GUEST], buffer);
} }
sprintf(buffer, "%5.1f%% ", this->values[7]); xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_IOWAIT]);
RichString_append(out, CRT_colors[METER_TEXT], "wa:"); RichString_append(out, CRT_colors[METER_TEXT], "wa:");
RichString_append(out, CRT_colors[CPU_IOWAIT], buffer); RichString_append(out, CRT_colors[CPU_IOWAIT], buffer);
} else { } else {
sprintf(buffer, "%5.1f%% ", this->values[2]); xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_KERNEL]);
RichString_append(out, CRT_colors[METER_TEXT], "sys:"); RichString_append(out, CRT_colors[METER_TEXT], "sys:");
RichString_append(out, CRT_colors[CPU_KERNEL], buffer); RichString_append(out, CRT_colors[CPU_KERNEL], buffer);
sprintf(buffer, "%5.1f%% ", this->values[0]); xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_NICE]);
RichString_append(out, CRT_colors[METER_TEXT], "low:"); RichString_append(out, CRT_colors[METER_TEXT], "low:");
RichString_append(out, CRT_colors[CPU_NICE_TEXT], buffer); RichString_append(out, CRT_colors[CPU_NICE_TEXT], buffer);
if (this->values[3]) { if (this->values[CPU_METER_IRQ]) {
sprintf(buffer, "%5.1f%% ", this->values[3]); xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_IRQ]);
RichString_append(out, CRT_colors[METER_TEXT], "vir:"); RichString_append(out, CRT_colors[METER_TEXT], "vir:");
RichString_append(out, CRT_colors[CPU_GUEST], buffer); RichString_append(out, CRT_colors[CPU_GUEST], buffer);
} }
@ -152,7 +140,7 @@ static void AllCPUsMeter_getRange(Meter* this, int* start, int* count) {
static void AllCPUsMeter_init(Meter* this) { static void AllCPUsMeter_init(Meter* this) {
int cpus = this->pl->cpuCount; int cpus = this->pl->cpuCount;
if (!this->drawData) if (!this->drawData)
this->drawData = calloc(cpus, sizeof(Meter*)); this->drawData = xCalloc(cpus, sizeof(Meter*));
Meter** meters = (Meter**) this->drawData; Meter** meters = (Meter**) this->drawData;
int start, count; int start, count;
AllCPUsMeter_getRange(this, &start, &count); AllCPUsMeter_getRange(this, &start, &count);
@ -196,16 +184,17 @@ static void AllCPUsMeter_updateMode(Meter* this, int mode) {
static void DualColCPUsMeter_draw(Meter* this, int x, int y, int w) { static void DualColCPUsMeter_draw(Meter* this, int x, int y, int w) {
Meter** meters = (Meter**) this->drawData; Meter** meters = (Meter**) this->drawData;
int start, count; int start, count;
int pad = this->pl->settings->headerMargin ? 2 : 0;
AllCPUsMeter_getRange(this, &start, &count); AllCPUsMeter_getRange(this, &start, &count);
int height = (count+1)/2; int height = (count+1)/2;
int startY = y; int startY = y;
for (int i = 0; i < height; i++) { for (int i = 0; i < height; i++) {
meters[i]->draw(meters[i], x, y, (w-2)/2); meters[i]->draw(meters[i], x, y, (w-pad)/2);
y += meters[i]->h; y += meters[i]->h;
} }
y = startY; y = startY;
for (int i = height; i < count; i++) { for (int i = height; i < count; i++) {
meters[i]->draw(meters[i], x+(w-1)/2+2, y, (w-2)/2); meters[i]->draw(meters[i], x+(w-1)/2+1+(pad/2), y, (w-pad)/2);
y += meters[i]->h; y += meters[i]->h;
} }
} }
@ -226,9 +215,9 @@ MeterClass CPUMeter_class = {
.delete = Meter_delete, .delete = Meter_delete,
.display = CPUMeter_display .display = CPUMeter_display
}, },
.setValues = CPUMeter_setValues, .updateValues = CPUMeter_updateValues,
.defaultMode = BAR_METERMODE, .defaultMode = BAR_METERMODE,
.maxItems = 8, .maxItems = CPU_METER_ITEMCOUNT,
.total = 100.0, .total = 100.0,
.attributes = CPUMeter_attributes, .attributes = CPUMeter_attributes,
.name = "CPU", .name = "CPU",
@ -248,6 +237,7 @@ MeterClass AllCPUsMeter_class = {
.attributes = CPUMeter_attributes, .attributes = CPUMeter_attributes,
.name = "AllCPUs", .name = "AllCPUs",
.uiName = "CPUs (1/1)", .uiName = "CPUs (1/1)",
.description = "CPUs (1/1): all CPUs",
.caption = "CPU", .caption = "CPU",
.draw = SingleColCPUsMeter_draw, .draw = SingleColCPUsMeter_draw,
.init = AllCPUsMeter_init, .init = AllCPUsMeter_init,
@ -266,6 +256,7 @@ MeterClass AllCPUs2Meter_class = {
.attributes = CPUMeter_attributes, .attributes = CPUMeter_attributes,
.name = "AllCPUs2", .name = "AllCPUs2",
.uiName = "CPUs (1&2/2)", .uiName = "CPUs (1&2/2)",
.description = "CPUs (1&2/2): all CPUs in 2 shorter columns",
.caption = "CPU", .caption = "CPU",
.draw = DualColCPUsMeter_draw, .draw = DualColCPUsMeter_draw,
.init = AllCPUsMeter_init, .init = AllCPUsMeter_init,
@ -284,6 +275,7 @@ MeterClass LeftCPUsMeter_class = {
.attributes = CPUMeter_attributes, .attributes = CPUMeter_attributes,
.name = "LeftCPUs", .name = "LeftCPUs",
.uiName = "CPUs (1/2)", .uiName = "CPUs (1/2)",
.description = "CPUs (1/2): first half of list",
.caption = "CPU", .caption = "CPU",
.draw = SingleColCPUsMeter_draw, .draw = SingleColCPUsMeter_draw,
.init = AllCPUsMeter_init, .init = AllCPUsMeter_init,
@ -302,6 +294,7 @@ MeterClass RightCPUsMeter_class = {
.attributes = CPUMeter_attributes, .attributes = CPUMeter_attributes,
.name = "RightCPUs", .name = "RightCPUs",
.uiName = "CPUs (2/2)", .uiName = "CPUs (2/2)",
.description = "CPUs (2/2): second half of list",
.caption = "CPU", .caption = "CPU",
.draw = SingleColCPUsMeter_draw, .draw = SingleColCPUsMeter_draw,
.init = AllCPUsMeter_init, .init = AllCPUsMeter_init,
@ -320,6 +313,7 @@ MeterClass LeftCPUs2Meter_class = {
.attributes = CPUMeter_attributes, .attributes = CPUMeter_attributes,
.name = "LeftCPUs2", .name = "LeftCPUs2",
.uiName = "CPUs (1&2/4)", .uiName = "CPUs (1&2/4)",
.description = "CPUs (1&2/4): first half in 2 shorter columns",
.caption = "CPU", .caption = "CPU",
.draw = DualColCPUsMeter_draw, .draw = DualColCPUsMeter_draw,
.init = AllCPUsMeter_init, .init = AllCPUsMeter_init,
@ -338,6 +332,7 @@ MeterClass RightCPUs2Meter_class = {
.attributes = CPUMeter_attributes, .attributes = CPUMeter_attributes,
.name = "RightCPUs2", .name = "RightCPUs2",
.uiName = "CPUs (3&4/4)", .uiName = "CPUs (3&4/4)",
.description = "CPUs (3&4/4): second half in 2 shorter columns",
.caption = "CPU", .caption = "CPU",
.draw = DualColCPUsMeter_draw, .draw = DualColCPUsMeter_draw,
.init = AllCPUsMeter_init, .init = AllCPUsMeter_init,

View File

@ -11,6 +11,19 @@ in the source distribution for its full text.
#include "Meter.h" #include "Meter.h"
typedef enum {
CPU_METER_NICE = 0,
CPU_METER_NORMAL = 1,
CPU_METER_KERNEL = 2,
CPU_METER_IRQ = 3,
CPU_METER_SOFTIRQ = 4,
CPU_METER_STEAL = 5,
CPU_METER_GUEST = 6,
CPU_METER_IOWAIT = 7,
CPU_METER_ITEMCOUNT = 8, // number of entries in this enum
} CPUMeterValues;
extern int CPUMeter_attributes[]; extern int CPUMeter_attributes[];
#ifndef MIN #ifndef MIN

980
CRT.c

File diff suppressed because it is too large Load Diff

98
CRT.h
View File

@ -9,17 +9,12 @@ Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#ifdef HAVE_EXECINFO_H #if HAVE_SETUID_ENABLED
#endif #endif
#define ColorPair(i,j) COLOR_PAIR((7-i)*8+j) #define ColorIndex(i,j) ((7-i)*8+j)
#define COLORSCHEME_DEFAULT 0 #define ColorPair(i,j) COLOR_PAIR(ColorIndex(i,j))
#define COLORSCHEME_MONOCHROME 1
#define COLORSCHEME_BLACKONWHITE 2
#define COLORSCHEME_BLACKONWHITE2 3
#define COLORSCHEME_MIDNIGHT 4
#define COLORSCHEME_BLACKNIGHT 5
#define Black COLOR_BLACK #define Black COLOR_BLACK
#define Red COLOR_RED #define Red COLOR_RED
@ -30,10 +25,39 @@ in the source distribution for its full text.
#define Cyan COLOR_CYAN #define Cyan COLOR_CYAN
#define White COLOR_WHITE #define White COLOR_WHITE
#define ColorPairGrayBlack ColorPair(Magenta,Magenta)
#define ColorIndexGrayBlack ColorIndex(Magenta,Magenta)
#define KEY_WHEELUP KEY_F(20)
#define KEY_WHEELDOWN KEY_F(21)
#define KEY_RECLICK KEY_F(22)
//#link curses //#link curses
#include <stdbool.h> #include <stdbool.h>
typedef enum TreeStr_ {
TREE_STR_HORZ,
TREE_STR_VERT,
TREE_STR_RTEE,
TREE_STR_BEND,
TREE_STR_TEND,
TREE_STR_OPEN,
TREE_STR_SHUT,
TREE_STR_COUNT
} TreeStr;
typedef enum ColorSchemes_ {
COLORSCHEME_DEFAULT = 0,
COLORSCHEME_MONOCHROME = 1,
COLORSCHEME_BLACKONWHITE = 2,
COLORSCHEME_LIGHTTERMINAL = 3,
COLORSCHEME_MIDNIGHT = 4,
COLORSCHEME_BLACKNIGHT = 5,
COLORSCHEME_BROKENGRAY = 6,
LAST_COLORSCHEME = 7,
} ColorSchemes;
typedef enum ColorElements_ { typedef enum ColorElements_ {
RESET_COLOR, RESET_COLOR,
DEFAULT_COLOR, DEFAULT_COLOR,
@ -42,8 +66,9 @@ typedef enum ColorElements_ {
FAILED_SEARCH, FAILED_SEARCH,
PANEL_HEADER_FOCUS, PANEL_HEADER_FOCUS,
PANEL_HEADER_UNFOCUS, PANEL_HEADER_UNFOCUS,
PANEL_HIGHLIGHT_FOCUS, PANEL_SELECTION_FOCUS,
PANEL_HIGHLIGHT_UNFOCUS, PANEL_SELECTION_FOLLOW,
PANEL_SELECTION_UNFOCUS,
LARGE_NUMBER, LARGE_NUMBER,
METER_TEXT, METER_TEXT,
METER_VALUE, METER_VALUE,
@ -58,6 +83,7 @@ typedef enum ColorElements_ {
PROCESS_MEGABYTES, PROCESS_MEGABYTES,
PROCESS_TREE, PROCESS_TREE,
PROCESS_R_STATE, PROCESS_R_STATE,
PROCESS_D_STATE,
PROCESS_BASENAME, PROCESS_BASENAME,
PROCESS_HIGH_PRIORITY, PROCESS_HIGH_PRIORITY,
PROCESS_LOW_PRIORITY, PROCESS_LOW_PRIORITY,
@ -67,13 +93,6 @@ typedef enum ColorElements_ {
BAR_SHADOW, BAR_SHADOW,
GRAPH_1, GRAPH_1,
GRAPH_2, GRAPH_2,
GRAPH_3,
GRAPH_4,
GRAPH_5,
GRAPH_6,
GRAPH_7,
GRAPH_8,
GRAPH_9,
MEMORY_USED, MEMORY_USED,
MEMORY_BUFFERS, MEMORY_BUFFERS,
MEMORY_BUFFERS_TEXT, MEMORY_BUFFERS_TEXT,
@ -102,23 +121,62 @@ typedef enum ColorElements_ {
void CRT_fatalError(const char* note) __attribute__ ((noreturn)); void CRT_fatalError(const char* note) __attribute__ ((noreturn));
void CRT_handleSIGSEGV(int sgn);
// TODO: centralize these in Settings. #define KEY_ALT(x) (KEY_F(64 - 26) + (x - 'A'))
extern int CRT_colorScheme;
extern const char *CRT_treeStrAscii[TREE_STR_COUNT];
#ifdef HAVE_LIBNCURSESW
extern const char *CRT_treeStrUtf8[TREE_STR_COUNT];
extern bool CRT_utf8; extern bool CRT_utf8;
extern int CRT_colors[LAST_COLORELEMENT]; #endif
extern const char **CRT_treeStr;
extern int CRT_delay;
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;
char* CRT_termType; char* CRT_termType;
// TODO move color scheme to Settings, perhaps?
extern int CRT_colorScheme;
void *backtraceArray[128]; void *backtraceArray[128];
#if HAVE_SETUID_ENABLED
#define DIE(msg) do { CRT_done(); fprintf(stderr, msg); exit(1); } while(0)
void CRT_dropPrivileges();
void CRT_restorePrivileges();
#else
/* Turn setuid operations into NOPs */
#ifndef CRT_dropPrivileges
#define CRT_dropPrivileges()
#define CRT_restorePrivileges()
#endif
#endif
// TODO: pass an instance of Settings instead. // TODO: pass an instance of Settings instead.
void CRT_init(int delay, int colorScheme); void CRT_init(int delay, int colorScheme);

View File

@ -21,27 +21,20 @@ in the source distribution for its full text.
#include "Panel.h" #include "Panel.h"
#include "Settings.h" #include "Settings.h"
#include "ScreenManager.h" #include "ScreenManager.h"
#include "ProcessList.h"
typedef struct CategoriesPanel_ { typedef struct CategoriesPanel_ {
Panel super; Panel super;
ScreenManager* scr;
Settings* settings; Settings* settings;
ScreenManager* scr; Header* header;
ProcessList* pl;
} CategoriesPanel; } CategoriesPanel;
}*/ }*/
static const char* MetersFunctions[] = {" ", " ", " ", "Type ", " ", " ", "MoveUp", "MoveDn", "Remove", "Done ", NULL}; static const char* const CategoriesFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL};
static const char* AvailableMetersFunctions[] = {" ", " ", " ", " ", "Add L ", "Add R ", " ", " ", " ", "Done ", NULL};
static const char* DisplayOptionsFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL};
static const char* ColumnsFunctions[] = {" ", " ", " ", " ", " ", " ", "MoveUp", "MoveDn", "Remove", "Done ", NULL};
static const char* ColorsFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL};
static const char* AvailableColumnsFunctions[] = {" ", " ", " ", " ", "Add ", " ", " ", " ", " ", "Done ", NULL};
static void CategoriesPanel_delete(Object* object) { static void CategoriesPanel_delete(Object* object) {
Panel* super = (Panel*) object; Panel* super = (Panel*) object;
@ -51,29 +44,31 @@ static void CategoriesPanel_delete(Object* object) {
} }
void CategoriesPanel_makeMetersPage(CategoriesPanel* this) { void CategoriesPanel_makeMetersPage(CategoriesPanel* this) {
Panel* leftMeters = (Panel*) MetersPanel_new(this->settings, "Left column", this->settings->header->leftMeters, this->scr); MetersPanel* leftMeters = MetersPanel_new(this->settings, "Left column", this->header->columns[0], this->scr);
Panel* rightMeters = (Panel*) MetersPanel_new(this->settings, "Right column", this->settings->header->rightMeters, this->scr); MetersPanel* rightMeters = MetersPanel_new(this->settings, "Right column", this->header->columns[1], this->scr);
Panel* availableMeters = (Panel*) AvailableMetersPanel_new(this->settings, leftMeters, rightMeters, this->scr); leftMeters->rightNeighbor = rightMeters;
ScreenManager_add(this->scr, leftMeters, FunctionBar_new(MetersFunctions, NULL, NULL), 20); rightMeters->leftNeighbor = leftMeters;
ScreenManager_add(this->scr, rightMeters, FunctionBar_new(MetersFunctions, NULL, NULL), 20); Panel* availableMeters = (Panel*) AvailableMetersPanel_new(this->settings, this->header, (Panel*) leftMeters, (Panel*) rightMeters, this->scr, this->pl);
ScreenManager_add(this->scr, availableMeters, FunctionBar_new(AvailableMetersFunctions, NULL, NULL), -1); ScreenManager_add(this->scr, (Panel*) leftMeters, 20);
ScreenManager_add(this->scr, (Panel*) rightMeters, 20);
ScreenManager_add(this->scr, availableMeters, -1);
} }
static void CategoriesPanel_makeDisplayOptionsPage(CategoriesPanel* this) { static void CategoriesPanel_makeDisplayOptionsPage(CategoriesPanel* this) {
Panel* displayOptions = (Panel*) DisplayOptionsPanel_new(this->settings, this->scr); Panel* displayOptions = (Panel*) DisplayOptionsPanel_new(this->settings, this->scr);
ScreenManager_add(this->scr, displayOptions, FunctionBar_new(DisplayOptionsFunctions, NULL, NULL), -1); ScreenManager_add(this->scr, displayOptions, -1);
} }
static void CategoriesPanel_makeColorsPage(CategoriesPanel* this) { static void CategoriesPanel_makeColorsPage(CategoriesPanel* this) {
Panel* colors = (Panel*) ColorsPanel_new(this->settings, this->scr); Panel* colors = (Panel*) ColorsPanel_new(this->settings, this->scr);
ScreenManager_add(this->scr, colors, FunctionBar_new(ColorsFunctions, NULL, NULL), -1); ScreenManager_add(this->scr, colors, -1);
} }
static void CategoriesPanel_makeColumnsPage(CategoriesPanel* this) { static void CategoriesPanel_makeColumnsPage(CategoriesPanel* this) {
Panel* columns = (Panel*) ColumnsPanel_new(this->settings, this->scr); Panel* columns = (Panel*) ColumnsPanel_new(this->settings);
Panel* availableColumns = (Panel*) AvailableColumnsPanel_new(this->settings, columns, this->scr); Panel* availableColumns = (Panel*) AvailableColumnsPanel_new(columns);
ScreenManager_add(this->scr, columns, FunctionBar_new(ColumnsFunctions, NULL, NULL), 20); ScreenManager_add(this->scr, columns, 20);
ScreenManager_add(this->scr, availableColumns, FunctionBar_new(AvailableColumnsFunctions, NULL, NULL), -1); ScreenManager_add(this->scr, availableColumns, -1);
} }
static HandlerResult CategoriesPanel_eventHandler(Panel* super, int ch) { static HandlerResult CategoriesPanel_eventHandler(Panel* super, int ch) {
@ -83,13 +78,13 @@ static HandlerResult CategoriesPanel_eventHandler(Panel* super, int ch) {
int selected = Panel_getSelectedIndex(super); int selected = Panel_getSelectedIndex(super);
switch (ch) { switch (ch) {
case EVENT_SETSELECTED: case EVENT_SET_SELECTED:
result = HANDLED; result = HANDLED;
break; break;
case KEY_UP: case KEY_UP:
case KEY_CTRLP: case KEY_CTRL('P'):
case KEY_DOWN: case KEY_DOWN:
case KEY_CTRLN: case KEY_CTRL('N'):
case KEY_NPAGE: case KEY_NPAGE:
case KEY_PPAGE: case KEY_PPAGE:
case KEY_HOME: case KEY_HOME:
@ -102,13 +97,12 @@ static HandlerResult CategoriesPanel_eventHandler(Panel* super, int ch) {
break; break;
} }
default: default:
if (isalpha(ch)) if (ch < 255 && isalpha(ch))
result = Panel_selectByTyping(super, ch); result = Panel_selectByTyping(super, ch);
if (result == BREAK_LOOP) if (result == BREAK_LOOP)
result = IGNORED; result = IGNORED;
break; break;
} }
if (result == HANDLED) { if (result == HANDLED) {
int size = ScreenManager_size(this->scr); int size = ScreenManager_size(this->scr);
for (int i = 1; i < size; i++) for (int i = 1; i < size; i++)
@ -128,7 +122,6 @@ static HandlerResult CategoriesPanel_eventHandler(Panel* super, int ch) {
break; break;
} }
} }
return result; return result;
} }
@ -140,13 +133,16 @@ PanelClass CategoriesPanel_class = {
.eventHandler = CategoriesPanel_eventHandler .eventHandler = CategoriesPanel_eventHandler
}; };
CategoriesPanel* CategoriesPanel_new(Settings* settings, ScreenManager* scr) { CategoriesPanel* CategoriesPanel_new(ScreenManager* scr, Settings* settings, Header* header, ProcessList* pl) {
CategoriesPanel* this = AllocThis(CategoriesPanel); CategoriesPanel* this = AllocThis(CategoriesPanel);
Panel* super = (Panel*) this; Panel* super = (Panel*) this;
Panel_init(super, 1, 1, 1, 1, Class(ListItem), true); FunctionBar* fuBar = FunctionBar_new(CategoriesFunctions, NULL, NULL);
Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar);
this->settings = settings;
this->scr = scr; this->scr = scr;
this->settings = settings;
this->header = header;
this->pl = pl;
Panel_setHeader(super, "Setup"); Panel_setHeader(super, "Setup");
Panel_add(super, (Object*) ListItem_new("Meters", 0)); Panel_add(super, (Object*) ListItem_new("Meters", 0));
Panel_add(super, (Object*) ListItem_new("Display options", 0)); Panel_add(super, (Object*) ListItem_new("Display options", 0));

View File

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

129
ChangeLog
View File

@ -1,3 +1,132 @@
What's new in version 2.2.0
* Solaris/Illumos/OpenIndiana support
(thanks to Guy M. Broome)
* -t/--tree flag for starting in tree-view mode
(thanks to Daniel Flanagan)
* macOS: detects High Sierra version to avoid OS bug
(thanks to Pierre Malhaire)
* OpenBSD: read battery data
(thanks to @nerd972)
* Various automake and build improvements
(thanks to Kang-Che Sung)
* Check for pkg-config when building with --enable-delayacct
(thanks to @florian2833z for the report)
* Avoid some bashisms in configure script
(thanks to Jesin)
* Use CFLAGS from ncurses*-config if present
(thanks to Michael Klein)
* Header generator supports non-UTF-8 environments
(thanks to @volkov-am)
* Linux: changed detection of kernel threads
* Collapse current subtree pressing Backspace
* BUGFIX: fix behavior of SYSCR column
(thanks to Marc Kleine-Budde)
* BUGFIX: obtain exit code of lsof correctly
(thanks to @wangqr)
* BUGFIX: fix crash with particular keycodes
(thanks to Wellington Torrejais da Silva for the report)
* BUGFIX: fix issue with small terminals
(thanks to Daniel Elf for the report)
* BUGFIX: fix terminal color issues
(thanks to Kang-Che Sung for the report)
* BUGFIX: preserve LDFLAGS when building
(thanks to Lance Frederickson for the report)
* BUGFIX: fixed overflow for systems with >= 100 signals
What's new in version 2.1.0
* Linux: Delay accounting metrics
(thanks to André Carvalho)
* DragonFlyBSD support
(thanks to Diederik de Groot)
* Support for real-time signals
(thanks to Kang-Che Sung)
* 'c' key now works with threads as well
* Session column renamed from SESN to SID
(thanks to Kamyar Rasta)
* Improved UI for meter style selection
(thanks to Kang-Che Sung)
* Improved code for constructing process tree
(thanks to wangqr)
* Compile-time option to disable setuid
* Error checking of various standard library operations
* Replacement of sprintf with snprintf
(thanks to Tomasz Kramkowski)
* Linux: performance improvements in battery meter
* Linux: update process TTY device
* Linux: add support for sorting TASK_IDLE
(thanks to Vladimir Panteleev)
* Linux: add upper-bound to running process counter
(thanks to Lucas Correia Villa Real)
* BUGFIX: avoid crash when battery is removed
(thanks to Jan Chren)
* BUGFIX: macOS: fix infinite loop in tree view
(thanks to Wataru Ashihara)
What's new in version 2.0.2
* Mac OS X: stop trying when task_for_pid fails for a process,
stops spamming logs with errors.
* Add Ctrl+A and Ctrl+E to go to beginning and end of line
* FreeBSD: fixes for CPU calculation
(thanks to Tim Creech, Andy Pilate)
* Usability: auto-follow process after a search.
* Use Linux backend on GNU Hurd
* Improvement for reproducible builds.
* BUGFIX: Fix behavior of Alt-key combinations
(thanks to Kang-Che Sung)
* Various code tweaks and cleanups
(thanks to Kang-Che Sung)
What's new in version 2.0.1
* OpenBSD: Various fixes and improvements
(thanks to Michael McConville and Juan Francisco Cantero Hurtado)
* FreeBSD: fix CPU and memory readings
(thanks to Tim Creech, Hung-Yi Chen, Bernard Spil, Greg V)
* FreeBSD: add battery support
(thanks to Greg V)
* Linux: Retain last-obtained name of a zombie process
* Mac OS X: Improve portability for OS X versions
(thanks to Michael Klein)
* Mac OS X: Fix reading command-line arguments and basename
* Mac OS X: Fix process state information
* Mac OS X: Fix tree view collapsing/expanding
* Mac OS X: Fix tree organization
* Mac OS X: Fix memory accounting
* Fix crash when emptying a column of meters
* Make Esc key more responsive
What's new in version 2.0.0
* Platform abstraction layer
* Initial FreeBSD support
* Initial Mac OS X support
(thanks to David Hunt)
* Swap meter for Mac OSX
(thanks to Ștefan Rusu)
* OpenBSD port
(thanks to Michael McConville)
* FreeBSD support improvements
(thanks to Martin Misuth)
* Support for NCurses 6 ABI, including mouse wheel support
* Much improved mouse responsiveness
* Process environment variables screen
(thanks to Michael Klein)
* Higher-resolution UTF-8 based Graph mode
(Thanks to James Hall from vtop for the idea!)
* Show program path settings
(thanks to Tobias Geerinckx-Rice)
* BUGFIX: Fix crash when scrolling an empty filtered list.
* Use dynamic units for text display, and several fixes
(thanks to Christian Hesse)
* BUGFIX: fix error caused by overflow in usertime calculation.
(thanks to Patrick Marlier)
* Catch all memory allocation errors
(thanks to Michael McConville for the push)
* Several tweaks and bugfixes
(See the Git log for details and contributors!)
What's new in version 1.0.3 What's new in version 1.0.3

View File

@ -49,11 +49,19 @@ ObjectClass CheckItem_class = {
.delete = CheckItem_delete .delete = CheckItem_delete
}; };
CheckItem* CheckItem_new(char* text, bool* ref, bool value) { CheckItem* CheckItem_newByRef(char* text, bool* ref) {
CheckItem* this = AllocThis(CheckItem);
this->text = text;
this->value = false;
this->ref = ref;
return this;
}
CheckItem* CheckItem_newByVal(char* text, bool value) {
CheckItem* this = AllocThis(CheckItem); CheckItem* this = AllocThis(CheckItem);
this->text = text; this->text = text;
this->value = value; this->value = value;
this->ref = ref; this->ref = NULL;
return this; return this;
} }

View File

@ -21,7 +21,9 @@ typedef struct CheckItem_ {
extern ObjectClass CheckItem_class; extern ObjectClass CheckItem_class;
CheckItem* CheckItem_new(char* text, bool* ref, bool value); CheckItem* CheckItem_newByRef(char* text, bool* ref);
CheckItem* CheckItem_newByVal(char* text, bool value);
void CheckItem_set(CheckItem* this, bool value); void CheckItem_set(CheckItem* this, bool value);

View File

@ -19,7 +19,7 @@ int ClockMeter_attributes[] = {
CLOCK CLOCK
}; };
static void ClockMeter_setValues(Meter* this, char* buffer, int size) { static void ClockMeter_updateValues(Meter* this, char* buffer, int size) {
time_t t = time(NULL); time_t t = time(NULL);
struct tm result; struct tm result;
struct tm *lt = localtime_r(&t, &result); struct tm *lt = localtime_r(&t, &result);
@ -32,9 +32,10 @@ MeterClass ClockMeter_class = {
.extends = Class(Meter), .extends = Class(Meter),
.delete = Meter_delete .delete = Meter_delete
}, },
.setValues = ClockMeter_setValues, .updateValues = ClockMeter_updateValues,
.defaultMode = TEXT_METERMODE, .defaultMode = TEXT_METERMODE,
.total = 100.0, .maxItems = 1,
.total = 1440, /* 24*60 */
.attributes = ClockMeter_attributes, .attributes = ClockMeter_attributes,
.name = "Clock", .name = "Clock",
.uiName = "Clock", .uiName = "Clock",

View File

@ -16,7 +16,7 @@ in the source distribution for its full text.
// TO ADD A NEW SCHEME: // TO ADD A NEW SCHEME:
// * Increment the size of bool check in ColorsPanel.h // * Increment the size of bool check in ColorsPanel.h
// * Add the entry in the ColorSchemes array below in the file // * Add the entry in the ColorSchemeNames array below in the file
// * Add a define in CRT.h that matches the order of the array // * Add a define in CRT.h that matches the order of the array
// * Add the colors in CRT_setColors // * Add the colors in CRT_setColors
@ -34,13 +34,16 @@ typedef struct ColorsPanel_ {
}*/ }*/
static const char* ColorSchemes[] = { static const char* const ColorsFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL};
static const char* const ColorSchemeNames[] = {
"Default", "Default",
"Monochromatic", "Monochromatic",
"Black on White", "Black on White",
"Light Terminal", "Light Terminal",
"MC", "MC",
"Black Night", "Black Night",
"Broken Gray",
NULL NULL
}; };
@ -62,8 +65,9 @@ static HandlerResult ColorsPanel_eventHandler(Panel* super, int ch) {
case 0x0d: case 0x0d:
case KEY_ENTER: case KEY_ENTER:
case KEY_MOUSE: case KEY_MOUSE:
case KEY_RECLICK:
case ' ': case ' ':
for (int i = 0; ColorSchemes[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;
@ -72,8 +76,9 @@ static HandlerResult ColorsPanel_eventHandler(Panel* super, int ch) {
if (result == HANDLED) { if (result == HANDLED) {
this->settings->changed = true; this->settings->changed = true;
Header* header = this->settings->header; const Header* header = this->scr->header;
CRT_setColors(mark); CRT_setColors(mark);
clear();
Panel* menu = (Panel*) Vector_get(this->scr->panels, 0); Panel* menu = (Panel*) Vector_get(this->scr->panels, 0);
Header_draw(header); Header_draw(header);
RichString_setAttr(&(super->header), CRT_colors[PANEL_HEADER_FOCUS]); RichString_setAttr(&(super->header), CRT_colors[PANEL_HEADER_FOCUS]);
@ -94,14 +99,15 @@ PanelClass ColorsPanel_class = {
ColorsPanel* ColorsPanel_new(Settings* settings, ScreenManager* scr) { ColorsPanel* ColorsPanel_new(Settings* settings, ScreenManager* scr) {
ColorsPanel* this = AllocThis(ColorsPanel); ColorsPanel* this = AllocThis(ColorsPanel);
Panel* super = (Panel*) this; Panel* super = (Panel*) this;
Panel_init(super, 1, 1, 1, 1, Class(CheckItem), true); FunctionBar* fuBar = FunctionBar_new(ColorsFunctions, NULL, NULL);
Panel_init(super, 1, 1, 1, 1, Class(CheckItem), true, fuBar);
this->settings = settings; this->settings = settings;
this->scr = scr; this->scr = scr;
Panel_setHeader(super, "Colors"); Panel_setHeader(super, "Colors");
for (int i = 0; ColorSchemes[i] != NULL; i++) { for (int i = 0; ColorSchemeNames[i] != NULL; i++) {
Panel_add(super, (Object*) CheckItem_new(strdup(ColorSchemes[i]), NULL, false)); Panel_add(super, (Object*) CheckItem_newByVal(xStrdup(ColorSchemeNames[i]), false));
} }
CheckItem_set((CheckItem*)Panel_get(super, settings->colorScheme), true); CheckItem_set((CheckItem*)Panel_get(super, settings->colorScheme), true);
return this; return this;

View File

@ -11,7 +11,7 @@ in the source distribution for its full text.
// TO ADD A NEW SCHEME: // TO ADD A NEW SCHEME:
// * Increment the size of bool check in ColorsPanel.h // * Increment the size of bool check in ColorsPanel.h
// * Add the entry in the ColorSchemes array below in the file // * Add the entry in the ColorSchemeNames array below in the file
// * Add a define in CRT.h that matches the order of the array // * Add a define in CRT.h that matches the order of the array
// * Add the colors in CRT_setColors // * Add the colors in CRT_setColors

View File

@ -6,8 +6,11 @@ in the source distribution for its full text.
*/ */
#include "ColumnsPanel.h" #include "ColumnsPanel.h"
#include "Platform.h"
#include "String.h" #include "StringUtils.h"
#include "ListItem.h"
#include "CRT.h"
#include <assert.h> #include <assert.h>
#include <stdlib.h> #include <stdlib.h>
@ -16,17 +19,18 @@ in the source distribution for its full text.
/*{ /*{
#include "Panel.h" #include "Panel.h"
#include "Settings.h" #include "Settings.h"
#include "ScreenManager.h"
typedef struct ColumnsPanel_ { typedef struct ColumnsPanel_ {
Panel super; Panel super;
Settings* settings; Settings* settings;
ScreenManager* scr; bool moving;
} ColumnsPanel; } ColumnsPanel;
}*/ }*/
static const char* const ColumnsFunctions[] = {" ", " ", " ", " ", " ", " ", "MoveUp", "MoveDn", "Remove", "Done ", NULL};
static void ColumnsPanel_delete(Object* object) { static void ColumnsPanel_delete(Object* object) {
Panel* super = (Panel*) object; Panel* super = (Panel*) object;
ColumnsPanel* this = (ColumnsPanel*) object; ColumnsPanel* this = (ColumnsPanel*) object;
@ -35,12 +39,34 @@ static void ColumnsPanel_delete(Object* object) {
} }
static HandlerResult ColumnsPanel_eventHandler(Panel* super, int ch) { static HandlerResult ColumnsPanel_eventHandler(Panel* super, int ch) {
ColumnsPanel* const this = (ColumnsPanel*) super;
int selected = Panel_getSelectedIndex(super); int selected = Panel_getSelectedIndex(super);
HandlerResult result = IGNORED; HandlerResult result = IGNORED;
int size = Panel_size(super); int size = Panel_size(super);
switch(ch) { switch(ch) {
case 0x0a:
case 0x0d:
case KEY_ENTER:
case KEY_MOUSE:
case KEY_RECLICK:
{
if (selected < size - 1) {
this->moving = !(this->moving);
Panel_setSelectionColor(super, this->moving ? CRT_colors[PANEL_SELECTION_FOLLOW] : CRT_colors[PANEL_SELECTION_FOCUS]);
((ListItem*)Panel_getSelected(super))->moving = this->moving;
result = HANDLED;
}
break;
}
case KEY_UP:
{
if (!this->moving) {
break;
}
}
/* else fallthrough */
case KEY_F(7): case KEY_F(7):
case '[': case '[':
case '-': case '-':
@ -50,6 +76,13 @@ static HandlerResult ColumnsPanel_eventHandler(Panel* super, int ch) {
result = HANDLED; result = HANDLED;
break; break;
} }
case KEY_DOWN:
{
if (!this->moving) {
break;
}
}
/* else fallthrough */
case KEY_F(8): case KEY_F(8):
case ']': case ']':
case '+': case '+':
@ -70,7 +103,7 @@ static HandlerResult ColumnsPanel_eventHandler(Panel* super, int ch) {
} }
default: default:
{ {
if (isalpha(ch)) if (ch < 255 && isalpha(ch))
result = Panel_selectByTyping(super, ch); result = Panel_selectByTyping(super, ch);
if (result == BREAK_LOOP) if (result == BREAK_LOOP)
result = IGNORED; result = IGNORED;
@ -90,47 +123,45 @@ PanelClass ColumnsPanel_class = {
.eventHandler = ColumnsPanel_eventHandler .eventHandler = ColumnsPanel_eventHandler
}; };
ColumnsPanel* ColumnsPanel_new(Settings* settings, ScreenManager* scr) { ColumnsPanel* ColumnsPanel_new(Settings* settings) {
ColumnsPanel* this = AllocThis(ColumnsPanel); ColumnsPanel* this = AllocThis(ColumnsPanel);
Panel* super = (Panel*) this; Panel* super = (Panel*) this;
Panel_init(super, 1, 1, 1, 1, Class(ListItem), true); FunctionBar* fuBar = FunctionBar_new(ColumnsFunctions, NULL, NULL);
Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar);
this->settings = settings; this->settings = settings;
this->scr = scr; this->moving = false;
Panel_setHeader(super, "Active Columns"); Panel_setHeader(super, "Active Columns");
ProcessField* fields = this->settings->pl->fields; ProcessField* fields = this->settings->fields;
for (; *fields; fields++) { for (; *fields; fields++) {
Panel_add(super, (Object*) ListItem_new(Process_fieldNames[*fields], 0)); if (Process_fields[*fields].name) {
Panel_add(super, (Object*) ListItem_new(Process_fields[*fields].name, *fields));
}
} }
return this; return this;
} }
int ColumnsPanel_fieldNameToIndex(const char* name) { int ColumnsPanel_fieldNameToIndex(const char* name) {
for (int j = 1; j <= LAST_PROCESSFIELD; j++) { for (int j = 1; j <= Platform_numberOfFields; j++) {
if (String_eq(name, Process_fieldNames[j])) { if (String_eq(name, Process_fields[j].name)) {
return j; return j;
} }
} }
return 0; return -1;
} }
void ColumnsPanel_update(Panel* super) { void ColumnsPanel_update(Panel* super) {
ColumnsPanel* this = (ColumnsPanel*) super; ColumnsPanel* this = (ColumnsPanel*) super;
int size = Panel_size(super); int size = Panel_size(super);
this->settings->changed = true; this->settings->changed = true;
// FIXME: this is crappily inefficient this->settings->fields = xRealloc(this->settings->fields, sizeof(ProcessField) * (size+1));
free(this->settings->pl->fields); this->settings->flags = 0;
this->settings->pl->fields = (ProcessField*) malloc(sizeof(ProcessField) * (size+1));
this->settings->pl->flags = 0;
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
char* text = ((ListItem*) Panel_get(super, i))->value; int key = ((ListItem*) Panel_get(super, i))->key;
int j = ColumnsPanel_fieldNameToIndex(text); this->settings->fields[i] = key;
if (j > 0) { this->settings->flags |= Process_fields[key].flags;
this->settings->pl->fields[i] = j;
this->settings->pl->flags |= Process_fieldFlags[j];
}
} }
this->settings->pl->fields[size] = 0; this->settings->fields[size] = 0;
} }

View File

@ -11,19 +11,18 @@ in the source distribution for its full text.
#include "Panel.h" #include "Panel.h"
#include "Settings.h" #include "Settings.h"
#include "ScreenManager.h"
typedef struct ColumnsPanel_ { typedef struct ColumnsPanel_ {
Panel super; Panel super;
Settings* settings; Settings* settings;
ScreenManager* scr; bool moving;
} ColumnsPanel; } ColumnsPanel;
extern PanelClass ColumnsPanel_class; extern PanelClass ColumnsPanel_class;
ColumnsPanel* ColumnsPanel_new(Settings* settings, ScreenManager* scr); ColumnsPanel* ColumnsPanel_new(Settings* settings);
int ColumnsPanel_fieldNameToIndex(const char* name); int ColumnsPanel_fieldNameToIndex(const char* name);

View File

@ -8,6 +8,7 @@ in the source distribution for its full text.
#include "DisplayOptionsPanel.h" #include "DisplayOptionsPanel.h"
#include "CheckItem.h" #include "CheckItem.h"
#include "CRT.h"
#include <assert.h> #include <assert.h>
#include <stdlib.h> #include <stdlib.h>
@ -27,6 +28,8 @@ typedef struct DisplayOptionsPanel_ {
}*/ }*/
static const char* const DisplayOptionsFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL};
static void DisplayOptionsPanel_delete(Object* object) { static void DisplayOptionsPanel_delete(Object* object) {
Panel* super = (Panel*) object; Panel* super = (Panel*) object;
DisplayOptionsPanel* this = (DisplayOptionsPanel*) object; DisplayOptionsPanel* this = (DisplayOptionsPanel*) object;
@ -45,6 +48,7 @@ static HandlerResult DisplayOptionsPanel_eventHandler(Panel* super, int ch) {
case 0x0d: case 0x0d:
case KEY_ENTER: case KEY_ENTER:
case KEY_MOUSE: case KEY_MOUSE:
case KEY_RECLICK:
case ' ': case ' ':
CheckItem_set(selected, ! (CheckItem_get(selected)) ); CheckItem_set(selected, ! (CheckItem_get(selected)) );
result = HANDLED; result = HANDLED;
@ -52,9 +56,9 @@ static HandlerResult DisplayOptionsPanel_eventHandler(Panel* super, int ch) {
if (result == HANDLED) { if (result == HANDLED) {
this->settings->changed = true; this->settings->changed = true;
Header* header = this->settings->header; const Header* header = this->scr->header;
Header_calculateHeight(header); Header_calculateHeight((Header*) header);
Header_reinit(header); Header_reinit((Header*) header);
Header_draw(header); Header_draw(header);
ScreenManager_resize(this->scr, this->scr->x1, header->height, this->scr->x2, this->scr->y2); ScreenManager_resize(this->scr, this->scr->x1, header->height, this->scr->x2, this->scr->y2);
} }
@ -72,24 +76,26 @@ PanelClass DisplayOptionsPanel_class = {
DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager* scr) { DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager* scr) {
DisplayOptionsPanel* this = AllocThis(DisplayOptionsPanel); DisplayOptionsPanel* this = AllocThis(DisplayOptionsPanel);
Panel* super = (Panel*) this; Panel* super = (Panel*) this;
Panel_init(super, 1, 1, 1, 1, Class(CheckItem), true); FunctionBar* fuBar = FunctionBar_new(DisplayOptionsFunctions, NULL, NULL);
Panel_init(super, 1, 1, 1, 1, Class(CheckItem), true, fuBar);
this->settings = settings; this->settings = settings;
this->scr = scr; this->scr = scr;
Panel_setHeader(super, "Display options"); Panel_setHeader(super, "Display options");
Panel_add(super, (Object*) CheckItem_new(strdup("Tree view"), &(settings->pl->treeView), false)); Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Tree view"), &(settings->treeView)));
Panel_add(super, (Object*) CheckItem_new(strdup("Shadow other users' processes"), &(settings->pl->shadowOtherUsers), false)); Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Shadow other users' processes"), &(settings->shadowOtherUsers)));
Panel_add(super, (Object*) CheckItem_new(strdup("Hide kernel threads"), &(settings->pl->hideKernelThreads), false)); Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Hide kernel threads"), &(settings->hideKernelThreads)));
Panel_add(super, (Object*) CheckItem_new(strdup("Hide userland threads"), &(settings->pl->hideUserlandThreads), false)); Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Hide userland process threads"), &(settings->hideUserlandThreads)));
Panel_add(super, (Object*) CheckItem_new(strdup("Display threads in a different color"), &(settings->pl->highlightThreads), false)); Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Display threads in a different color"), &(settings->highlightThreads)));
Panel_add(super, (Object*) CheckItem_new(strdup("Show custom thread names"), &(settings->pl->showThreadNames), false)); Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Show custom thread names"), &(settings->showThreadNames)));
Panel_add(super, (Object*) CheckItem_new(strdup("Highlight program \"basename\""), &(settings->pl->highlightBaseName), false)); Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Show program path"), &(settings->showProgramPath)));
Panel_add(super, (Object*) CheckItem_new(strdup("Highlight large numbers in memory counters"), &(settings->pl->highlightMegabytes), false)); Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Highlight program \"basename\""), &(settings->highlightBaseName)));
Panel_add(super, (Object*) CheckItem_new(strdup("Leave a margin around header"), &(settings->header->margin), false)); Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Highlight large numbers in memory counters"), &(settings->highlightMegabytes)));
Panel_add(super, (Object*) CheckItem_new(strdup("Detailed CPU time (System/IO-Wait/Hard-IRQ/Soft-IRQ/Steal/Guest)"), &(settings->pl->detailedCPUTime), false)); Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Leave a margin around header"), &(settings->headerMargin)));
Panel_add(super, (Object*) CheckItem_new(strdup("Count CPUs from 0 instead of 1"), &(settings->pl->countCPUsFromZero), false)); Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Detailed CPU time (System/IO-Wait/Hard-IRQ/Soft-IRQ/Steal/Guest)"), &(settings->detailedCPUTime)));
Panel_add(super, (Object*) CheckItem_new(strdup("Update process names on every refresh"), &(settings->pl->updateProcessNames), false)); Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Count CPUs from 0 instead of 1"), &(settings->countCPUsFromZero)));
Panel_add(super, (Object*) CheckItem_new(strdup("Add guest time in CPU meter percentage"), &(settings->pl->accountGuestInCPUMeter), false)); Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Update process names on every refresh"), &(settings->updateProcessNames)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Add guest time in CPU meter percentage"), &(settings->accountGuestInCPUMeter)));
return this; return this;
} }

66
EnvScreen.c Normal file
View File

@ -0,0 +1,66 @@
#include "EnvScreen.h"
#include "config.h"
#include "CRT.h"
#include "IncSet.h"
#include "ListItem.h"
#include "Platform.h"
#include "StringUtils.h"
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
/*{
#include "InfoScreen.h"
typedef struct EnvScreen_ {
InfoScreen super;
} EnvScreen;
}*/
InfoScreenClass EnvScreen_class = {
.super = {
.extends = Class(Object),
.delete = EnvScreen_delete
},
.scan = EnvScreen_scan,
.draw = EnvScreen_draw
};
EnvScreen* EnvScreen_new(Process* process) {
EnvScreen* this = xMalloc(sizeof(EnvScreen));
Object_setClass(this, Class(EnvScreen));
return (EnvScreen*) InfoScreen_init(&this->super, process, NULL, LINES-3, " ");
}
void EnvScreen_delete(Object* this) {
free(InfoScreen_done((InfoScreen*)this));
}
void EnvScreen_draw(InfoScreen* this) {
InfoScreen_drawTitled(this, "Environment of process %d - %s", this->process->pid, this->process->comm);
}
void EnvScreen_scan(InfoScreen* this) {
Panel* panel = this->display;
int idx = MAX(Panel_getSelectedIndex(panel), 0);
Panel_prune(panel);
CRT_dropPrivileges();
char* env = Platform_getProcessEnv(this->process->pid);
CRT_restorePrivileges();
if (env) {
for (char *p = env; *p; p = strrchr(p, 0)+1)
InfoScreen_addLine(this, p);
free(env);
}
else {
InfoScreen_addLine(this, "Could not read process environment.");
}
Vector_insertionSort(this->lines);
Vector_insertionSort(panel->items);
Panel_setSelected(panel, idx);
}

22
EnvScreen.h Normal file
View File

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

View File

@ -6,19 +6,19 @@ in the source distribution for its full text.
*/ */
#include "FunctionBar.h" #include "FunctionBar.h"
#include "CRT.h" #include "CRT.h"
#include "RichString.h"
#include "XAlloc.h"
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdbool.h>
/*{ /*{
#include "Object.h"
#include <stdbool.h>
typedef struct FunctionBar_ { typedef struct FunctionBar_ {
Object super;
int size; int size;
char** functions; char** functions;
char** keys; char** keys;
@ -28,32 +28,36 @@ typedef struct FunctionBar_ {
}*/ }*/
static const char* FunctionBar_FKeys[] = {"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", NULL}; static const char* const FunctionBar_FKeys[] = {"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", NULL};
static const char* FunctionBar_FLabels[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", NULL}; static const char* const FunctionBar_FLabels[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", NULL};
static int FunctionBar_FEvents[] = {KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5), KEY_F(6), KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10)}; static int FunctionBar_FEvents[] = {KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5), KEY_F(6), KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10)};
ObjectClass FunctionBar_class = { static const char* const FunctionBar_EnterEscKeys[] = {"Enter", "Esc", NULL};
.delete = FunctionBar_delete static const int FunctionBar_EnterEscEvents[] = {13, 27};
};
FunctionBar* FunctionBar_new(const char** functions, const char** keys, int* events) { FunctionBar* FunctionBar_newEnterEsc(const char* enter, const char* esc) {
FunctionBar* this = AllocThis(FunctionBar); const char* functions[] = {enter, esc, NULL};
this->functions = calloc(16, sizeof(char*)); return FunctionBar_new(functions, FunctionBar_EnterEscKeys, FunctionBar_EnterEscEvents);
}
FunctionBar* FunctionBar_new(const char* const* functions, const char* const* keys, const int* events) {
FunctionBar* this = xCalloc(1, sizeof(FunctionBar));
this->functions = xCalloc(16, sizeof(char*));
if (!functions) { if (!functions) {
functions = FunctionBar_FLabels; functions = FunctionBar_FLabels;
} }
for (int i = 0; i < 15 && functions[i]; i++) { for (int i = 0; i < 15 && functions[i]; i++) {
this->functions[i] = strdup(functions[i]); this->functions[i] = xStrdup(functions[i]);
} }
if (keys && events) { if (keys && events) {
this->staticData = false; this->staticData = false;
this->keys = malloc(sizeof(char*) * 15); this->keys = xCalloc(15, sizeof(char*));
this->events = malloc(sizeof(int) * 15); this->events = xCalloc(15, sizeof(int));
int i = 0; int i = 0;
while (i < 15 && functions[i]) { while (i < 15 && functions[i]) {
this->keys[i] = strdup(keys[i]); this->keys[i] = xStrdup(keys[i]);
this->events[i] = events[i]; this->events[i] = events[i];
i++; i++;
} }
@ -67,8 +71,7 @@ FunctionBar* FunctionBar_new(const char** functions, const char** keys, int* eve
return this; return this;
} }
void FunctionBar_delete(Object* cast) { void FunctionBar_delete(FunctionBar* this) {
FunctionBar* this = (FunctionBar*) cast;
for (int i = 0; i < 15 && this->functions[i]; i++) { for (int i = 0; i < 15 && this->functions[i]; i++) {
free(this->functions[i]); free(this->functions[i]);
} }
@ -87,7 +90,7 @@ void FunctionBar_setLabel(FunctionBar* this, int event, const char* text) {
for (int i = 0; i < this->size; i++) { for (int i = 0; i < this->size; i++) {
if (this->events[i] == event) { if (this->events[i] == event) {
free(this->functions[i]); free(this->functions[i]);
this->functions[i] = strdup(text); this->functions[i] = xStrdup(text);
break; break;
} }
} }

View File

@ -9,10 +9,10 @@ Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "Object.h"
#include <stdbool.h>
typedef struct FunctionBar_ { typedef struct FunctionBar_ {
Object super;
int size; int size;
char** functions; char** functions;
char** keys; char** keys;
@ -21,11 +21,12 @@ typedef struct FunctionBar_ {
} FunctionBar; } FunctionBar;
extern ObjectClass FunctionBar_class;
FunctionBar* FunctionBar_new(const char** functions, const char** keys, int* events); FunctionBar* FunctionBar_newEnterEsc(const char* enter, const char* esc);
void FunctionBar_delete(Object* cast); FunctionBar* FunctionBar_new(const char* const* functions, const char* const* keys, const int* events);
void FunctionBar_delete(FunctionBar* this);
void FunctionBar_setLabel(FunctionBar* this, int event, const char* text); void FunctionBar_setLabel(FunctionBar* this, int event, const char* text);

View File

@ -6,6 +6,7 @@ in the source distribution for its full text.
*/ */
#include "Hashtable.h" #include "Hashtable.h"
#include "XAlloc.h"
#include <stdlib.h> #include <stdlib.h>
#include <assert.h> #include <assert.h>
@ -63,7 +64,7 @@ int Hashtable_count(Hashtable* this) {
static HashtableItem* HashtableItem_new(unsigned int key, void* value) { static HashtableItem* HashtableItem_new(unsigned int key, void* value) {
HashtableItem* this; HashtableItem* this;
this = (HashtableItem*) malloc(sizeof(HashtableItem)); this = xMalloc(sizeof(HashtableItem));
this->key = key; this->key = key;
this->value = value; this->value = value;
this->next = NULL; this->next = NULL;
@ -73,10 +74,10 @@ static HashtableItem* HashtableItem_new(unsigned int key, void* value) {
Hashtable* Hashtable_new(int size, bool owner) { Hashtable* Hashtable_new(int size, bool owner) {
Hashtable* this; Hashtable* this;
this = (Hashtable*) malloc(sizeof(Hashtable)); this = xMalloc(sizeof(Hashtable));
this->items = 0; this->items = 0;
this->size = size; this->size = size;
this->buckets = (HashtableItem**) calloc(size, sizeof(HashtableItem*)); this->buckets = (HashtableItem**) xCalloc(size, sizeof(HashtableItem*));
this->owner = owner; this->owner = owner;
assert(Hashtable_isConsistent(this)); assert(Hashtable_isConsistent(this));
return this; return this;

219
Header.c
View File

@ -8,16 +8,8 @@ in the source distribution for its full text.
#include "Header.h" #include "Header.h"
#include "CRT.h" #include "CRT.h"
#include "CPUMeter.h" #include "StringUtils.h"
#include "MemoryMeter.h" #include "Platform.h"
#include "SwapMeter.h"
#include "TasksMeter.h"
#include "LoadAverageMeter.h"
#include "UptimeMeter.h"
#include "BatteryMeter.h"
#include "ClockMeter.h"
#include "HostnameMeter.h"
#include "String.h"
#include <assert.h> #include <assert.h>
#include <time.h> #include <time.h>
@ -25,21 +17,17 @@ in the source distribution for its full text.
#include <stdlib.h> #include <stdlib.h>
/*{ /*{
#include "ProcessList.h"
#include "Meter.h" #include "Meter.h"
#include "Settings.h"
typedef enum HeaderSide_ { #include "Vector.h"
LEFT_HEADER,
RIGHT_HEADER
} HeaderSide;
typedef struct Header_ { typedef struct Header_ {
Vector* leftMeters; Vector** columns;
Vector* rightMeters; Settings* settings;
ProcessList* pl; struct ProcessList_* pl;
int height; int nrColumns;
int pad; int pad;
bool margin; int height;
} Header; } Header;
}*/ }*/
@ -48,25 +36,73 @@ typedef struct Header_ {
#define MAX(a,b) ((a)>(b)?(a):(b)) #define MAX(a,b) ((a)>(b)?(a):(b))
#endif #endif
Header* Header_new(ProcessList* pl) { #ifndef Header_forEachColumn
Header* this = calloc(1, sizeof(Header)); #define Header_forEachColumn(this_, i_) for (int (i_)=0; (i_) < (this_)->nrColumns; ++(i_))
this->leftMeters = Vector_new(Class(Meter), true, DEFAULT_SIZE); #endif
this->rightMeters = Vector_new(Class(Meter), true, DEFAULT_SIZE);
this->margin = true; Header* Header_new(struct ProcessList_* pl, Settings* settings, int nrColumns) {
Header* this = xCalloc(1, sizeof(Header));
this->columns = xCalloc(nrColumns, sizeof(Vector*));
this->settings = settings;
this->pl = pl; this->pl = pl;
this->nrColumns = nrColumns;
Header_forEachColumn(this, i) {
this->columns[i] = Vector_new(Class(Meter), true, DEFAULT_SIZE);
}
return this; return this;
} }
void Header_delete(Header* this) { void Header_delete(Header* this) {
Vector_delete(this->leftMeters); Header_forEachColumn(this, i) {
Vector_delete(this->rightMeters); Vector_delete(this->columns[i]);
}
free(this->columns);
free(this); free(this);
} }
void Header_createMeter(Header* this, char* name, HeaderSide side) { void Header_populateFromSettings(Header* this) {
Vector* meters = side == LEFT_HEADER Header_forEachColumn(this, col) {
? this->leftMeters MeterColumnSettings* colSettings = &this->settings->columns[col];
: this->rightMeters; for (int i = 0; i < colSettings->len; i++) {
Header_addMeterByName(this, colSettings->names[i], col);
if (colSettings->modes[i] != 0) {
Header_setMode(this, i, colSettings->modes[i], col);
}
}
}
Header_calculateHeight(this);
}
void Header_writeBackToSettings(const Header* this) {
Header_forEachColumn(this, col) {
MeterColumnSettings* colSettings = &this->settings->columns[col];
String_freeArray(colSettings->names);
free(colSettings->modes);
Vector* vec = this->columns[col];
int len = Vector_size(vec);
colSettings->names = xCalloc(len+1, sizeof(char*));
colSettings->modes = xCalloc(len, sizeof(int));
colSettings->len = len;
for (int i = 0; i < len; i++) {
Meter* meter = (Meter*) Vector_get(vec, i);
char* name = xCalloc(64, sizeof(char));
if (meter->param) {
xSnprintf(name, 63, "%s(%d)", As_Meter(meter)->name, meter->param);
} else {
xSnprintf(name, 63, "%s", As_Meter(meter)->name);
}
colSettings->names[i] = name;
colSettings->modes[i] = meter->mode;
}
}
}
MeterModeId Header_addMeterByName(Header* this, char* name, int column) {
Vector* meters = this->columns[column];
char* paren = strchr(name, '('); char* paren = strchr(name, '(');
int param = 0; int param = 0;
@ -75,18 +111,20 @@ void Header_createMeter(Header* this, char* name, HeaderSide side) {
if (!ok) param = 0; if (!ok) param = 0;
*paren = '\0'; *paren = '\0';
} }
for (MeterClass** type = Meter_types; *type; type++) { MeterModeId mode = TEXT_METERMODE;
for (MeterClass** type = Platform_meterTypes; *type; type++) {
if (String_eq(name, (*type)->name)) { if (String_eq(name, (*type)->name)) {
Vector_add(meters, Meter_new(this->pl, param, *type)); Meter* meter = Meter_new(this->pl, param, *type);
Vector_add(meters, meter);
mode = meter->mode;
break; break;
} }
} }
return mode;
} }
void Header_setMode(Header* this, int i, MeterModeId mode, HeaderSide side) { void Header_setMode(Header* this, int i, MeterModeId mode, int column) {
Vector* meters = side == LEFT_HEADER Vector* meters = this->columns[column];
? this->leftMeters
: this->rightMeters;
if (i >= Vector_size(meters)) if (i >= Vector_size(meters))
return; return;
@ -94,77 +132,48 @@ void Header_setMode(Header* this, int i, MeterModeId mode, HeaderSide side) {
Meter_setMode(meter, mode); Meter_setMode(meter, mode);
} }
Meter* Header_addMeter(Header* this, MeterClass* type, int param, HeaderSide side) { Meter* Header_addMeterByClass(Header* this, MeterClass* type, int param, int column) {
Vector* meters = side == LEFT_HEADER Vector* meters = this->columns[column];
? this->leftMeters
: this->rightMeters;
Meter* meter = Meter_new(this->pl, param, type); Meter* meter = Meter_new(this->pl, param, type);
Vector_add(meters, meter); Vector_add(meters, meter);
return meter; return meter;
} }
int Header_size(Header* this, HeaderSide side) { int Header_size(Header* this, int column) {
Vector* meters = side == LEFT_HEADER Vector* meters = this->columns[column];
? this->leftMeters
: this->rightMeters;
return Vector_size(meters); return Vector_size(meters);
} }
char* Header_readMeterName(Header* this, int i, HeaderSide side) { char* Header_readMeterName(Header* this, int i, int column) {
Vector* meters = side == LEFT_HEADER Vector* meters = this->columns[column];
? this->leftMeters
: this->rightMeters;
Meter* meter = (Meter*) Vector_get(meters, i); Meter* meter = (Meter*) Vector_get(meters, i);
int nameLen = strlen(Meter_name(meter)); int nameLen = strlen(Meter_name(meter));
int len = nameLen + 100; int len = nameLen + 100;
char* name = malloc(len); char* name = xMalloc(len);
strncpy(name, Meter_name(meter), nameLen); strncpy(name, Meter_name(meter), nameLen);
name[nameLen] = '\0'; name[nameLen] = '\0';
if (meter->param) if (meter->param)
snprintf(name + nameLen, len - nameLen, "(%d)", meter->param); xSnprintf(name + nameLen, len - nameLen, "(%d)", meter->param);
return name; return name;
} }
MeterModeId Header_readMeterMode(Header* this, int i, HeaderSide side) { MeterModeId Header_readMeterMode(Header* this, int i, int column) {
Vector* meters = side == LEFT_HEADER Vector* meters = this->columns[column];
? this->leftMeters
: this->rightMeters;
Meter* meter = (Meter*) Vector_get(meters, i); Meter* meter = (Meter*) Vector_get(meters, i);
return meter->mode; return meter->mode;
} }
void Header_defaultMeters(Header* this, int cpuCount) {
if (cpuCount > 8) {
Vector_add(this->leftMeters, Meter_new(this->pl, 0, (MeterClass*) Class(LeftCPUs2Meter)));
Vector_add(this->rightMeters, Meter_new(this->pl, 0, (MeterClass*) Class(RightCPUs2Meter)));
} else if (cpuCount > 4) {
Vector_add(this->leftMeters, Meter_new(this->pl, 0, (MeterClass*) Class(LeftCPUsMeter)));
Vector_add(this->rightMeters, Meter_new(this->pl, 0, (MeterClass*) Class(RightCPUsMeter)));
} else {
Vector_add(this->leftMeters, Meter_new(this->pl, 0, (MeterClass*) Class(AllCPUsMeter)));
}
Vector_add(this->leftMeters, Meter_new(this->pl, 0, (MeterClass*) Class(MemoryMeter)));
Vector_add(this->leftMeters, Meter_new(this->pl, 0, (MeterClass*) Class(SwapMeter)));
Vector_add(this->rightMeters, Meter_new(this->pl, 0, (MeterClass*) Class(TasksMeter)));
Vector_add(this->rightMeters, Meter_new(this->pl, 0, (MeterClass*) Class(LoadAverageMeter)));
Vector_add(this->rightMeters, Meter_new(this->pl, 0, (MeterClass*) Class(UptimeMeter)));
}
void Header_reinit(Header* this) { void Header_reinit(Header* this) {
for (int i = 0; i < Vector_size(this->leftMeters); i++) { Header_forEachColumn(this, col) {
Meter* meter = (Meter*) Vector_get(this->leftMeters, i); for (int i = 0; i < Vector_size(this->columns[col]); i++) {
if (Meter_initFn(meter)) Meter* meter = (Meter*) Vector_get(this->columns[col], i);
Meter_init(meter); if (Meter_initFn(meter))
} Meter_init(meter);
for (int i = 0; i < Vector_size(this->rightMeters); i++) { }
Meter* meter = (Meter*) Vector_get(this->rightMeters, i);
if (Meter_initFn(meter))
Meter_init(meter);
} }
} }
@ -175,32 +184,34 @@ void Header_draw(const Header* this) {
for (int y = 0; y < height; y++) { for (int y = 0; y < height; y++) {
mvhline(y, 0, ' ', COLS); mvhline(y, 0, ' ', COLS);
} }
for (int y = (pad / 2), i = 0; i < Vector_size(this->leftMeters); i++) { int width = COLS / this->nrColumns - (pad * this->nrColumns - 1) - 1;
Meter* meter = (Meter*) Vector_get(this->leftMeters, i); int x = pad;
meter->draw(meter, pad, y, COLS / 2 - (pad * 2 - 1) - 1);
y += meter->h; Header_forEachColumn(this, col) {
} Vector* meters = this->columns[col];
for (int y = (pad / 2), i = 0; i < Vector_size(this->rightMeters); i++) { for (int y = (pad / 2), i = 0; i < Vector_size(meters); i++) {
Meter* meter = (Meter*) Vector_get(this->rightMeters, i); Meter* meter = (Meter*) Vector_get(meters, i);
meter->draw(meter, COLS / 2 + pad, y, COLS / 2 - (pad * 2 - 1) - 1); meter->draw(meter, x, y, width);
y += meter->h; y += meter->h;
}
x += width + pad;
} }
} }
int Header_calculateHeight(Header* this) { int Header_calculateHeight(Header* this) {
int pad = this->margin ? 2 : 0; int pad = this->settings->headerMargin ? 2 : 0;
int leftHeight = pad; int maxHeight = pad;
int rightHeight = pad;
for (int i = 0; i < Vector_size(this->leftMeters); i++) { Header_forEachColumn(this, col) {
Meter* meter = (Meter*) Vector_get(this->leftMeters, i); Vector* meters = this->columns[col];
leftHeight += meter->h; int height = pad;
} for (int i = 0; i < Vector_size(meters); i++) {
for (int i = 0; i < Vector_size(this->rightMeters); i++) { Meter* meter = (Meter*) Vector_get(meters, i);
Meter* meter = (Meter*) Vector_get(this->rightMeters, i); height += meter->h;
rightHeight += meter->h; }
maxHeight = MAX(maxHeight, height);
} }
this->height = maxHeight;
this->pad = pad; this->pad = pad;
this->height = MAX(leftHeight, rightHeight); return maxHeight;
return this->height;
} }

View File

@ -9,21 +9,17 @@ Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "ProcessList.h"
#include "Meter.h" #include "Meter.h"
#include "Settings.h"
typedef enum HeaderSide_ { #include "Vector.h"
LEFT_HEADER,
RIGHT_HEADER
} HeaderSide;
typedef struct Header_ { typedef struct Header_ {
Vector* leftMeters; Vector** columns;
Vector* rightMeters; Settings* settings;
ProcessList* pl; struct ProcessList_* pl;
int height; int nrColumns;
int pad; int pad;
bool margin; int height;
} Header; } Header;
@ -31,23 +27,29 @@ typedef struct Header_ {
#define MAX(a,b) ((a)>(b)?(a):(b)) #define MAX(a,b) ((a)>(b)?(a):(b))
#endif #endif
Header* Header_new(ProcessList* pl); #ifndef Header_forEachColumn
#define Header_forEachColumn(this_, i_) for (int (i_)=0; (i_) < (this_)->nrColumns; ++(i_))
#endif
Header* Header_new(struct ProcessList_* pl, Settings* settings, int nrColumns);
void Header_delete(Header* this); void Header_delete(Header* this);
void Header_createMeter(Header* this, char* name, HeaderSide side); void Header_populateFromSettings(Header* this);
void Header_setMode(Header* this, int i, MeterModeId mode, HeaderSide side); void Header_writeBackToSettings(const Header* this);
Meter* Header_addMeter(Header* this, MeterClass* type, int param, HeaderSide side); MeterModeId Header_addMeterByName(Header* this, char* name, int column);
int Header_size(Header* this, HeaderSide side); void Header_setMode(Header* this, int i, MeterModeId mode, int column);
char* Header_readMeterName(Header* this, int i, HeaderSide side); Meter* Header_addMeterByClass(Header* this, MeterClass* type, int param, int column);
MeterModeId Header_readMeterMode(Header* this, int i, HeaderSide side); int Header_size(Header* this, int column);
void Header_defaultMeters(Header* this, int cpuCount); char* Header_readMeterName(Header* this, int i, int column);
MeterModeId Header_readMeterMode(Header* this, int i, int column);
void Header_reinit(Header* this); void Header_reinit(Header* this);

View File

@ -19,7 +19,7 @@ int HostnameMeter_attributes[] = {
HOSTNAME HOSTNAME
}; };
static void HostnameMeter_setValues(Meter* this, char* buffer, int size) { static void HostnameMeter_updateValues(Meter* this, char* buffer, int size) {
(void) this; (void) this;
gethostname(buffer, size-1); gethostname(buffer, size-1);
} }
@ -29,8 +29,9 @@ MeterClass HostnameMeter_class = {
.extends = Class(Meter), .extends = Class(Meter),
.delete = Meter_delete .delete = Meter_delete
}, },
.setValues = HostnameMeter_setValues, .updateValues = HostnameMeter_updateValues,
.defaultMode = TEXT_METERMODE, .defaultMode = TEXT_METERMODE,
.maxItems = 0,
.total = 100.0, .total = 100.0,
.attributes = HostnameMeter_attributes, .attributes = HostnameMeter_attributes,
.name = "Hostname", .name = "Hostname",

233
INSTALL
View File

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

View File

@ -6,7 +6,7 @@ in the source distribution for its full text.
*/ */
#include "IncSet.h" #include "IncSet.h"
#include "String.h" #include "StringUtils.h"
#include "Panel.h" #include "Panel.h"
#include "ListItem.h" #include "ListItem.h"
#include "CRT.h" #include "CRT.h"
@ -38,9 +38,9 @@ typedef struct IncMode_ {
typedef struct IncSet_ { typedef struct IncSet_ {
IncMode modes[2]; IncMode modes[2];
IncMode* active; IncMode* active;
FunctionBar* bar;
FunctionBar* defaultBar; FunctionBar* defaultBar;
bool filtering; bool filtering;
bool found;
} IncSet; } IncSet;
typedef const char* (*IncMode_GetPanelValue)(Panel*, int); typedef const char* (*IncMode_GetPanelValue)(Panel*, int);
@ -52,8 +52,8 @@ static void IncMode_reset(IncMode* mode) {
mode->buffer[0] = 0; mode->buffer[0] = 0;
} }
static const char* searchFunctions[] = {"Next ", "Cancel ", " Search: ", NULL}; static const char* const searchFunctions[] = {"Next ", "Cancel ", " Search: ", NULL};
static const char* searchKeys[] = {"F3", "Esc", " "}; static const char* const searchKeys[] = {"F3", "Esc", " "};
static int searchEvents[] = {KEY_F(3), 27, ERR}; static int searchEvents[] = {KEY_F(3), 27, ERR};
static inline void IncMode_initSearch(IncMode* search) { static inline void IncMode_initSearch(IncMode* search) {
@ -62,8 +62,8 @@ static inline void IncMode_initSearch(IncMode* search) {
search->isFilter = false; search->isFilter = false;
} }
static const char* filterFunctions[] = {"Done ", "Clear ", " Filter: ", NULL}; static const char* const filterFunctions[] = {"Done ", "Clear ", " Filter: ", NULL};
static const char* filterKeys[] = {"Enter", "Esc", " "}; static const char* const filterKeys[] = {"Enter", "Esc", " "};
static int filterEvents[] = {13, 27, ERR}; static int filterEvents[] = {13, 27, ERR};
static inline void IncMode_initFilter(IncMode* filter) { static inline void IncMode_initFilter(IncMode* filter) {
@ -73,16 +73,15 @@ static inline void IncMode_initFilter(IncMode* filter) {
} }
static inline void IncMode_done(IncMode* mode) { static inline void IncMode_done(IncMode* mode) {
FunctionBar_delete((Object*)mode->bar); FunctionBar_delete(mode->bar);
} }
IncSet* IncSet_new(FunctionBar* bar) { IncSet* IncSet_new(FunctionBar* bar) {
IncSet* this = calloc(1, sizeof(IncSet)); IncSet* this = xCalloc(1, 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->filtering = false;
this->bar = bar;
this->defaultBar = bar; this->defaultBar = bar;
return this; return this;
} }
@ -116,7 +115,7 @@ static void updateWeakPanel(IncSet* this, Panel* panel, Vector* lines) {
} }
} }
static void 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; bool found = false;
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
@ -130,6 +129,7 @@ static void search(IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelVa
FunctionBar_draw(mode->bar, mode->buffer); FunctionBar_draw(mode->bar, mode->buffer);
else else
FunctionBar_drawAttr(mode->bar, mode->buffer, CRT_colors[FAILED_SEARCH]); FunctionBar_drawAttr(mode->bar, mode->buffer, CRT_colors[FAILED_SEARCH]);
return found;
} }
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) {
@ -137,7 +137,7 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue
return true; return true;
IncMode* mode = this->active; IncMode* mode = this->active;
int size = Panel_size(panel); int size = Panel_size(panel);
bool filterChange = false; bool filterChanged = false;
bool doSearch = true; bool doSearch = true;
if (ch == KEY_F(3)) { if (ch == KEY_F(3)) {
if (size == 0) return true; if (size == 0) return true;
@ -153,27 +153,35 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue
} }
} }
doSearch = false; doSearch = false;
} else if (isprint((char)ch) && (mode->index < INCMODE_MAX)) { } else if (ch < 255 && isprint((char)ch)) {
mode->buffer[mode->index] = ch; if (mode->index < INCMODE_MAX) {
mode->index++; mode->buffer[mode->index] = ch;
mode->buffer[mode->index] = 0; mode->index++;
if (mode->isFilter) { mode->buffer[mode->index] = 0;
filterChange = true; if (mode->isFilter) {
if (mode->index == 1) this->filtering = true; filterChanged = true;
} if (mode->index == 1) this->filtering = true;
} else if ((ch == KEY_BACKSPACE || ch == 127) && (mode->index > 0)) {
mode->index--;
mode->buffer[mode->index] = 0;
if (mode->isFilter) {
filterChange = true;
if (mode->index == 0) {
this->filtering = false;
IncMode_reset(mode);
} }
} }
} else if ((ch == KEY_BACKSPACE || ch == 127)) {
if (mode->index > 0) {
mode->index--;
mode->buffer[mode->index] = 0;
if (mode->isFilter) {
filterChanged = true;
if (mode->index == 0) {
this->filtering = false;
IncMode_reset(mode);
}
}
} else {
doSearch = false;
}
} else if (ch == KEY_RESIZE) {
Panel_resize(panel, COLS, LINES-panel->y-1);
} else { } else {
if (mode->isFilter) { if (mode->isFilter) {
filterChange = true; filterChanged = true;
if (ch == 27) { if (ch == 27) {
this->filtering = false; this->filtering = false;
IncMode_reset(mode); IncMode_reset(mode);
@ -182,17 +190,17 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue
IncMode_reset(mode); IncMode_reset(mode);
} }
this->active = NULL; this->active = NULL;
this->bar = this->defaultBar; Panel_setDefaultBar(panel);
FunctionBar_draw(this->defaultBar, NULL); FunctionBar_draw(this->defaultBar, NULL);
doSearch = false; doSearch = false;
} }
if (doSearch) { if (doSearch) {
search(mode, panel, getPanelValue); this->found = search(mode, panel, getPanelValue);
} }
if (filterChange && lines) { if (filterChanged && lines) {
updateWeakPanel(this, panel, lines); updateWeakPanel(this, panel, lines);
} }
return filterChange; return filterChanged;
} }
const char* IncSet_getListItemValue(Panel* panel, int i) { const char* IncSet_getListItemValue(Panel* panel, int i) {
@ -202,12 +210,24 @@ const char* IncSet_getListItemValue(Panel* panel, int i) {
return ""; return "";
} }
void IncSet_activate(IncSet* this, IncType type) { void IncSet_activate(IncSet* this, IncType type, Panel* panel) {
this->active = &(this->modes[type]); this->active = &(this->modes[type]);
this->bar = this->active->bar;
FunctionBar_draw(this->active->bar, this->active->buffer); FunctionBar_draw(this->active->bar, this->active->buffer);
panel->currentBar = this->active->bar;
} }
void IncSet_drawBar(IncSet* this) { void IncSet_drawBar(IncSet* this) {
FunctionBar_draw(this->bar, this->active ? this->active->buffer : NULL); if (this->active) {
FunctionBar_draw(this->active->bar, this->active->buffer);
} else {
FunctionBar_draw(this->defaultBar, NULL);
}
}
int IncSet_synthesizeEvent(IncSet* this, int x) {
if (this->active) {
return FunctionBar_synthesizeEvent(this->active->bar, x);
} else {
return FunctionBar_synthesizeEvent(this->defaultBar, x);
}
} }

View File

@ -33,9 +33,9 @@ typedef struct IncMode_ {
typedef struct IncSet_ { typedef struct IncSet_ {
IncMode modes[2]; IncMode modes[2];
IncMode* active; IncMode* active;
FunctionBar* bar;
FunctionBar* defaultBar; FunctionBar* defaultBar;
bool filtering; bool filtering;
bool found;
} IncSet; } IncSet;
typedef const char* (*IncMode_GetPanelValue)(Panel*, int); typedef const char* (*IncMode_GetPanelValue)(Panel*, int);
@ -49,8 +49,10 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue
const char* IncSet_getListItemValue(Panel* panel, int i); const char* IncSet_getListItemValue(Panel* panel, int i);
void IncSet_activate(IncSet* this, IncType type); void IncSet_activate(IncSet* this, IncType type, Panel* panel);
void IncSet_drawBar(IncSet* this); void IncSet_drawBar(IncSet* this);
int IncSet_synthesizeEvent(IncSet* this, int x);
#endif #endif

183
InfoScreen.c Normal file
View File

@ -0,0 +1,183 @@
#include "InfoScreen.h"
#include "config.h"
#include "Object.h"
#include "CRT.h"
#include "IncSet.h"
#include "ListItem.h"
#include "Platform.h"
#include "StringUtils.h"
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdarg.h>
/*{
#include "Process.h"
#include "Panel.h"
#include "FunctionBar.h"
#include "IncSet.h"
typedef struct InfoScreen_ InfoScreen;
typedef void(*InfoScreen_Scan)(InfoScreen*);
typedef void(*InfoScreen_Draw)(InfoScreen*);
typedef void(*InfoScreen_OnErr)(InfoScreen*);
typedef bool(*InfoScreen_OnKey)(InfoScreen*, int);
typedef struct InfoScreenClass_ {
ObjectClass super;
const InfoScreen_Scan scan;
const InfoScreen_Draw draw;
const InfoScreen_OnErr onErr;
const InfoScreen_OnKey onKey;
} InfoScreenClass;
#define As_InfoScreen(this_) ((InfoScreenClass*)(((InfoScreen*)(this_))->super.klass))
#define InfoScreen_scan(this_) As_InfoScreen(this_)->scan((InfoScreen*)(this_))
#define InfoScreen_draw(this_) As_InfoScreen(this_)->draw((InfoScreen*)(this_))
#define InfoScreen_onErr(this_) As_InfoScreen(this_)->onErr((InfoScreen*)(this_))
#define InfoScreen_onKey(this_, ch_) As_InfoScreen(this_)->onKey((InfoScreen*)(this_), ch_)
struct InfoScreen_ {
Object super;
Process* process;
Panel* display;
FunctionBar* bar;
IncSet* inc;
Vector* lines;
};
}*/
static const char* const InfoScreenFunctions[] = {"Search ", "Filter ", "Refresh", "Done ", NULL};
static const char* const InfoScreenKeys[] = {"F3", "F4", "F5", "Esc"};
static int InfoScreenEvents[] = {KEY_F(3), KEY_F(4), KEY_F(5), 27};
InfoScreen* InfoScreen_init(InfoScreen* this, Process* process, FunctionBar* bar, int height, char* panelHeader) {
this->process = process;
if (!bar) {
bar = FunctionBar_new(InfoScreenFunctions, InfoScreenKeys, InfoScreenEvents);
}
this->display = Panel_new(0, 1, COLS, height, false, Class(ListItem), bar);
this->inc = IncSet_new(bar);
this->lines = Vector_new(this->display->items->type, true, DEFAULT_SIZE);
Panel_setHeader(this->display, panelHeader);
return this;
}
InfoScreen* InfoScreen_done(InfoScreen* this) {
Panel_delete((Object*)this->display);
IncSet_delete(this->inc);
Vector_delete(this->lines);
return this;
}
void InfoScreen_drawTitled(InfoScreen* this, char* fmt, ...) {
va_list ap;
va_start(ap, fmt);
attrset(CRT_colors[METER_TEXT]);
mvhline(0, 0, ' ', COLS);
wmove(stdscr, 0, 0);
vw_printw(stdscr, fmt, ap);
attrset(CRT_colors[DEFAULT_COLOR]);
this->display->needsRedraw = true;
Panel_draw(this->display, true);
IncSet_drawBar(this->inc);
va_end(ap);
}
void InfoScreen_addLine(InfoScreen* this, const char* line) {
Vector_add(this->lines, (Object*) ListItem_new(line, 0));
const char* incFilter = IncSet_filter(this->inc);
if (!incFilter || String_contains_i(line, incFilter))
Panel_add(this->display, (Object*)Vector_get(this->lines, Vector_size(this->lines)-1));
}
void InfoScreen_appendLine(InfoScreen* this, const char* line) {
ListItem* last = (ListItem*)Vector_get(this->lines, Vector_size(this->lines)-1);
ListItem_append(last, line);
const char* incFilter = IncSet_filter(this->inc);
if (incFilter && Panel_get(this->display, Panel_size(this->display)-1) != (Object*)last && String_contains_i(line, incFilter))
Panel_add(this->display, (Object*)last);
}
void InfoScreen_run(InfoScreen* this) {
Panel* panel = this->display;
if (As_InfoScreen(this)->scan) InfoScreen_scan(this);
InfoScreen_draw(this);
bool looping = true;
while (looping) {
Panel_draw(panel, true);
if (this->inc->active) {
(void) move(LINES-1, CRT_cursorX);
}
set_escdelay(25);
int ch = getch();
if (ch == ERR) {
if (As_InfoScreen(this)->onErr) {
InfoScreen_onErr(this);
continue;
}
}
if (ch == KEY_MOUSE) {
MEVENT mevent;
int ok = getmouse(&mevent);
if (ok == OK)
if (mevent.y >= panel->y && mevent.y < LINES - 1) {
Panel_setSelected(panel, mevent.y - panel->y + panel->scrollV);
ch = 0;
} if (mevent.y == LINES - 1)
ch = IncSet_synthesizeEvent(this->inc, mevent.x);
}
if (this->inc->active) {
IncSet_handleKey(this->inc, ch, panel, IncSet_getListItemValue, this->lines);
continue;
}
switch(ch) {
case ERR:
continue;
case KEY_F(3):
case '/':
IncSet_activate(this->inc, INC_SEARCH, panel);
break;
case KEY_F(4):
case '\\':
IncSet_activate(this->inc, INC_FILTER, panel);
break;
case KEY_F(5):
clear();
if (As_InfoScreen(this)->scan) InfoScreen_scan(this);
InfoScreen_draw(this);
break;
case '\014': // Ctrl+L
clear();
InfoScreen_draw(this);
break;
case 'q':
case 27:
case KEY_F(10):
looping = false;
break;
case KEY_RESIZE:
Panel_resize(panel, COLS, LINES-2);
InfoScreen_draw(this);
break;
default:
if (As_InfoScreen(this)->onKey && InfoScreen_onKey(this, ch)) {
continue;
}
Panel_onKey(panel, ch);
}
}
}

53
InfoScreen.h Normal file
View File

@ -0,0 +1,53 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_InfoScreen
#define HEADER_InfoScreen
#include "Process.h"
#include "Panel.h"
#include "FunctionBar.h"
#include "IncSet.h"
typedef struct InfoScreen_ InfoScreen;
typedef void(*InfoScreen_Scan)(InfoScreen*);
typedef void(*InfoScreen_Draw)(InfoScreen*);
typedef void(*InfoScreen_OnErr)(InfoScreen*);
typedef bool(*InfoScreen_OnKey)(InfoScreen*, int);
typedef struct InfoScreenClass_ {
ObjectClass super;
const InfoScreen_Scan scan;
const InfoScreen_Draw draw;
const InfoScreen_OnErr onErr;
const InfoScreen_OnKey onKey;
} InfoScreenClass;
#define As_InfoScreen(this_) ((InfoScreenClass*)(((InfoScreen*)(this_))->super.klass))
#define InfoScreen_scan(this_) As_InfoScreen(this_)->scan((InfoScreen*)(this_))
#define InfoScreen_draw(this_) As_InfoScreen(this_)->draw((InfoScreen*)(this_))
#define InfoScreen_onErr(this_) As_InfoScreen(this_)->onErr((InfoScreen*)(this_))
#define InfoScreen_onKey(this_, ch_) As_InfoScreen(this_)->onKey((InfoScreen*)(this_), ch_)
struct InfoScreen_ {
Object super;
Process* process;
Panel* display;
FunctionBar* bar;
IncSet* inc;
Vector* lines;
};
InfoScreen* InfoScreen_init(InfoScreen* this, Process* process, FunctionBar* bar, int height, char* panelHeader);
InfoScreen* InfoScreen_done(InfoScreen* this);
void InfoScreen_drawTitled(InfoScreen* this, char* fmt, ...);
void InfoScreen_addLine(InfoScreen* this, const char* line);
void InfoScreen_appendLine(InfoScreen* this, const char* line);
void InfoScreen_run(InfoScreen* this);
#endif

View File

@ -8,7 +8,7 @@ in the source distribution for its full text.
#include "ListItem.h" #include "ListItem.h"
#include "CRT.h" #include "CRT.h"
#include "String.h" #include "StringUtils.h"
#include "RichString.h" #include "RichString.h"
#include <string.h> #include <string.h>
@ -22,6 +22,7 @@ typedef struct ListItem_ {
Object super; Object super;
char* value; char* value;
int key; int key;
bool moving;
} ListItem; } ListItem;
}*/ }*/
@ -33,14 +34,23 @@ static void ListItem_delete(Object* cast) {
} }
static void ListItem_display(Object* cast, RichString* out) { static void ListItem_display(Object* cast, RichString* out) {
ListItem* this = (ListItem*)cast; ListItem* const this = (ListItem*)cast;
assert (this != NULL); assert (this != NULL);
/* /*
int len = strlen(this->value)+1; int len = strlen(this->value)+1;
char buffer[len+1]; char buffer[len+1];
snprintf(buffer, len, "%s", this->value); xSnprintf(buffer, len, "%s", this->value);
*/ */
RichString_write(out, CRT_colors[DEFAULT_COLOR], this->value/*buffer*/); if (this->moving) {
RichString_write(out, CRT_colors[DEFAULT_COLOR],
#ifdef HAVE_LIBNCURSESW
CRT_utf8 ? "" :
#endif
"+ ");
} else {
RichString_prune(out);
}
RichString_append(out, CRT_colors[DEFAULT_COLOR], this->value/*buffer*/);
} }
ObjectClass ListItem_class = { ObjectClass ListItem_class = {
@ -51,8 +61,9 @@ ObjectClass ListItem_class = {
ListItem* ListItem_new(const char* value, int key) { ListItem* ListItem_new(const char* value, int key) {
ListItem* this = AllocThis(ListItem); ListItem* this = AllocThis(ListItem);
this->value = strdup(value); this->value = xStrdup(value);
this->key = key; this->key = key;
this->moving = false;
return this; return this;
} }
@ -60,7 +71,7 @@ void ListItem_append(ListItem* this, const char* text) {
int oldLen = strlen(this->value); int oldLen = strlen(this->value);
int textLen = strlen(text); int textLen = strlen(text);
int newLen = strlen(this->value) + textLen; int newLen = strlen(this->value) + textLen;
this->value = realloc(this->value, newLen + 1); this->value = xRealloc(this->value, newLen + 1);
memcpy(this->value + oldLen, text, textLen); memcpy(this->value + oldLen, text, textLen);
this->value[newLen] = '\0'; this->value[newLen] = '\0';
} }
@ -69,7 +80,7 @@ const char* ListItem_getRef(ListItem* this) {
return this->value; return this->value;
} }
int ListItem_compare(const void* cast1, const void* cast2) { long ListItem_compare(const void* cast1, const void* cast2) {
ListItem* obj1 = (ListItem*) cast1; ListItem* obj1 = (ListItem*) cast1;
ListItem* obj2 = (ListItem*) cast2; ListItem* obj2 = (ListItem*) cast2;
return strcmp(obj1->value, obj2->value); return strcmp(obj1->value, obj2->value);

View File

@ -15,6 +15,7 @@ typedef struct ListItem_ {
Object super; Object super;
char* value; char* value;
int key; int key;
bool moving;
} ListItem; } ListItem;
@ -26,7 +27,7 @@ void ListItem_append(ListItem* this, const char* text);
const char* ListItem_getRef(ListItem* this); const char* ListItem_getRef(ListItem* this);
int ListItem_compare(const void* cast1, const void* cast2); long ListItem_compare(const void* cast1, const void* cast2);
#endif #endif

View File

@ -8,61 +8,47 @@ in the source distribution for its full text.
#include "LoadAverageMeter.h" #include "LoadAverageMeter.h"
#include "CRT.h" #include "CRT.h"
#include "Platform.h"
#include <assert.h>
/*{ /*{
#include "Meter.h" #include "Meter.h"
}*/ }*/
int LoadAverageMeter_attributes[] = { int LoadAverageMeter_attributes[] = {
LOAD_AVERAGE_FIFTEEN, LOAD_AVERAGE_FIVE, LOAD_AVERAGE_ONE LOAD_AVERAGE_ONE, LOAD_AVERAGE_FIVE, LOAD_AVERAGE_FIFTEEN
}; };
int LoadMeter_attributes[] = { LOAD }; int LoadMeter_attributes[] = { LOAD };
static inline void LoadAverageMeter_scan(double* one, double* five, double* fifteen) { static void LoadAverageMeter_updateValues(Meter* this, char* buffer, int size) {
int activeProcs, totalProcs, lastProc; Platform_getLoadAverage(&this->values[0], &this->values[1], &this->values[2]);
*one = 0; *five = 0; *fifteen = 0; xSnprintf(buffer, size, "%.2f/%.2f/%.2f", this->values[0], this->values[1], this->values[2]);
FILE *fd = fopen(PROCDIR "/loadavg", "r");
if (fd) {
int total = fscanf(fd, "%32lf %32lf %32lf %32d/%32d %32d", one, five, fifteen,
&activeProcs, &totalProcs, &lastProc);
(void) total;
assert(total == 6);
fclose(fd);
}
}
static void LoadAverageMeter_setValues(Meter* this, char* buffer, int size) {
LoadAverageMeter_scan(&this->values[2], &this->values[1], &this->values[0]);
snprintf(buffer, size, "%.2f/%.2f/%.2f", this->values[2], this->values[1], this->values[0]);
} }
static void LoadAverageMeter_display(Object* cast, RichString* out) { static void LoadAverageMeter_display(Object* cast, RichString* out) {
Meter* this = (Meter*)cast; Meter* this = (Meter*)cast;
char buffer[20]; char buffer[20];
sprintf(buffer, "%.2f ", this->values[2]); xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[0]);
RichString_write(out, CRT_colors[LOAD_AVERAGE_FIFTEEN], buffer); RichString_write(out, CRT_colors[LOAD_AVERAGE_ONE], buffer);
sprintf(buffer, "%.2f ", this->values[1]); xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[1]);
RichString_append(out, CRT_colors[LOAD_AVERAGE_FIVE], buffer); RichString_append(out, CRT_colors[LOAD_AVERAGE_FIVE], buffer);
sprintf(buffer, "%.2f ", this->values[0]); xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[2]);
RichString_append(out, CRT_colors[LOAD_AVERAGE_ONE], buffer); RichString_append(out, CRT_colors[LOAD_AVERAGE_FIFTEEN], buffer);
} }
static void LoadMeter_setValues(Meter* this, char* buffer, int size) { static void LoadMeter_updateValues(Meter* this, char* buffer, int size) {
double five, fifteen; double five, fifteen;
LoadAverageMeter_scan(&this->values[0], &five, &fifteen); Platform_getLoadAverage(&this->values[0], &five, &fifteen);
if (this->values[0] > this->total) { if (this->values[0] > this->total) {
this->total = this->values[0]; this->total = this->values[0];
} }
snprintf(buffer, size, "%.2f", this->values[0]); xSnprintf(buffer, size, "%.2f", this->values[0]);
} }
static void LoadMeter_display(Object* cast, RichString* out) { static void LoadMeter_display(Object* cast, RichString* out) {
Meter* this = (Meter*)cast; Meter* this = (Meter*)cast;
char buffer[20]; char buffer[20];
sprintf(buffer, "%.2f ", ((Meter*)this)->values[0]); xSnprintf(buffer, sizeof(buffer), "%.2f ", ((Meter*)this)->values[0]);
RichString_write(out, CRT_colors[LOAD], buffer); RichString_write(out, CRT_colors[LOAD], buffer);
} }
@ -72,13 +58,14 @@ MeterClass LoadAverageMeter_class = {
.delete = Meter_delete, .delete = Meter_delete,
.display = LoadAverageMeter_display, .display = LoadAverageMeter_display,
}, },
.setValues = LoadAverageMeter_setValues, .updateValues = LoadAverageMeter_updateValues,
.defaultMode = TEXT_METERMODE, .defaultMode = TEXT_METERMODE,
.maxItems = 3, .maxItems = 3,
.total = 100.0, .total = 100.0,
.attributes = LoadAverageMeter_attributes, .attributes = LoadAverageMeter_attributes,
.name = "LoadAverage", .name = "LoadAverage",
.uiName = "Load average", .uiName = "Load average",
.description = "Load averages: 1 minute, 5 minutes, 15 minutes",
.caption = "Load average: " .caption = "Load average: "
}; };
@ -88,11 +75,13 @@ MeterClass LoadMeter_class = {
.delete = Meter_delete, .delete = Meter_delete,
.display = LoadMeter_display, .display = LoadMeter_display,
}, },
.setValues = LoadMeter_setValues, .updateValues = LoadMeter_updateValues,
.defaultMode = TEXT_METERMODE, .defaultMode = TEXT_METERMODE,
.maxItems = 1,
.total = 100.0, .total = 100.0,
.attributes = LoadMeter_attributes, .attributes = LoadMeter_attributes,
.name = "Load", .name = "Load",
.uiName = "Load", .uiName = "Load",
.description = "Load: average of ready processes in the last minute",
.caption = "Load: " .caption = "Load: "
}; };

207
MainPanel.c Normal file
View File

@ -0,0 +1,207 @@
/*
htop - ColumnsPanel.c
(C) 2004-2015 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "MainPanel.h"
#include "Process.h"
#include "Platform.h"
#include "CRT.h"
#include <stdlib.h>
/*{
#include "Panel.h"
#include "Action.h"
#include "Settings.h"
typedef struct MainPanel_ {
Panel super;
State* state;
IncSet* inc;
Htop_Action *keys;
pid_t pidSearch;
} MainPanel;
typedef union {
int i;
void* v;
} Arg;
typedef bool(*MainPanel_ForeachProcessFn)(Process*, Arg);
#define MainPanel_getFunctionBar(this_) (((Panel*)(this_))->defaultBar)
}*/
static const char* const MainFunctions[] = {"Help ", "Setup ", "Search", "Filter", "Tree ", "SortBy", "Nice -", "Nice +", "Kill ", "Quit ", NULL};
void MainPanel_updateTreeFunctions(MainPanel* this, bool mode) {
FunctionBar* bar = MainPanel_getFunctionBar(this);
if (mode) {
FunctionBar_setLabel(bar, KEY_F(5), "Sorted");
FunctionBar_setLabel(bar, KEY_F(6), "Collap");
} else {
FunctionBar_setLabel(bar, KEY_F(5), "Tree ");
FunctionBar_setLabel(bar, KEY_F(6), "SortBy");
}
}
void MainPanel_pidSearch(MainPanel* this, int ch) {
Panel* super = (Panel*) this;
pid_t pid = ch-48 + this->pidSearch;
for (int i = 0; i < Panel_size(super); i++) {
Process* p = (Process*) Panel_get(super, i);
if (p && p->pid == pid) {
Panel_setSelected(super, i);
break;
}
}
this->pidSearch = pid * 10;
if (this->pidSearch > 10000000) {
this->pidSearch = 0;
}
}
static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
MainPanel* this = (MainPanel*) super;
HandlerResult result = IGNORED;
Htop_Reaction reaction = HTOP_OK;
if (EVENT_IS_HEADER_CLICK(ch)) {
int x = EVENT_HEADER_CLICK_GET_X(ch);
ProcessList* pl = this->state->pl;
Settings* settings = this->state->settings;
int hx = super->scrollH + x + 1;
ProcessField field = ProcessList_keyAt(pl, hx);
if (field == settings->sortKey) {
Settings_invertSortOrder(settings);
settings->treeView = false;
} else {
reaction |= Action_setSortKey(settings, field);
}
reaction |= HTOP_RECALCULATE | HTOP_REDRAW_BAR | HTOP_SAVE_SETTINGS;
result = HANDLED;
} else if (ch != ERR && this->inc->active) {
bool filterChanged = IncSet_handleKey(this->inc, ch, super, (IncMode_GetPanelValue) MainPanel_getValue, NULL);
if (filterChanged) {
this->state->pl->incFilter = IncSet_filter(this->inc);
reaction = HTOP_REFRESH | HTOP_REDRAW_BAR;
}
if (this->inc->found) {
reaction |= Action_follow(this->state);
reaction |= HTOP_KEEP_FOLLOWING;
}
result = HANDLED;
} else if (ch == 27) {
return HANDLED;
} else if (ch != ERR && ch > 0 && ch < KEY_MAX && this->keys[ch]) {
reaction |= (this->keys[ch])(this->state);
result = HANDLED;
} else if (isdigit(ch)) {
MainPanel_pidSearch(this, ch);
} else {
if (ch != ERR) {
this->pidSearch = 0;
} else {
reaction |= HTOP_KEEP_FOLLOWING;
}
}
if (reaction & HTOP_REDRAW_BAR) {
MainPanel_updateTreeFunctions(this, this->state->settings->treeView);
IncSet_drawBar(this->inc);
}
if (reaction & HTOP_UPDATE_PANELHDR) {
ProcessList_printHeader(this->state->pl, Panel_getHeader(super));
}
if (reaction & HTOP_REFRESH) {
result |= REDRAW;
}
if (reaction & HTOP_RECALCULATE) {
result |= RESCAN;
}
if (reaction & HTOP_SAVE_SETTINGS) {
this->state->settings->changed = true;
}
if (reaction & HTOP_QUIT) {
return BREAK_LOOP;
}
if (!(reaction & HTOP_KEEP_FOLLOWING)) {
this->state->pl->following = -1;
Panel_setSelectionColor(super, CRT_colors[PANEL_SELECTION_FOCUS]);
}
return result;
}
int MainPanel_selectedPid(MainPanel* this) {
Process* p = (Process*) Panel_getSelected((Panel*)this);
if (p) {
return p->pid;
}
return -1;
}
const char* MainPanel_getValue(MainPanel* this, int i) {
Process* p = (Process*) Panel_get((Panel*)this, i);
if (p)
return p->comm;
return "";
}
bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, Arg arg, bool* wasAnyTagged) {
Panel* super = (Panel*) this;
bool ok = true;
bool anyTagged = false;
for (int i = 0; i < Panel_size(super); i++) {
Process* p = (Process*) Panel_get(super, i);
if (p->tag) {
ok = fn(p, arg) && ok;
anyTagged = true;
}
}
if (!anyTagged) {
Process* p = (Process*) Panel_getSelected(super);
if (p) ok = fn(p, arg) && ok;
}
if (wasAnyTagged)
*wasAnyTagged = anyTagged;
return ok;
}
PanelClass MainPanel_class = {
.super = {
.extends = Class(Panel),
.delete = MainPanel_delete
},
.eventHandler = MainPanel_eventHandler
};
MainPanel* MainPanel_new() {
MainPanel* this = AllocThis(MainPanel);
Panel_init((Panel*) this, 1, 1, 1, 1, Class(Process), false, FunctionBar_new(MainFunctions, NULL, NULL));
this->keys = xCalloc(KEY_MAX, sizeof(Htop_Action));
this->inc = IncSet_new(MainPanel_getFunctionBar(this));
Action_setBindings(this->keys);
Platform_setBindings(this->keys);
return this;
}
void MainPanel_setState(MainPanel* this, State* state) {
this->state = state;
}
void MainPanel_delete(Object* object) {
Panel* super = (Panel*) object;
MainPanel* this = (MainPanel*) object;
Panel_done(super);
IncSet_delete(this->inc);
free(this->keys);
free(this);
}

52
MainPanel.h Normal file
View File

@ -0,0 +1,52 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_MainPanel
#define HEADER_MainPanel
/*
htop - ColumnsPanel.h
(C) 2004-2015 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "Panel.h"
#include "Action.h"
#include "Settings.h"
typedef struct MainPanel_ {
Panel super;
State* state;
IncSet* inc;
Htop_Action *keys;
pid_t pidSearch;
} MainPanel;
typedef union {
int i;
void* v;
} Arg;
typedef bool(*MainPanel_ForeachProcessFn)(Process*, Arg);
#define MainPanel_getFunctionBar(this_) (((Panel*)(this_))->defaultBar)
void MainPanel_updateTreeFunctions(MainPanel* this, bool mode);
void MainPanel_pidSearch(MainPanel* this, int ch);
int MainPanel_selectedPid(MainPanel* this);
const char* MainPanel_getValue(MainPanel* this, int i);
bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, Arg arg, bool* wasAnyTagged);
extern PanelClass MainPanel_class;
MainPanel* MainPanel_new();
void MainPanel_setState(MainPanel* this, State* state);
void MainPanel_delete(Object* object);
#endif

View File

@ -1,7 +1,9 @@
ACLOCAL_AMFLAGS = -I m4 ACLOCAL_AMFLAGS = -I m4
AUTOMAKE_OPTIONS = subdir-objects
bin_PROGRAMS = htop bin_PROGRAMS = htop
dist_man_MANS = htop.1 dist_man_MANS = htop.1
EXTRA_DIST = $(dist_man_MANS) htop.desktop htop.png scripts/MakeHeader.py \ EXTRA_DIST = $(dist_man_MANS) htop.desktop htop.png scripts/MakeHeader.py \
install-sh autogen.sh missing install-sh autogen.sh missing
@ -10,31 +12,188 @@ applications_DATA = htop.desktop
pixmapdir = $(datadir)/pixmaps pixmapdir = $(datadir)/pixmaps
pixmap_DATA = htop.png pixmap_DATA = htop.png
htop_CFLAGS = -pedantic -Wall -Wextra -std=c99 -rdynamic -D_XOPEN_SOURCE_EXTENDED -DSYSCONFDIR=\"$(sysconfdir)\" AM_CFLAGS = -pedantic -Wall $(wextra_flag) -std=c99 -D_XOPEN_SOURCE_EXTENDED -DSYSCONFDIR=\"$(sysconfdir)\" -I"$(top_srcdir)/$(my_htop_platform)"
AM_LDFLAGS =
AM_CPPFLAGS = -DNDEBUG AM_CPPFLAGS = -DNDEBUG
myhtopsources = AvailableMetersPanel.c CategoriesPanel.c CheckItem.c \ myhtopsources = AvailableMetersPanel.c CategoriesPanel.c CheckItem.c \
ClockMeter.c ColorsPanel.c ColumnsPanel.c CPUMeter.c CRT.c \ ClockMeter.c ColorsPanel.c ColumnsPanel.c CPUMeter.c CRT.c MainPanel.c \
DisplayOptionsPanel.c FunctionBar.c Hashtable.c Header.c htop.c ListItem.c \ DisplayOptionsPanel.c FunctionBar.c Hashtable.c Header.c htop.c ListItem.c \
LoadAverageMeter.c MemoryMeter.c Meter.c MetersPanel.c Object.c Panel.c \ LoadAverageMeter.c MemoryMeter.c Meter.c MetersPanel.c Object.c Panel.c \
BatteryMeter.c Process.c ProcessList.c RichString.c ScreenManager.c Settings.c \ BatteryMeter.c Process.c ProcessList.c RichString.c ScreenManager.c Settings.c \
IOPriorityPanel.c SignalsPanel.c String.c SwapMeter.c TasksMeter.c TraceScreen.c \ SignalsPanel.c StringUtils.c SwapMeter.c TasksMeter.c UptimeMeter.c \
UptimeMeter.c UsersTable.c Vector.c AvailableColumnsPanel.c AffinityPanel.c \ TraceScreen.c UsersTable.c Vector.c AvailableColumnsPanel.c AffinityPanel.c \
HostnameMeter.c OpenFilesScreen.c Affinity.c IOPriority.c IncSet.c HostnameMeter.c OpenFilesScreen.c Affinity.c IncSet.c Action.c EnvScreen.c \
InfoScreen.c XAlloc.c
myhtopheaders = AvailableColumnsPanel.h AvailableMetersPanel.h \ myhtopheaders = AvailableColumnsPanel.h AvailableMetersPanel.h \
CategoriesPanel.h CheckItem.h ClockMeter.h ColorsPanel.h ColumnsPanel.h \ CategoriesPanel.h CheckItem.h ClockMeter.h ColorsPanel.h ColumnsPanel.h \
IOPriorityPanel.h CPUMeter.h CRT.h DisplayOptionsPanel.h FunctionBar.h \ CPUMeter.h CRT.h MainPanel.h DisplayOptionsPanel.h FunctionBar.h \
Hashtable.h Header.h htop.h ListItem.h LoadAverageMeter.h MemoryMeter.h \ Hashtable.h Header.h htop.h ListItem.h LoadAverageMeter.h MemoryMeter.h \
BatteryMeter.h Meter.h MetersPanel.h Object.h Panel.h ProcessList.h RichString.h \ BatteryMeter.h Meter.h MetersPanel.h Object.h Panel.h ProcessList.h RichString.h \
ScreenManager.h Settings.h SignalsPanel.h String.h SwapMeter.h TasksMeter.h \ ScreenManager.h Settings.h SignalsPanel.h StringUtils.h SwapMeter.h \
TraceScreen.h UptimeMeter.h UsersTable.h Vector.h Process.h AffinityPanel.h \ TasksMeter.h UptimeMeter.h TraceScreen.h UsersTable.h Vector.h Process.h \
HostnameMeter.h OpenFilesScreen.h Affinity.h IOPriority.h IncSet.h AffinityPanel.h HostnameMeter.h OpenFilesScreen.h Affinity.h IncSet.h Action.h \
EnvScreen.h InfoScreen.h XAlloc.h
all_platform_headers =
# Linux
# -----
linux_platform_headers = \
linux/Platform.h \
linux/IOPriorityPanel.h \
linux/IOPriority.h \
linux/LinuxProcess.h \
linux/LinuxProcessList.h \
linux/LinuxCRT.h \
linux/Battery.h
all_platform_headers += $(linux_platform_headers)
if HTOP_LINUX
AM_CFLAGS += -rdynamic
myhtopplatsources = linux/Platform.c linux/IOPriorityPanel.c linux/IOPriority.c \
linux/LinuxProcess.c linux/LinuxProcessList.c linux/LinuxCRT.c linux/Battery.c
myhtopplatheaders = $(linux_platform_headers)
endif
# FreeBSD
# -------
freebsd_platform_headers = \
freebsd/Platform.h \
freebsd/FreeBSDProcessList.h \
freebsd/FreeBSDProcess.h \
freebsd/FreeBSDCRT.h \
freebsd/Battery.h
all_platform_headers += $(freebsd_platform_headers)
if HTOP_FREEBSD
myhtopplatsources = freebsd/Platform.c freebsd/FreeBSDProcessList.c \
freebsd/FreeBSDProcess.c freebsd/FreeBSDCRT.c freebsd/Battery.c
myhtopplatheaders = $(freebsd_platform_headers)
endif
# DragonFlyBSD
# ------------
dragonflybsd_platform_headers = \
dragonflybsd/Platform.h \
dragonflybsd/DragonFlyBSDProcessList.h \
dragonflybsd/DragonFlyBSDProcess.h \
dragonflybsd/DragonFlyBSDCRT.h \
dragonflybsd/Battery.h
all_platform_headers += $(dragonflybsd_platform_headers)
if HTOP_DRAGONFLYBSD
AM_LDFLAGS += -lkvm -lkinfo -lexecinfo
myhtopplatsources = dragonflybsd/Platform.c dragonflybsd/DragonFlyBSDProcessList.c \
dragonflybsd/DragonFlyBSDProcess.c dragonflybsd/DragonFlyBSDCRT.c dragonflybsd/Battery.c
myhtopplatheaders = $(dragonflybsd_platform_headers)
endif
# OpenBSD
# -------
openbsd_platform_headers = \
openbsd/Platform.h \
openbsd/OpenBSDProcessList.h \
openbsd/OpenBSDProcess.h \
openbsd/OpenBSDCRT.h \
openbsd/Battery.h
all_platform_headers += $(openbsd_platform_headers)
if HTOP_OPENBSD
myhtopplatsources = openbsd/Platform.c openbsd/OpenBSDProcessList.c \
openbsd/OpenBSDProcess.c openbsd/OpenBSDCRT.c openbsd/Battery.c
myhtopplatheaders = $(openbsd_platform_headers)
endif
# Darwin
# ------
darwin_platform_headers = \
darwin/Platform.h \
darwin/DarwinProcess.h \
darwin/DarwinProcessList.h \
darwin/DarwinCRT.h \
darwin/Battery.h
all_platform_headers += $(darwin_platform_headers)
if HTOP_DARWIN
AM_LDFLAGS += -framework IOKit -framework CoreFoundation
myhtopplatsources = darwin/Platform.c darwin/DarwinProcess.c \
darwin/DarwinProcessList.c darwin/DarwinCRT.c darwin/Battery.c
myhtopplatheaders = $(darwin_platform_headers)
endif
# Solaris
# -------
solaris_platform_headers = \
solaris/Platform.h \
solaris/SolarisProcess.h \
solaris/SolarisProcessList.h \
solaris/SolarisCRT.h \
solaris/Battery.h
all_platform_headers += $(solaris_platform_headers)
if HTOP_SOLARIS
myhtopplatsources = solaris/Platform.c \
solaris/SolarisProcess.c solaris/SolarisProcessList.c \
solaris/SolarisCRT.c solaris/Battery.c
myhtopplatheaders = $(solaris_platform_headers)
endif
# Unsupported
# -----------
unsupported_platform_headers = \
unsupported/Platform.h \
unsupported/UnsupportedProcess.h \
unsupported/UnsupportedProcessList.h \
unsupported/UnsupportedCRT.h \
unsupported/Battery.h
all_platform_headers += $(unsupported_platform_headers)
if HTOP_UNSUPPORTED
myhtopplatsources = unsupported/Platform.c \
unsupported/UnsupportedProcess.c unsupported/UnsupportedProcessList.c \
unsupported/UnsupportedCRT.c unsupported/Battery.c
myhtopplatheaders = $(unsupported_platform_headers)
endif
# ----
SUFFIXES = .h SUFFIXES = .h
BUILT_SOURCES = $(myhtopheaders) BUILT_SOURCES = $(myhtopheaders) $(myhtopplatheaders)
htop_SOURCES = $(myhtopheaders) $(myhtopsources) config.h htop_SOURCES = $(myhtopheaders) $(myhtopplatheaders) $(myhtopsources) $(myhtopplatsources) config.h
.PHONY: htop-headers clean-htop-headers
htop-headers: $(myhtopheaders) $(all_platform_headers)
clean-htop-headers:
-rm -f $(myhtopheaders) $(all_platform_headers)
target:
echo $(htop_SOURCES)
profile: profile:
$(MAKE) all CFLAGS="-pg" AM_CPPFLAGS="-pg -O2 -DNDEBUG" $(MAKE) all CFLAGS="-pg" AM_CPPFLAGS="-pg -O2 -DNDEBUG"
@ -42,8 +201,18 @@ profile:
debug: debug:
$(MAKE) all CFLAGS="" AM_CPPFLAGS="-ggdb -DDEBUG" $(MAKE) all CFLAGS="" AM_CPPFLAGS="-ggdb -DDEBUG"
coverage:
$(MAKE) all CFLAGS="" AM_CPPFLAGS="-fprofile-arcs -ftest-coverage -DDEBUG" LDFLAGS="-lgcov"
.c.h: .c.h:
@srcdir@/scripts/MakeHeader.py $< @srcdir@/scripts/MakeHeader.py $<
cppcheck: cppcheck:
cppcheck -q -v . --enable=all -DHAVE_CGROUP -DHAVE_OPENVZ -DHAVE_TASKSTATS cppcheck -q -v . --enable=all -DHAVE_CGROUP -DHAVE_OPENVZ -DHAVE_TASKSTATS
.PHONY: lcov
lcov:
mkdir -p lcov
lcov --capture --directory . --output-file coverage.info
genhtml coverage.info --output-directory lcov

View File

@ -8,7 +8,7 @@ in the source distribution for its full text.
#include "MemoryMeter.h" #include "MemoryMeter.h"
#include "CRT.h" #include "CRT.h"
#include "ProcessList.h" #include "Platform.h"
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -24,37 +24,33 @@ int MemoryMeter_attributes[] = {
MEMORY_USED, MEMORY_BUFFERS, MEMORY_CACHE MEMORY_USED, MEMORY_BUFFERS, MEMORY_CACHE
}; };
static void MemoryMeter_setValues(Meter* this, char* buffer, int size) { static void MemoryMeter_updateValues(Meter* this, char* buffer, int size) {
long int usedMem = this->pl->usedMem; int written;
long int buffersMem = this->pl->buffersMem; Platform_setMemoryValues(this);
long int cachedMem = this->pl->cachedMem;
usedMem -= buffersMem + cachedMem; written = Meter_humanUnit(buffer, this->values[0], size);
this->total = this->pl->totalMem; buffer += written;
this->values[0] = usedMem; if ((size -= written) > 0) {
this->values[1] = buffersMem; *buffer++ = '/';
this->values[2] = cachedMem; size--;
snprintf(buffer, size, "%ld/%ldMB", (long int) usedMem / 1024, (long int) this->total / 1024); Meter_humanUnit(buffer, this->total, size);
}
} }
static void MemoryMeter_display(Object* cast, RichString* out) { static void MemoryMeter_display(Object* cast, RichString* out) {
char buffer[50]; char buffer[50];
Meter* this = (Meter*)cast; Meter* this = (Meter*)cast;
int k = 1024; const char* format = "%ldM ";
long int totalMem = this->total / k;
long int usedMem = this->values[0] / k;
long int buffersMem = this->values[1] / k;
long int cachedMem = this->values[2] / k;
RichString_write(out, CRT_colors[METER_TEXT], ":"); RichString_write(out, CRT_colors[METER_TEXT], ":");
sprintf(buffer, format, totalMem); Meter_humanUnit(buffer, this->total, 50);
RichString_append(out, CRT_colors[METER_VALUE], buffer); RichString_append(out, CRT_colors[METER_VALUE], buffer);
sprintf(buffer, format, usedMem); Meter_humanUnit(buffer, this->values[0], 50);
RichString_append(out, CRT_colors[METER_TEXT], "used:"); RichString_append(out, CRT_colors[METER_TEXT], " used:");
RichString_append(out, CRT_colors[MEMORY_USED], buffer); RichString_append(out, CRT_colors[MEMORY_USED], buffer);
sprintf(buffer, format, buffersMem); Meter_humanUnit(buffer, this->values[1], 50);
RichString_append(out, CRT_colors[METER_TEXT], "buffers:"); RichString_append(out, CRT_colors[METER_TEXT], " buffers:");
RichString_append(out, CRT_colors[MEMORY_BUFFERS_TEXT], buffer); RichString_append(out, CRT_colors[MEMORY_BUFFERS_TEXT], buffer);
sprintf(buffer, format, cachedMem); Meter_humanUnit(buffer, this->values[2], 50);
RichString_append(out, CRT_colors[METER_TEXT], "cache:"); RichString_append(out, CRT_colors[METER_TEXT], " cache:");
RichString_append(out, CRT_colors[MEMORY_CACHE], buffer); RichString_append(out, CRT_colors[MEMORY_CACHE], buffer);
} }
@ -64,12 +60,12 @@ MeterClass MemoryMeter_class = {
.delete = Meter_delete, .delete = Meter_delete,
.display = MemoryMeter_display, .display = MemoryMeter_display,
}, },
.setValues = MemoryMeter_setValues, .updateValues = MemoryMeter_updateValues,
.defaultMode = BAR_METERMODE, .defaultMode = BAR_METERMODE,
.maxItems = 3, .maxItems = 3,
.total = 100.0, .total = 100.0,
.attributes = MemoryMeter_attributes, .attributes = MemoryMeter_attributes,
"Memory", .name = "Memory",
"Memory", .uiName = "Memory",
"Mem" .caption = "Mem"
}; };

274
Meter.c
View File

@ -7,19 +7,10 @@ in the source distribution for its full text.
#include "Meter.h" #include "Meter.h"
#include "CPUMeter.h"
#include "MemoryMeter.h"
#include "SwapMeter.h"
#include "TasksMeter.h"
#include "LoadAverageMeter.h"
#include "UptimeMeter.h"
#include "BatteryMeter.h"
#include "ClockMeter.h"
#include "HostnameMeter.h"
#include "RichString.h" #include "RichString.h"
#include "Object.h" #include "Object.h"
#include "CRT.h" #include "CRT.h"
#include "String.h" #include "StringUtils.h"
#include "ListItem.h" #include "ListItem.h"
#include "Settings.h" #include "Settings.h"
@ -30,18 +21,23 @@ in the source distribution for its full text.
#include <assert.h> #include <assert.h>
#include <sys/time.h> #include <sys/time.h>
#define METER_BUFFER_LEN 128 #define METER_BUFFER_LEN 256
#define GRAPH_DELAY (DEFAULT_DELAY/2)
#define GRAPH_HEIGHT 4 /* Unit: rows (lines) */
/*{ /*{
#include "ListItem.h" #include "ListItem.h"
#include "ProcessList.h"
#include <sys/time.h>
typedef struct Meter_ Meter; typedef struct Meter_ Meter;
typedef void(*Meter_Init)(Meter*); typedef void(*Meter_Init)(Meter*);
typedef void(*Meter_Done)(Meter*); typedef void(*Meter_Done)(Meter*);
typedef void(*Meter_UpdateMode)(Meter*, int); typedef void(*Meter_UpdateMode)(Meter*, int);
typedef void(*Meter_SetValues)(Meter*, char*, int); typedef void(*Meter_UpdateValues)(Meter*, char*, int);
typedef void(*Meter_Draw)(Meter*, int, int, int); typedef void(*Meter_Draw)(Meter*, int, int, int);
typedef struct MeterClass_ { typedef struct MeterClass_ {
@ -50,13 +46,14 @@ typedef struct MeterClass_ {
const Meter_Done done; const Meter_Done done;
const Meter_UpdateMode updateMode; const Meter_UpdateMode updateMode;
const Meter_Draw draw; const Meter_Draw draw;
const Meter_SetValues setValues; const Meter_UpdateValues updateValues;
const int defaultMode; const int defaultMode;
const double total; const double total;
const int* attributes; const int* attributes;
const char* name; const char* name;
const char* uiName; const char* uiName;
const char* caption; const char* caption;
const char* description;
const char maxItems; const char maxItems;
char curItems; char curItems;
} MeterClass; } MeterClass;
@ -69,7 +66,8 @@ typedef struct MeterClass_ {
#define Meter_updateMode(this_, m_) As_Meter(this_)->updateMode((Meter*)(this_), m_) #define Meter_updateMode(this_, m_) As_Meter(this_)->updateMode((Meter*)(this_), m_)
#define Meter_drawFn(this_) As_Meter(this_)->draw #define Meter_drawFn(this_) As_Meter(this_)->draw
#define Meter_doneFn(this_) As_Meter(this_)->done #define Meter_doneFn(this_) As_Meter(this_)->done
#define Meter_setValues(this_, c_, i_) As_Meter(this_)->setValues((Meter*)(this_), c_, i_) #define Meter_updateValues(this_, buf_, sz_) \
As_Meter(this_)->updateValues((Meter*)(this_), buf_, sz_)
#define Meter_defaultMode(this_) As_Meter(this_)->defaultMode #define Meter_defaultMode(this_) As_Meter(this_)->defaultMode
#define Meter_getItems(this_) As_Meter(this_)->curItems #define Meter_getItems(this_) As_Meter(this_)->curItems
#define Meter_setItems(this_, n_) As_Meter(this_)->curItems = (n_) #define Meter_setItems(this_, n_) As_Meter(this_)->curItems = (n_)
@ -86,7 +84,7 @@ struct Meter_ {
int param; int param;
void* drawData; void* drawData;
int h; int h;
ProcessList* pl; struct ProcessList_* pl;
double* values; double* values;
double total; double total;
}; };
@ -119,6 +117,9 @@ typedef struct GraphData_ {
#ifndef MAX #ifndef MAX
#define MAX(a,b) ((a)>(b)?(a):(b)) #define MAX(a,b) ((a)>(b)?(a):(b))
#endif #endif
#ifndef CLAMP
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif
MeterClass Meter_class = { MeterClass Meter_class = {
.super = { .super = {
@ -126,47 +127,53 @@ MeterClass Meter_class = {
} }
}; };
MeterClass* Meter_types[] = { Meter* Meter_new(struct ProcessList_* pl, int param, MeterClass* type) {
&CPUMeter_class, Meter* this = xCalloc(1, sizeof(Meter));
&ClockMeter_class,
&LoadAverageMeter_class,
&LoadMeter_class,
&MemoryMeter_class,
&SwapMeter_class,
&TasksMeter_class,
&UptimeMeter_class,
&BatteryMeter_class,
&HostnameMeter_class,
&AllCPUsMeter_class,
&AllCPUs2Meter_class,
&LeftCPUsMeter_class,
&RightCPUsMeter_class,
&LeftCPUs2Meter_class,
&RightCPUs2Meter_class,
&BlankMeter_class,
NULL
};
Meter* Meter_new(ProcessList* pl, int param, MeterClass* type) {
Meter* this = calloc(1, sizeof(Meter));
Object_setClass(this, type); Object_setClass(this, type);
this->h = 1; this->h = 1;
this->param = param; this->param = param;
this->pl = pl; this->pl = pl;
char maxItems = type->maxItems; type->curItems = type->maxItems;
if (maxItems == 0) { this->values = xCalloc(type->maxItems, sizeof(double));
maxItems = 1;
}
type->curItems = maxItems;
this->values = calloc(maxItems, sizeof(double));
this->total = type->total; this->total = type->total;
this->caption = strdup(type->caption); this->caption = xStrdup(type->caption);
if (Meter_initFn(this)) if (Meter_initFn(this))
Meter_init(this); Meter_init(this);
Meter_setMode(this, type->defaultMode); Meter_setMode(this, type->defaultMode);
return this; return this;
} }
int Meter_humanUnit(char* buffer, unsigned long int value, int size) {
const char * prefix = "KMGTPEZY";
unsigned long int powi = 1;
unsigned int written, powj = 1, precision = 2;
for(;;) {
if (value / 1024 < powi)
break;
if (prefix[1] == '\0')
break;
powi *= 1024;
++prefix;
}
if (*prefix == 'K')
precision = 0;
for (; precision > 0; precision--) {
powj *= 10;
if (value / powi < powj)
break;
}
written = snprintf(buffer, size, "%.*f%c",
precision, (double) value / powi, *prefix);
return written;
}
void Meter_delete(Object* cast) { void Meter_delete(Object* cast) {
if (!cast) if (!cast)
return; return;
@ -174,8 +181,7 @@ void Meter_delete(Object* cast) {
if (Meter_doneFn(this)) { if (Meter_doneFn(this)) {
Meter_done(this); Meter_done(this);
} }
if (this->drawData) free(this->drawData);
free(this->drawData);
free(this->caption); free(this->caption);
free(this->values); free(this->values);
free(this); free(this);
@ -183,7 +189,7 @@ void Meter_delete(Object* cast) {
void Meter_setCaption(Meter* this, const char* caption) { void Meter_setCaption(Meter* this, const char* caption) {
free(this->caption); free(this->caption);
this->caption = strdup(caption); this->caption = xStrdup(caption);
} }
static inline void Meter_displayBuffer(Meter* this, char* buffer, RichString* out) { static inline void Meter_displayBuffer(Meter* this, char* buffer, RichString* out) {
@ -206,8 +212,7 @@ void Meter_setMode(Meter* this, int modeIndex) {
Meter_updateMode(this, modeIndex); Meter_updateMode(this, modeIndex);
} else { } else {
assert(modeIndex >= 1); assert(modeIndex >= 1);
if (this->drawData) free(this->drawData);
free(this->drawData);
this->drawData = NULL; this->drawData = NULL;
MeterMode* mode = Meter_modes[modeIndex]; MeterMode* mode = Meter_modes[modeIndex];
@ -217,34 +222,35 @@ void Meter_setMode(Meter* this, int modeIndex) {
this->mode = modeIndex; this->mode = modeIndex;
} }
ListItem* Meter_toListItem(Meter* this) { ListItem* Meter_toListItem(Meter* this, bool moving) {
char mode[21]; char mode[21];
if (this->mode) if (this->mode)
snprintf(mode, 20, " [%s]", Meter_modes[this->mode]->uiName); xSnprintf(mode, 20, " [%s]", Meter_modes[this->mode]->uiName);
else else
mode[0] = '\0'; mode[0] = '\0';
char number[11]; char number[11];
if (this->param > 0) if (this->param > 0)
snprintf(number, 10, " %d", this->param); xSnprintf(number, 10, " %d", this->param);
else else
number[0] = '\0'; number[0] = '\0';
char buffer[51]; char buffer[51];
snprintf(buffer, 50, "%s%s%s", Meter_uiName(this), number, mode); xSnprintf(buffer, 50, "%s%s%s", Meter_uiName(this), number, mode);
return ListItem_new(buffer, 0); ListItem* li = ListItem_new(buffer, 0);
li->moving = moving;
return li;
} }
/* ---------- TextMeterMode ---------- */ /* ---------- TextMeterMode ---------- */
static void TextMeterMode_draw(Meter* this, int x, int y, int w) { static void TextMeterMode_draw(Meter* this, int x, int y, int w) {
char buffer[METER_BUFFER_LEN]; char buffer[METER_BUFFER_LEN];
Meter_setValues(this, buffer, METER_BUFFER_LEN - 1); Meter_updateValues(this, buffer, METER_BUFFER_LEN - 1);
(void) w; (void) w;
attrset(CRT_colors[METER_TEXT]); attrset(CRT_colors[METER_TEXT]);
mvaddstr(y, x, this->caption); mvaddstr(y, x, this->caption);
int captionLen = strlen(this->caption); int captionLen = strlen(this->caption);
x += captionLen; x += captionLen;
mvhline(y, x, ' ', CRT_colors[DEFAULT_COLOR]);
attrset(CRT_colors[RESET_COLOR]); attrset(CRT_colors[RESET_COLOR]);
RichString_begin(out); RichString_begin(out);
Meter_displayBuffer(this, buffer, &out); Meter_displayBuffer(this, buffer, &out);
@ -254,11 +260,11 @@ static void TextMeterMode_draw(Meter* this, int x, int y, int w) {
/* ---------- BarMeterMode ---------- */ /* ---------- BarMeterMode ---------- */
static char BarMeterMode_characters[] = "|#*@$%&"; static const char BarMeterMode_characters[] = "|#*@$%&.";
static void BarMeterMode_draw(Meter* this, int x, int y, int w) { static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
char buffer[METER_BUFFER_LEN]; char buffer[METER_BUFFER_LEN];
Meter_setValues(this, buffer, METER_BUFFER_LEN - 1); Meter_updateValues(this, buffer, METER_BUFFER_LEN - 1);
w -= 2; w -= 2;
attrset(CRT_colors[METER_TEXT]); attrset(CRT_colors[METER_TEXT]);
@ -280,19 +286,15 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
char bar[w + 1]; char bar[w + 1];
int blockSizes[10]; int blockSizes[10];
for (int i = 0; i < w; i++)
bar[i] = ' ';
const size_t barOffset = w - MIN((int)strlen(buffer), w); xSnprintf(bar, w + 1, "%*.*s", w, w, buffer);
snprintf(bar + barOffset, w - barOffset + 1, "%s", buffer);
// First draw in the bar[] buffer... // First draw in the bar[] buffer...
int offset = 0; int offset = 0;
int items = Meter_getItems(this); int items = Meter_getItems(this);
for (int i = 0; i < items; i++) { for (int i = 0; i < items; i++) {
double value = this->values[i]; double value = this->values[i];
value = MAX(value, 0); value = CLAMP(value, 0.0, this->total);
value = MIN(value, this->total);
if (value > 0) { if (value > 0) {
blockSizes[i] = ceil((value/this->total) * w); blockSizes[i] = ceil((value/this->total) * w);
} else { } else {
@ -300,7 +302,7 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
} }
int nextOffset = offset + blockSizes[i]; int nextOffset = offset + blockSizes[i];
// (Control against invalid values) // (Control against invalid values)
nextOffset = MIN(MAX(nextOffset, 0), w); nextOffset = CLAMP(nextOffset, 0, w);
for (int j = offset; j < nextOffset; j++) for (int j = offset; j < nextOffset; j++)
if (bar[j] == ' ') { if (bar[j] == ' ') {
if (CRT_colorScheme == COLORSCHEME_MONOCHROME) { if (CRT_colorScheme == COLORSCHEME_MONOCHROME) {
@ -318,8 +320,7 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
attrset(CRT_colors[Meter_attributes(this)[i]]); attrset(CRT_colors[Meter_attributes(this)[i]]);
mvaddnstr(y, x + offset, bar + offset, blockSizes[i]); mvaddnstr(y, x + offset, bar + offset, blockSizes[i]);
offset += blockSizes[i]; offset += blockSizes[i];
offset = MAX(offset, 0); offset = CLAMP(offset, 0, w);
offset = MIN(offset, w);
} }
if (offset < w) { if (offset < w) {
attrset(CRT_colors[BAR_SHADOW]); attrset(CRT_colors[BAR_SHADOW]);
@ -332,37 +333,63 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
/* ---------- GraphMeterMode ---------- */ /* ---------- GraphMeterMode ---------- */
#define DrawDot(a,y,c) do { attrset(a); mvaddch(y, x+k, c); } while(0) #ifdef HAVE_LIBNCURSESW
static int GraphMeterMode_colors[21] = { #define PIXPERROW_UTF8 4
GRAPH_1, GRAPH_1, GRAPH_1, static const char* const GraphMeterMode_dotsUtf8[] = {
GRAPH_2, GRAPH_2, GRAPH_2, /*00*/" ", /*01*/"", /*02*/"", /*03*/"", /*04*/ "",
GRAPH_3, GRAPH_3, GRAPH_3, /*10*/"", /*11*/"", /*12*/"", /*13*/"", /*14*/ "",
GRAPH_4, GRAPH_4, GRAPH_4, /*20*/"", /*21*/"", /*22*/"", /*23*/"", /*24*/ "",
GRAPH_5, GRAPH_5, GRAPH_6, /*30*/"", /*31*/"", /*32*/"", /*33*/"", /*34*/ "",
GRAPH_7, GRAPH_7, GRAPH_7, /*40*/"", /*41*/"", /*42*/"", /*43*/"", /*44*/ ""
GRAPH_8, GRAPH_8, GRAPH_9
}; };
static const char* GraphMeterMode_characters = "^`'-.,_~'`-.,_~'`-.,_"; #endif
#define PIXPERROW_ASCII 2
static const char* const GraphMeterMode_dotsAscii[] = {
/*00*/" ", /*01*/".", /*02*/":",
/*10*/".", /*11*/".", /*12*/":",
/*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) this->drawData = calloc(1, sizeof(GraphData)); if (!this->drawData) this->drawData = xCalloc(1, sizeof(GraphData));
GraphData* data = (GraphData*) this->drawData; GraphData* data = (GraphData*) this->drawData;
const int nValues = METER_BUFFER_LEN; const int nValues = METER_BUFFER_LEN;
#ifdef HAVE_LIBNCURSESW
if (CRT_utf8) {
GraphMeterMode_dots = GraphMeterMode_dotsUtf8;
GraphMeterMode_pixPerRow = PIXPERROW_UTF8;
} else
#endif
{
GraphMeterMode_dots = GraphMeterMode_dotsAscii;
GraphMeterMode_pixPerRow = PIXPERROW_ASCII;
}
attrset(CRT_colors[METER_TEXT]);
int captionLen = 3;
mvaddnstr(y, x, this->caption, captionLen);
x += captionLen;
w -= captionLen;
struct timeval now; struct timeval now;
gettimeofday(&now, NULL); gettimeofday(&now, NULL);
if (!timercmp(&now, &(data->time), <)) { if (!timercmp(&now, &(data->time), <)) {
struct timeval delay = { .tv_sec = (int)(DEFAULT_DELAY/10), .tv_usec = (DEFAULT_DELAY-((int)(DEFAULT_DELAY/10)*10)) * 100000 }; struct timeval delay = { .tv_sec = (int)(CRT_delay/10), .tv_usec = (CRT_delay-((int)(CRT_delay/10)*10)) * 100000 };
timeradd(&now, &delay, &(data->time)); timeradd(&now, &delay, &(data->time));
for (int i = 0; i < nValues - 1; i++) for (int i = 0; i < nValues - 1; i++)
data->values[i] = data->values[i+1]; data->values[i] = data->values[i+1];
char buffer[nValues]; char buffer[nValues];
Meter_setValues(this, buffer, nValues - 1); Meter_updateValues(this, buffer, nValues - 1);
double value = 0.0; double value = 0.0;
int items = Meter_getItems(this); int items = Meter_getItems(this);
@ -372,55 +399,75 @@ static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
data->values[nValues - 1] = value; data->values[nValues - 1] = value;
} }
for (int i = nValues - w, k = 0; i < nValues; i++, k++) { int i = nValues - (w*2) + 2, k = 0;
double value = data->values[i]; if (i < 0) {
DrawDot( CRT_colors[DEFAULT_COLOR], y, ' ' ); k = -i/2;
DrawDot( CRT_colors[DEFAULT_COLOR], y+1, ' ' ); i = 0;
DrawDot( CRT_colors[DEFAULT_COLOR], y+2, ' ' ); }
for (; i < nValues - 1; i+=2, k++) {
int pix = GraphMeterMode_pixPerRow * GRAPH_HEIGHT;
int v1 = CLAMP((int) lround(data->values[i] * pix), 1, pix);
int v2 = CLAMP((int) lround(data->values[i+1] * pix), 1, pix);
double threshold = 1.00; int colorIdx = GRAPH_1;
for (int j = 0; j < 21; j++, threshold -= 0.05) for (int line = 0; line < GRAPH_HEIGHT; line++) {
if (value >= threshold) { int line1 = CLAMP(v1 - (GraphMeterMode_pixPerRow * (GRAPH_HEIGHT - 1 - line)), 0, GraphMeterMode_pixPerRow);
DrawDot(CRT_colors[GraphMeterMode_colors[j]], y+(j/7.0), GraphMeterMode_characters[j]); int line2 = CLAMP(v2 - (GraphMeterMode_pixPerRow * (GRAPH_HEIGHT - 1 - line)), 0, GraphMeterMode_pixPerRow);
break;
} attrset(CRT_colors[colorIdx]);
mvaddstr(y+line, x+k, GraphMeterMode_dots[line1 * (GraphMeterMode_pixPerRow + 1) + line2]);
colorIdx = GRAPH_2;
}
} }
attrset(CRT_colors[RESET_COLOR]); attrset(CRT_colors[RESET_COLOR]);
} }
/* ---------- LEDMeterMode ---------- */ /* ---------- LEDMeterMode ---------- */
static const char* LEDMeterMode_digitsAscii[3][10] = { static const char* const LEDMeterMode_digitsAscii[] = {
{ " __ "," "," __ "," __ "," "," __ "," __ "," __ "," __ "," __ "}, " __ "," "," __ "," __ "," "," __ "," __ "," __ "," __ "," __ ",
{ "| |"," |"," __|"," __|","|__|","|__ ","|__ "," |","|__|","|__|"}, "| |"," |"," __|"," __|","|__|","|__ ","|__ "," |","|__|","|__|",
{ "|__|"," |","|__ "," __|"," |"," __|","|__|"," |","|__|"," __|"}, "|__|"," |","|__ "," __|"," |"," __|","|__|"," |","|__|"," __|"
}; };
static const char* LEDMeterMode_digitsUtf8[3][10] = { #ifdef HAVE_LIBNCURSESW
{ "┌──┐","","╶──┐","╶──┐","╷ ╷","┌──╴","┌──╴","╶──┐","┌──┐","┌──┐"},
{ "│ │","","┌──┘"," ──┤","└──┤","└──┐","├──┐","","├──┤","└──┤"}, static const char* const LEDMeterMode_digitsUtf8[] = {
{ "──"," ","──","╶──"," ","──","──","","──"," ──"}, "──"," ","──","╶──"," ","──","──","╶──┐","──","──",
"│ │","","┌──┘"," ──┤","└──┤","└──┐","├──┐","","├──┤","└──┤",
"└──┘","","└──╴","╶──┘","","╶──┘","└──┘","","└──┘"," ──┘"
}; };
#endif
static const char* const* LEDMeterMode_digits;
static void LEDMeterMode_drawDigit(int x, int y, int n) { static void LEDMeterMode_drawDigit(int x, int y, int n) {
if (CRT_utf8) { for (int i = 0; i < 3; i++)
for (int i = 0; i < 3; i++) mvaddstr(y+i, x, LEDMeterMode_digits[i * 10 + n]);
mvaddstr(y+i, x, LEDMeterMode_digitsUtf8[i][n]);
} else {
for (int i = 0; i < 3; i++)
mvaddstr(y+i, x, LEDMeterMode_digitsAscii[i][n]);
}
} }
static void LEDMeterMode_draw(Meter* this, int x, int y, int w) { static void LEDMeterMode_draw(Meter* this, int x, int y, int w) {
(void) w; (void) w;
#ifdef HAVE_LIBNCURSESW
if (CRT_utf8)
LEDMeterMode_digits = LEDMeterMode_digitsUtf8;
else
#endif
LEDMeterMode_digits = LEDMeterMode_digitsAscii;
char buffer[METER_BUFFER_LEN]; char buffer[METER_BUFFER_LEN];
Meter_setValues(this, buffer, METER_BUFFER_LEN - 1); Meter_updateValues(this, buffer, METER_BUFFER_LEN - 1);
RichString_begin(out); RichString_begin(out);
Meter_displayBuffer(this, buffer, &out); Meter_displayBuffer(this, buffer, &out);
int yText = CRT_utf8 ? y+1 : y+2; int yText =
#ifdef HAVE_LIBNCURSESW
CRT_utf8 ? y+1 :
#endif
y+2;
attrset(CRT_colors[LED_COLOR]); attrset(CRT_colors[LED_COLOR]);
mvaddstr(yText, x, this->caption); mvaddstr(yText, x, this->caption);
int xx = x + strlen(this->caption); int xx = x + strlen(this->caption);
@ -453,7 +500,7 @@ static MeterMode TextMeterMode = {
static MeterMode GraphMeterMode = { static MeterMode GraphMeterMode = {
.uiName = "Graph", .uiName = "Graph",
.h = 3, .h = GRAPH_HEIGHT,
.draw = GraphMeterMode_draw, .draw = GraphMeterMode_draw,
}; };
@ -474,7 +521,7 @@ MeterMode* Meter_modes[] = {
/* Blank meter */ /* Blank meter */
static void BlankMeter_setValues(Meter* this, char* buffer, int size) { static void BlankMeter_updateValues(Meter* this, char* buffer, int size) {
(void) this; (void) buffer; (void) size; (void) this; (void) buffer; (void) size;
} }
@ -493,8 +540,9 @@ MeterClass BlankMeter_class = {
.delete = Meter_delete, .delete = Meter_delete,
.display = BlankMeter_display, .display = BlankMeter_display,
}, },
.setValues = BlankMeter_setValues, .updateValues = BlankMeter_updateValues,
.defaultMode = TEXT_METERMODE, .defaultMode = TEXT_METERMODE,
.maxItems = 0,
.total = 100.0, .total = 100.0,
.attributes = BlankMeter_attributes, .attributes = BlankMeter_attributes,
.name = "Blank", .name = "Blank",

39
Meter.h
View File

@ -9,17 +9,22 @@ Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#define METER_BUFFER_LEN 128 #define METER_BUFFER_LEN 256
#define GRAPH_DELAY (DEFAULT_DELAY/2)
#define GRAPH_HEIGHT 4 /* Unit: rows (lines) */
#include "ListItem.h" #include "ListItem.h"
#include "ProcessList.h"
#include <sys/time.h>
typedef struct Meter_ Meter; typedef struct Meter_ Meter;
typedef void(*Meter_Init)(Meter*); typedef void(*Meter_Init)(Meter*);
typedef void(*Meter_Done)(Meter*); typedef void(*Meter_Done)(Meter*);
typedef void(*Meter_UpdateMode)(Meter*, int); typedef void(*Meter_UpdateMode)(Meter*, int);
typedef void(*Meter_SetValues)(Meter*, char*, int); typedef void(*Meter_UpdateValues)(Meter*, char*, int);
typedef void(*Meter_Draw)(Meter*, int, int, int); typedef void(*Meter_Draw)(Meter*, int, int, int);
typedef struct MeterClass_ { typedef struct MeterClass_ {
@ -28,13 +33,14 @@ typedef struct MeterClass_ {
const Meter_Done done; const Meter_Done done;
const Meter_UpdateMode updateMode; const Meter_UpdateMode updateMode;
const Meter_Draw draw; const Meter_Draw draw;
const Meter_SetValues setValues; const Meter_UpdateValues updateValues;
const int defaultMode; const int defaultMode;
const double total; const double total;
const int* attributes; const int* attributes;
const char* name; const char* name;
const char* uiName; const char* uiName;
const char* caption; const char* caption;
const char* description;
const char maxItems; const char maxItems;
char curItems; char curItems;
} MeterClass; } MeterClass;
@ -47,7 +53,8 @@ typedef struct MeterClass_ {
#define Meter_updateMode(this_, m_) As_Meter(this_)->updateMode((Meter*)(this_), m_) #define Meter_updateMode(this_, m_) As_Meter(this_)->updateMode((Meter*)(this_), m_)
#define Meter_drawFn(this_) As_Meter(this_)->draw #define Meter_drawFn(this_) As_Meter(this_)->draw
#define Meter_doneFn(this_) As_Meter(this_)->done #define Meter_doneFn(this_) As_Meter(this_)->done
#define Meter_setValues(this_, c_, i_) As_Meter(this_)->setValues((Meter*)(this_), c_, i_) #define Meter_updateValues(this_, buf_, sz_) \
As_Meter(this_)->updateValues((Meter*)(this_), buf_, sz_)
#define Meter_defaultMode(this_) As_Meter(this_)->defaultMode #define Meter_defaultMode(this_) As_Meter(this_)->defaultMode
#define Meter_getItems(this_) As_Meter(this_)->curItems #define Meter_getItems(this_) As_Meter(this_)->curItems
#define Meter_setItems(this_, n_) As_Meter(this_)->curItems = (n_) #define Meter_setItems(this_, n_) As_Meter(this_)->curItems = (n_)
@ -64,7 +71,7 @@ struct Meter_ {
int param; int param;
void* drawData; void* drawData;
int h; int h;
ProcessList* pl; struct ProcessList_* pl;
double* values; double* values;
double total; double total;
}; };
@ -96,12 +103,15 @@ typedef struct GraphData_ {
#ifndef MAX #ifndef MAX
#define MAX(a,b) ((a)>(b)?(a):(b)) #define MAX(a,b) ((a)>(b)?(a):(b))
#endif #endif
#ifndef CLAMP
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif
extern MeterClass Meter_class; extern MeterClass Meter_class;
extern MeterClass* Meter_types[]; Meter* Meter_new(struct ProcessList_* pl, int param, MeterClass* type);
Meter* Meter_new(ProcessList* pl, int param, MeterClass* type); int Meter_humanUnit(char* buffer, unsigned long int value, int size);
void Meter_delete(Object* cast); void Meter_delete(Object* cast);
@ -109,7 +119,7 @@ void Meter_setCaption(Meter* this, const char* caption);
void Meter_setMode(Meter* this, int modeIndex); void Meter_setMode(Meter* this, int modeIndex);
ListItem* Meter_toListItem(Meter* this); ListItem* Meter_toListItem(Meter* this, bool moving);
/* ---------- TextMeterMode ---------- */ /* ---------- TextMeterMode ---------- */
@ -117,10 +127,19 @@ ListItem* Meter_toListItem(Meter* this);
/* ---------- GraphMeterMode ---------- */ /* ---------- GraphMeterMode ---------- */
#define DrawDot(a,y,c) do { attrset(a); mvaddch(y, x+k, c); } while(0) #ifdef HAVE_LIBNCURSESW
#define PIXPERROW_UTF8 4
#endif
#define PIXPERROW_ASCII 2
/* ---------- LEDMeterMode ---------- */ /* ---------- LEDMeterMode ---------- */
#ifdef HAVE_LIBNCURSESW
#endif
extern MeterMode* Meter_modes[]; extern MeterMode* Meter_modes[];
/* Blank meter */ /* Blank meter */

View File

@ -9,22 +9,43 @@ in the source distribution for its full text.
#include <stdlib.h> #include <stdlib.h>
#include <assert.h> #include <assert.h>
#include "CRT.h"
/*{ /*{
#include "Panel.h" #include "Panel.h"
#include "Settings.h" #include "Settings.h"
#include "ScreenManager.h" #include "ScreenManager.h"
typedef struct MetersPanel_ { typedef struct MetersPanel_ MetersPanel;
struct MetersPanel_ {
Panel super; Panel super;
Settings* settings; Settings* settings;
Vector* meters; Vector* meters;
ScreenManager* scr; ScreenManager* scr;
} MetersPanel; MetersPanel* leftNeighbor;
MetersPanel* rightNeighbor;
bool moving;
};
}*/ }*/
// Note: In code the meters are known to have bar/text/graph "Modes", but in UI
// we call them "Styles".
static const char* const MetersFunctions[] = {"Style ", "Move ", " ", "Delete", "Done ", NULL};
static const char* const MetersKeys[] = {"Space", "Enter", " ", "Del", "F10"};
static int MetersEvents[] = {' ', 13, ERR, KEY_DC, KEY_F(10)};
// We avoid UTF-8 arrows ← → here as they might display full-width on Chinese
// terminals, breaking our aligning.
// In <http://unicode.org/reports/tr11/>, arrows (U+2019..U+2199) are
// considered "Ambiguous characters".
static const char* const MetersMovingFunctions[] = {"Style ", "Lock ", "Up ", "Down ", "Left ", "Right ", " ", "Delete", "Done ", NULL};
static const char* const MetersMovingKeys[] = {"Space", "Enter", "Up", "Dn", "<-", "->", " ", "Del", "F10"};
static int MetersMovingEvents[] = {' ', 13, KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, ERR, KEY_DC, KEY_F(10)};
static FunctionBar* Meters_movingBar = NULL;
static void MetersPanel_delete(Object* object) { static void MetersPanel_delete(Object* object) {
Panel* super = (Panel*) object; Panel* super = (Panel*) object;
MetersPanel* this = (MetersPanel*) object; MetersPanel* this = (MetersPanel*) object;
@ -32,27 +53,83 @@ static void MetersPanel_delete(Object* object) {
free(this); free(this);
} }
void MetersPanel_setMoving(MetersPanel* this, bool moving) {
Panel* super = (Panel*) this;
this->moving = moving;
ListItem* selected = (ListItem*)Panel_getSelected(super);
if (selected) {
selected->moving = moving;
}
if (!moving) {
Panel_setSelectionColor(super, CRT_colors[PANEL_SELECTION_FOCUS]);
Panel_setDefaultBar(super);
} else {
Panel_setSelectionColor(super, CRT_colors[PANEL_SELECTION_FOLLOW]);
super->currentBar = Meters_movingBar;
}
FunctionBar_draw(this->super.currentBar, NULL);
}
static inline bool moveToNeighbor(MetersPanel* this, MetersPanel* neighbor, int selected) {
Panel* super = (Panel*) this;
if (this->moving) {
if (neighbor) {
if (selected < Vector_size(this->meters)) {
MetersPanel_setMoving(this, false);
Meter* meter = (Meter*) Vector_take(this->meters, selected);
Panel_remove(super, selected);
Vector_insert(neighbor->meters, selected, meter);
Panel_insert(&(neighbor->super), selected, (Object*) Meter_toListItem(meter, false));
Panel_setSelected(&(neighbor->super), selected);
MetersPanel_setMoving(neighbor, true);
return true;
}
}
}
return false;
}
static HandlerResult MetersPanel_eventHandler(Panel* super, int ch) { static HandlerResult MetersPanel_eventHandler(Panel* super, int ch) {
MetersPanel* this = (MetersPanel*) super; MetersPanel* this = (MetersPanel*) super;
int selected = Panel_getSelectedIndex(super); int selected = Panel_getSelectedIndex(super);
HandlerResult result = IGNORED; HandlerResult result = IGNORED;
bool sideMove = false;
switch(ch) { switch(ch) {
case 0x0a: case 0x0a:
case 0x0d: case 0x0d:
case KEY_ENTER: case KEY_ENTER:
{
if (!Vector_size(this->meters))
break;
MetersPanel_setMoving(this, !(this->moving));
result = HANDLED;
break;
}
case ' ':
case KEY_F(4): case KEY_F(4):
case 't': case 't':
{ {
if (!Vector_size(this->meters))
break;
Meter* meter = (Meter*) Vector_get(this->meters, selected); Meter* meter = (Meter*) Vector_get(this->meters, selected);
int mode = meter->mode + 1; int mode = meter->mode + 1;
if (mode == LAST_METERMODE) mode = 1; if (mode == LAST_METERMODE) mode = 1;
Meter_setMode(meter, mode); Meter_setMode(meter, mode);
Panel_set(super, selected, (Object*) Meter_toListItem(meter)); Panel_set(super, selected, (Object*) Meter_toListItem(meter, this->moving));
result = HANDLED; result = HANDLED;
break; break;
} }
case KEY_UP:
{
if (!this->moving) {
break;
}
}
/* else fallthrough */
case KEY_F(7): case KEY_F(7):
case '[': case '[':
case '-': case '-':
@ -62,6 +139,13 @@ static HandlerResult MetersPanel_eventHandler(Panel* super, int ch) {
result = HANDLED; result = HANDLED;
break; break;
} }
case KEY_DOWN:
{
if (!this->moving) {
break;
}
}
/* else fallthrough */
case KEY_F(8): case KEY_F(8):
case ']': case ']':
case '+': case '+':
@ -71,19 +155,41 @@ static HandlerResult MetersPanel_eventHandler(Panel* super, int ch) {
result = HANDLED; result = HANDLED;
break; break;
} }
case KEY_RIGHT:
{
sideMove = moveToNeighbor(this, this->rightNeighbor, selected);
if (this->moving && !sideMove) {
// lock user here until it exits positioning-mode
result = HANDLED;
}
// if user is free, don't set HANDLED;
// let ScreenManager handle focus.
break;
}
case KEY_LEFT:
{
sideMove = moveToNeighbor(this, this->leftNeighbor, selected);
if (this->moving && !sideMove) {
result = HANDLED;
}
break;
}
case KEY_F(9): case KEY_F(9):
case KEY_DC: case KEY_DC:
{ {
if (!Vector_size(this->meters))
break;
if (selected < Vector_size(this->meters)) { if (selected < Vector_size(this->meters)) {
Vector_remove(this->meters, selected); Vector_remove(this->meters, selected);
Panel_remove(super, selected); Panel_remove(super, selected);
} }
MetersPanel_setMoving(this, false);
result = HANDLED; result = HANDLED;
break; break;
} }
} }
if (result == HANDLED) { if (result == HANDLED || sideMove) {
Header* header = this->settings->header; Header* header = (Header*) this->scr->header;
this->settings->changed = true; this->settings->changed = true;
Header_calculateHeight(header); Header_calculateHeight(header);
Header_draw(header); Header_draw(header);
@ -103,15 +209,22 @@ PanelClass MetersPanel_class = {
MetersPanel* MetersPanel_new(Settings* settings, const char* header, Vector* meters, ScreenManager* scr) { MetersPanel* MetersPanel_new(Settings* settings, const char* header, Vector* meters, ScreenManager* scr) {
MetersPanel* this = AllocThis(MetersPanel); MetersPanel* this = AllocThis(MetersPanel);
Panel* super = (Panel*) this; Panel* super = (Panel*) this;
Panel_init(super, 1, 1, 1, 1, Class(ListItem), true); FunctionBar* fuBar = FunctionBar_new(MetersFunctions, MetersKeys, MetersEvents);
if (!Meters_movingBar) {
Meters_movingBar = FunctionBar_new(MetersMovingFunctions, MetersMovingKeys, MetersMovingEvents);
}
Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar);
this->settings = settings; this->settings = settings;
this->meters = meters; this->meters = meters;
this->scr = scr; this->scr = scr;
this->moving = false;
this->rightNeighbor = NULL;
this->leftNeighbor = NULL;
Panel_setHeader(super, header); Panel_setHeader(super, header);
for (int i = 0; i < Vector_size(meters); i++) { for (int i = 0; i < Vector_size(meters); i++) {
Meter* meter = (Meter*) Vector_get(meters, i); Meter* meter = (Meter*) Vector_get(meters, i);
Panel_add(super, (Object*) Meter_toListItem(meter)); Panel_add(super, (Object*) Meter_toListItem(meter, false));
} }
return this; return this;
} }

View File

@ -13,15 +13,29 @@ in the source distribution for its full text.
#include "Settings.h" #include "Settings.h"
#include "ScreenManager.h" #include "ScreenManager.h"
typedef struct MetersPanel_ { typedef struct MetersPanel_ MetersPanel;
struct MetersPanel_ {
Panel super; Panel super;
Settings* settings; Settings* settings;
Vector* meters; Vector* meters;
ScreenManager* scr; ScreenManager* scr;
} MetersPanel; MetersPanel* leftNeighbor;
MetersPanel* rightNeighbor;
bool moving;
};
// Note: In code the meters are known to have bar/text/graph "Modes", but in UI
// we call them "Styles".
// We avoid UTF-8 arrows ← → here as they might display full-width on Chinese
// terminals, breaking our aligning.
// In <http://unicode.org/reports/tr11/>, arrows (U+2019..U+2199) are
// considered "Ambiguous characters".
void MetersPanel_setMoving(MetersPanel* this, bool moving);
extern PanelClass MetersPanel_class; extern PanelClass MetersPanel_class;
MetersPanel* MetersPanel_new(Settings* settings, const char* header, Vector* meters, ScreenManager* scr); MetersPanel* MetersPanel_new(Settings* settings, const char* header, Vector* meters, ScreenManager* scr);

View File

@ -9,11 +9,12 @@ in the source distribution for its full text.
/*{ /*{
#include "RichString.h" #include "RichString.h"
#include "XAlloc.h"
typedef struct Object_ Object; typedef struct Object_ Object;
typedef void(*Object_Display)(Object*, RichString*); typedef void(*Object_Display)(Object*, RichString*);
typedef int(*Object_Compare)(const void*, const void*); typedef long(*Object_Compare)(const void*, const void*);
typedef void(*Object_Delete)(Object*); typedef void(*Object_Delete)(Object*);
#define Object_getClass(obj_) ((Object*)(obj_))->klass #define Object_getClass(obj_) ((Object*)(obj_))->klass
@ -26,7 +27,7 @@ typedef void(*Object_Delete)(Object*);
#define Class(class_) ((ObjectClass*)(&(class_ ## _class))) #define Class(class_) ((ObjectClass*)(&(class_ ## _class)))
#define AllocThis(class_) (class_*) malloc(sizeof(class_)); Object_setClass(this, Class(class_)); #define AllocThis(class_) (class_*) xMalloc(sizeof(class_)); Object_setClass(this, Class(class_));
typedef struct ObjectClass_ { typedef struct ObjectClass_ {
const void* extends; const void* extends;

View File

@ -10,11 +10,12 @@ in the source distribution for its full text.
*/ */
#include "RichString.h" #include "RichString.h"
#include "XAlloc.h"
typedef struct Object_ Object; typedef struct Object_ Object;
typedef void(*Object_Display)(Object*, RichString*); typedef void(*Object_Display)(Object*, RichString*);
typedef int(*Object_Compare)(const void*, const void*); typedef long(*Object_Compare)(const void*, const void*);
typedef void(*Object_Delete)(Object*); typedef void(*Object_Delete)(Object*);
#define Object_getClass(obj_) ((Object*)(obj_))->klass #define Object_getClass(obj_) ((Object*)(obj_))->klass
@ -27,7 +28,7 @@ typedef void(*Object_Delete)(Object*);
#define Class(class_) ((ObjectClass*)(&(class_ ## _class))) #define Class(class_) ((ObjectClass*)(&(class_ ## _class)))
#define AllocThis(class_) (class_*) malloc(sizeof(class_)); Object_setClass(this, Class(class_)); #define AllocThis(class_) (class_*) xMalloc(sizeof(class_)); Object_setClass(this, Class(class_));
typedef struct ObjectClass_ { typedef struct ObjectClass_ {
const void* extends; const void* extends;

View File

@ -9,9 +9,9 @@ in the source distribution for its full text.
#include "CRT.h" #include "CRT.h"
#include "ProcessList.h" #include "ProcessList.h"
#include "ListItem.h"
#include "IncSet.h" #include "IncSet.h"
#include "String.h" #include "StringUtils.h"
#include "FunctionBar.h"
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
@ -24,9 +24,7 @@ in the source distribution for its full text.
#include <sys/wait.h> #include <sys/wait.h>
/*{ /*{
#include "Process.h" #include "InfoScreen.h"
#include "Panel.h"
#include "FunctionBar.h"
typedef struct OpenFiles_Data_ { typedef struct OpenFiles_Data_ {
char* data[256]; char* data[256];
@ -44,69 +42,77 @@ typedef struct OpenFiles_FileData_ {
} OpenFiles_FileData; } OpenFiles_FileData;
typedef struct OpenFilesScreen_ { typedef struct OpenFilesScreen_ {
Process* process; InfoScreen super;
pid_t pid; pid_t pid;
Panel* display;
FunctionBar* bar;
} OpenFilesScreen; } OpenFilesScreen;
}*/ }*/
static const char* ofsFunctions[] = {"Search ", "Filter ", "Refresh", "Done ", NULL}; InfoScreenClass OpenFilesScreen_class = {
.super = {
static const char* ofsKeys[] = {"F3", "F4", "F5", "Esc"}; .extends = Class(Object),
.delete = OpenFilesScreen_delete
static int ofsEvents[] = {KEY_F(3), KEY_F(4), KEY_F(5), 27}; },
.scan = OpenFilesScreen_scan,
.draw = OpenFilesScreen_draw
};
OpenFilesScreen* OpenFilesScreen_new(Process* process) { OpenFilesScreen* OpenFilesScreen_new(Process* process) {
OpenFilesScreen* this = (OpenFilesScreen*) malloc(sizeof(OpenFilesScreen)); OpenFilesScreen* this = xMalloc(sizeof(OpenFilesScreen));
this->process = process; Object_setClass(this, Class(OpenFilesScreen));
this->display = Panel_new(0, 1, COLS, LINES-3, false, Class(ListItem));
if (Process_isThread(process)) if (Process_isThread(process))
this->pid = process->tgid; this->pid = process->tgid;
else else
this->pid = process->pid; this->pid = process->pid;
return this; return (OpenFilesScreen*) InfoScreen_init(&this->super, process, NULL, LINES-3, " FD TYPE DEVICE SIZE NODE NAME");
} }
void OpenFilesScreen_delete(OpenFilesScreen* this) { void OpenFilesScreen_delete(Object* this) {
Panel_delete((Object*)this->display); free(InfoScreen_done((InfoScreen*)this));
free(this);
} }
static void OpenFilesScreen_draw(OpenFilesScreen* this, IncSet* inc) { void OpenFilesScreen_draw(InfoScreen* this) {
attrset(CRT_colors[METER_TEXT]); InfoScreen_drawTitled(this, "Snapshot of files open in process %d - %s", ((OpenFilesScreen*)this)->pid, this->process->comm);
mvhline(0, 0, ' ', COLS);
mvprintw(0, 0, "Snapshot of files open in process %d - %s", this->pid, this->process->comm);
attrset(CRT_colors[DEFAULT_COLOR]);
Panel_draw(this->display, true);
IncSet_drawBar(inc);
} }
static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) { static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) {
char command[1025]; char buffer[1025];
snprintf(command, 1024, "lsof -P -p %d -F 2> /dev/null", pid); xSnprintf(buffer, 1024, "%d", pid);
FILE* fd = popen(command, "r"); OpenFiles_ProcessData* pdata = xCalloc(1, sizeof(OpenFiles_ProcessData));
OpenFiles_ProcessData* pdata = calloc(1, sizeof(OpenFiles_ProcessData));
OpenFiles_FileData* fdata = NULL; OpenFiles_FileData* fdata = NULL;
OpenFiles_Data* item = &(pdata->data); OpenFiles_Data* item = &(pdata->data);
if (!fd) { int fdpair[2];
pdata->error = 127; if (pipe(fdpair) == -1) {
pdata->error = 1;
return pdata; return pdata;
} }
while (!feof(fd)) { pid_t child = fork();
int cmd = fgetc(fd); if (child == -1) {
if (cmd == EOF) pdata->error = 1;
break; return pdata;
char* entry = malloc(1024); }
if (!fgets(entry, 1024, fd)) { if (child == 0) {
free(entry); close(fdpair[0]);
dup2(fdpair[1], STDOUT_FILENO);
close(fdpair[1]);
int fdnull = open("/dev/null", O_WRONLY);
if (fdnull < 0)
exit(1);
dup2(fdnull, STDERR_FILENO);
close(fdnull);
execlp("lsof", "lsof", "-P", "-p", buffer, "-F", NULL);
exit(127);
}
close(fdpair[1]);
FILE* fd = fdopen(fdpair[0], "r");
for (;;) {
char* line = String_readLine(fd);
if (!line) {
break; break;
} }
char* newline = strrchr(entry, '\n'); unsigned char cmd = line[0];
*newline = '\0';
if (cmd == 'f') { if (cmd == 'f') {
OpenFiles_FileData* nextFile = calloc(1, sizeof(OpenFiles_FileData)); OpenFiles_FileData* nextFile = xCalloc(1, sizeof(OpenFiles_FileData));
if (fdata == NULL) { if (fdata == NULL) {
pdata->files = nextFile; pdata->files = nextFile;
} else { } else {
@ -115,47 +121,51 @@ static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) {
fdata = nextFile; fdata = nextFile;
item = &(fdata->data); item = &(fdata->data);
} }
assert(cmd >= 0 && cmd <= 0xff); item->data[cmd] = xStrdup(line + 1);
item->data[cmd] = entry; free(line);
} }
pdata->error = pclose(fd); int wstatus;
if (waitpid(child, &wstatus, 0) == -1) {
pdata->error = 1;
return pdata;
}
if (!WIFEXITED(wstatus))
pdata->error = 1;
else
pdata->error = WEXITSTATUS(wstatus);
return pdata; return pdata;
} }
static inline void addLine(const char* line, Vector* lines, Panel* panel, const char* incFilter) {
Vector_add(lines, (Object*) ListItem_new(line, 0));
if (!incFilter || String_contains_i(line, incFilter))
Panel_add(panel, (Object*)Vector_get(lines, Vector_size(lines)-1));
}
static inline void OpenFiles_Data_clear(OpenFiles_Data* data) { static inline void OpenFiles_Data_clear(OpenFiles_Data* data) {
for (int i = 0; i < 255; i++) for (int i = 0; i < 255; i++)
if (data->data[i]) if (data->data[i])
free(data->data[i]); free(data->data[i]);
} }
static void OpenFilesScreen_scan(OpenFilesScreen* this, Vector* lines, IncSet* inc) { void OpenFilesScreen_scan(InfoScreen* this) {
Panel* panel = this->display; Panel* panel = this->display;
int idx = Panel_getSelectedIndex(panel); int idx = Panel_getSelectedIndex(panel);
Panel_prune(panel); Panel_prune(panel);
OpenFiles_ProcessData* pdata = OpenFilesScreen_getProcessData(this->pid); OpenFiles_ProcessData* pdata = OpenFilesScreen_getProcessData(((OpenFilesScreen*)this)->pid);
if (pdata->error == 127) { if (pdata->error == 127) {
addLine("Could not execute 'lsof'. Please make sure it is available in your $PATH.", lines, panel, IncSet_filter(inc)); InfoScreen_addLine(this, "Could not execute 'lsof'. Please make sure it is available in your $PATH.");
} else if (pdata->error == 1) { } else if (pdata->error == 1) {
addLine("Failed listing open files.", lines, panel, IncSet_filter(inc)); InfoScreen_addLine(this, "Failed listing open files.");
} else { } else {
OpenFiles_FileData* fdata = pdata->files; OpenFiles_FileData* fdata = pdata->files;
while (fdata) { while (fdata) {
char entry[1024];
char** data = fdata->data.data; char** data = fdata->data.data;
sprintf(entry, "%5s %4s %10s %10s %10s %s", int lenN = data['n'] ? strlen(data['n']) : 0;
int sizeEntry = 5 + 7 + 10 + 10 + 10 + lenN + 5 /*spaces*/ + 1 /*null*/;
char entry[sizeEntry];
xSnprintf(entry, sizeEntry, "%5.5s %7.7s %10.10s %10.10s %10.10s %s",
data['f'] ? data['f'] : "", data['f'] ? data['f'] : "",
data['t'] ? data['t'] : "", data['t'] ? data['t'] : "",
data['D'] ? data['D'] : "", data['D'] ? data['D'] : "",
data['s'] ? data['s'] : "", data['s'] ? data['s'] : "",
data['i'] ? data['i'] : "", data['i'] ? data['i'] : "",
data['n'] ? data['n'] : ""); data['n'] ? data['n'] : "");
addLine(entry, lines, panel, IncSet_filter(inc)); InfoScreen_addLine(this, entry);
OpenFiles_Data_clear(&fdata->data); OpenFiles_Data_clear(&fdata->data);
OpenFiles_FileData* old = fdata; OpenFiles_FileData* old = fdata;
fdata = fdata->next; fdata = fdata->next;
@ -164,83 +174,7 @@ static void OpenFilesScreen_scan(OpenFilesScreen* this, Vector* lines, IncSet* i
OpenFiles_Data_clear(&pdata->data); OpenFiles_Data_clear(&pdata->data);
} }
free(pdata); free(pdata);
Vector_insertionSort(lines); Vector_insertionSort(this->lines);
Vector_insertionSort(panel->items); Vector_insertionSort(panel->items);
Panel_setSelected(panel, idx); Panel_setSelected(panel, idx);
} }
void OpenFilesScreen_run(OpenFilesScreen* this) {
Panel* panel = this->display;
Panel_setHeader(panel, " FD TYPE DEVICE SIZE NODE NAME");
FunctionBar* bar = FunctionBar_new(ofsFunctions, ofsKeys, ofsEvents);
IncSet* inc = IncSet_new(bar);
Vector* lines = Vector_new(panel->items->type, true, DEFAULT_SIZE);
OpenFilesScreen_scan(this, lines, inc);
OpenFilesScreen_draw(this, inc);
bool looping = true;
while (looping) {
Panel_draw(panel, true);
if (inc->active)
move(LINES-1, CRT_cursorX);
int ch = getch();
if (ch == KEY_MOUSE) {
MEVENT mevent;
int ok = getmouse(&mevent);
if (ok == OK)
if (mevent.y >= panel->y && mevent.y < LINES - 1) {
Panel_setSelected(panel, mevent.y - panel->y + panel->scrollV);
ch = 0;
} if (mevent.y == LINES - 1)
ch = FunctionBar_synthesizeEvent(inc->bar, mevent.x);
}
if (inc->active) {
IncSet_handleKey(inc, ch, panel, IncSet_getListItemValue, lines);
continue;
}
switch(ch) {
case ERR:
continue;
case KEY_F(3):
case '/':
IncSet_activate(inc, INC_SEARCH);
break;
case KEY_F(4):
case '\\':
IncSet_activate(inc, INC_FILTER);
break;
case KEY_F(5):
clear();
OpenFilesScreen_scan(this, lines, inc);
OpenFilesScreen_draw(this, inc);
break;
case '\014': // Ctrl+L
clear();
OpenFilesScreen_draw(this, inc);
break;
case 'q':
case 27:
case KEY_F(10):
looping = false;
break;
case KEY_RESIZE:
Panel_resize(panel, COLS, LINES-2);
OpenFilesScreen_draw(this, inc);
break;
default:
Panel_onKey(panel, ch);
}
}
Vector_delete(lines);
FunctionBar_delete((Object*)bar);
IncSet_delete(inc);
}

View File

@ -9,9 +9,7 @@ Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "Process.h" #include "InfoScreen.h"
#include "Panel.h"
#include "FunctionBar.h"
typedef struct OpenFiles_Data_ { typedef struct OpenFiles_Data_ {
char* data[256]; char* data[256];
@ -29,17 +27,19 @@ typedef struct OpenFiles_FileData_ {
} OpenFiles_FileData; } OpenFiles_FileData;
typedef struct OpenFilesScreen_ { typedef struct OpenFilesScreen_ {
Process* process; InfoScreen super;
pid_t pid; pid_t pid;
Panel* display;
FunctionBar* bar;
} OpenFilesScreen; } OpenFilesScreen;
extern InfoScreenClass OpenFilesScreen_class;
OpenFilesScreen* OpenFilesScreen_new(Process* process); OpenFilesScreen* OpenFilesScreen_new(Process* process);
void OpenFilesScreen_delete(OpenFilesScreen* this); void OpenFilesScreen_delete(Object* this);
void OpenFilesScreen_run(OpenFilesScreen* this); void OpenFilesScreen_draw(InfoScreen* this);
void OpenFilesScreen_scan(InfoScreen* this);
#endif #endif

280
Panel.c
View File

@ -10,7 +10,7 @@ in the source distribution for its full text.
#include "CRT.h" #include "CRT.h"
#include "RichString.h" #include "RichString.h"
#include "ListItem.h" #include "ListItem.h"
#include "String.h" #include "StringUtils.h"
#include <math.h> #include <math.h>
#include <stdbool.h> #include <stdbool.h>
@ -24,16 +24,24 @@ in the source distribution for its full text.
/*{ /*{
#include "Object.h" #include "Object.h"
#include "Vector.h" #include "Vector.h"
#include "FunctionBar.h"
typedef struct Panel_ Panel; typedef struct Panel_ Panel;
typedef enum HandlerResult_ { typedef enum HandlerResult_ {
HANDLED, HANDLED = 0x01,
IGNORED, IGNORED = 0x02,
BREAK_LOOP BREAK_LOOP = 0x04,
REDRAW = 0x08,
RESCAN = 0x10,
SYNTH_KEY = 0x20,
} HandlerResult; } HandlerResult;
#define EVENT_SETSELECTED -1 #define EVENT_SET_SELECTED -1
#define EVENT_HEADER_CLICK(x_) (-10000 + x_)
#define EVENT_IS_HEADER_CLICK(ev_) (ev_ >= -10000 && ev_ <= -9000)
#define EVENT_HEADER_CLICK_GET_X(ev_) (ev_ + 10000)
typedef HandlerResult(*Panel_EventHandler)(Panel*, int); typedef HandlerResult(*Panel_EventHandler)(Panel*, int);
@ -48,19 +56,24 @@ typedef struct PanelClass_ {
struct Panel_ { struct Panel_ {
Object super; Object super;
PanelClass* class;
int x, y, w, h; int x, y, w, h;
WINDOW* window; WINDOW* window;
Vector* items; Vector* items;
int selected; int selected;
int oldSelected; int oldSelected;
char* eventHandlerBuffer; int selectedLen;
void* eventHandlerState;
int scrollV; int scrollV;
short scrollH; short scrollH;
bool needsRedraw; bool needsRedraw;
FunctionBar* currentBar;
FunctionBar* defaultBar;
RichString header; RichString header;
int selectionColor;
}; };
#define Panel_setDefaultBar(this_) do{ (this_)->currentBar = (this_)->defaultBar; }while(0)
}*/ }*/
#ifndef MIN #ifndef MIN
@ -70,24 +83,21 @@ struct Panel_ {
#define MAX(a,b) ((a)>(b)?(a):(b)) #define MAX(a,b) ((a)>(b)?(a):(b))
#endif #endif
#define KEY_CTRLN 0016 /* control-n key */ #define KEY_CTRL(l) ((l)-'A'+1)
#define KEY_CTRLP 0020 /* control-p key */
#define KEY_CTRLF 0006 /* control-f key */
#define KEY_CTRLB 0002 /* control-b key */
PanelClass Panel_class = { PanelClass Panel_class = {
.super = { .super = {
.extends = Class(Object), .extends = Class(Object),
.delete = Panel_delete .delete = Panel_delete
}, },
.eventHandler = Panel_selectByTyping .eventHandler = Panel_selectByTyping,
}; };
Panel* Panel_new(int x, int y, int w, int h, bool owner, ObjectClass* type) { Panel* Panel_new(int x, int y, int w, int h, bool owner, ObjectClass* type, FunctionBar* fuBar) {
Panel* this; Panel* this;
this = malloc(sizeof(Panel)); this = xMalloc(sizeof(Panel));
Object_setClass(this, Class(Panel)); Object_setClass(this, Class(Panel));
Panel_init(this, x, y, w, h, type, owner); Panel_init(this, x, y, w, h, type, owner, fuBar);
return this; return this;
} }
@ -97,12 +107,12 @@ void Panel_delete(Object* cast) {
free(this); free(this);
} }
void Panel_init(Panel* this, int x, int y, int w, int h, ObjectClass* type, bool owner) { void Panel_init(Panel* this, int x, int y, int w, int h, ObjectClass* type, bool owner, FunctionBar* fuBar) {
this->x = x; this->x = x;
this->y = y; this->y = y;
this->w = w; this->w = w;
this->h = h; this->h = h;
this->eventHandlerBuffer = NULL; this->eventHandlerState = NULL;
this->items = Vector_new(type, owner, DEFAULT_SIZE); this->items = Vector_new(type, owner, DEFAULT_SIZE);
this->scrollV = 0; this->scrollV = 0;
this->scrollH = 0; this->scrollH = 0;
@ -110,15 +120,23 @@ void Panel_init(Panel* this, int x, int y, int w, int h, ObjectClass* type, bool
this->oldSelected = 0; this->oldSelected = 0;
this->needsRedraw = true; this->needsRedraw = true;
RichString_beginAllocated(this->header); RichString_beginAllocated(this->header);
this->defaultBar = fuBar;
this->currentBar = fuBar;
this->selectionColor = CRT_colors[PANEL_SELECTION_FOCUS];
} }
void Panel_done(Panel* this) { void Panel_done(Panel* this) {
assert (this != NULL); assert (this != NULL);
free(this->eventHandlerBuffer); free(this->eventHandlerState);
Vector_delete(this->items); Vector_delete(this->items);
FunctionBar_delete(this->defaultBar);
RichString_end(this->header); RichString_end(this->header);
} }
void Panel_setSelectionColor(Panel* this, int color) {
this->selectionColor = color;
}
RichString* Panel_getHeader(Panel* this) { RichString* Panel_getHeader(Panel* this) {
assert (this != NULL); assert (this != NULL);
@ -234,38 +252,26 @@ int Panel_size(Panel* this) {
void Panel_setSelected(Panel* this, int selected) { void Panel_setSelected(Panel* this, int selected) {
assert (this != NULL); assert (this != NULL);
selected = MAX(0, MIN(Vector_size(this->items) - 1, selected)); int size = Vector_size(this->items);
if (selected >= size) {
selected = size - 1;
}
if (selected < 0)
selected = 0;
this->selected = selected; this->selected = selected;
if (Panel_eventHandlerFn(this)) { if (Panel_eventHandlerFn(this)) {
Panel_eventHandler(this, EVENT_SETSELECTED); Panel_eventHandler(this, EVENT_SET_SELECTED);
} }
} }
void Panel_draw(Panel* this, bool focus) { void Panel_draw(Panel* this, bool focus) {
assert (this != NULL); assert (this != NULL);
int itemCount = Vector_size(this->items); int size = Vector_size(this->items);
int scrollH = this->scrollH; int scrollH = this->scrollH;
int y = this->y; int x = this->x; int y = this->y;
int first = this->scrollV; int x = this->x;
if (itemCount > this->h && first > itemCount - this->h) { int h = this->h;
first = itemCount - this->h;
this->scrollV = first;
}
int last = MIN(itemCount, first + MIN(itemCount, this->h));
if (this->selected < first) {
first = this->selected;
this->scrollV = first;
this->needsRedraw = true;
}
if (this->selected >= last) {
last = MIN(itemCount, this->selected + 1);
first = MAX(0, last - this->h);
this->scrollV = first;
this->needsRedraw = true;
}
assert(first >= 0);
assert(last <= itemCount);
int headerLen = RichString_sizeVal(this->header); int headerLen = RichString_sizeVal(this->header);
if (headerLen > 0) { if (headerLen > 0) {
@ -282,13 +288,33 @@ void Panel_draw(Panel* this, bool focus) {
y++; y++;
} }
int highlight = focus // ensure scroll area is on screen
? CRT_colors[PANEL_HIGHLIGHT_FOCUS] if (this->scrollV < 0) {
: CRT_colors[PANEL_HIGHLIGHT_UNFOCUS]; this->scrollV = 0;
this->needsRedraw = true;
} else if (this->scrollV >= size) {
this->scrollV = MAX(size - 1, 0);
this->needsRedraw = true;
}
// ensure selection is on screen
if (this->selected < this->scrollV) {
this->scrollV = this->selected;
this->needsRedraw = true;
} else if (this->selected >= this->scrollV + h) {
this->scrollV = this->selected - h + 1;
this->needsRedraw = true;
}
int first = this->scrollV;
int upTo = MIN(first + h, size);
int selectionColor = focus
? this->selectionColor
: CRT_colors[PANEL_SELECTION_UNFOCUS];
if (this->needsRedraw) { if (this->needsRedraw) {
int line = 0;
for(int i = first, j = 0; j < this->h && i < last; i++, j++) { for(int i = first; line < h && i < upTo; i++) {
Object* itemObj = Vector_get(this->items, i); Object* itemObj = Vector_get(this->items, i);
assert(itemObj); if(!itemObj) continue; assert(itemObj); if(!itemObj) continue;
RichString_begin(item); RichString_begin(item);
@ -297,18 +323,22 @@ void Panel_draw(Panel* this, bool focus) {
int amt = MIN(itemLen - scrollH, this->w); int amt = MIN(itemLen - scrollH, this->w);
bool selected = (i == this->selected); bool selected = (i == this->selected);
if (selected) { if (selected) {
attrset(highlight); attrset(selectionColor);
RichString_setAttr(&item, highlight); RichString_setAttr(&item, selectionColor);
this->selectedLen = itemLen;
} }
mvhline(y + j, x, ' ', this->w); mvhline(y + line, x, ' ', this->w);
if (amt > 0) if (amt > 0)
RichString_printoffnVal(item, y+j, x, scrollH, amt); RichString_printoffnVal(item, y + line, x, scrollH, amt);
if (selected) if (selected)
attrset(CRT_colors[RESET_COLOR]); attrset(CRT_colors[RESET_COLOR]);
RichString_end(item); RichString_end(item);
line++;
}
while (line < h) {
mvhline(y + line, x, ' ', this->w);
line++;
} }
for (int i = y + (last - first); i < y + this->h; i++)
mvhline(i, x+0, ' ', this->w);
this->needsRedraw = false; this->needsRedraw = false;
} else { } else {
@ -321,15 +351,16 @@ void Panel_draw(Panel* this, bool focus) {
RichString_begin(new); RichString_begin(new);
Object_display(newObj, &new); Object_display(newObj, &new);
int newLen = RichString_sizeVal(new); int newLen = RichString_sizeVal(new);
mvhline(y+ this->oldSelected - this->scrollV, x+0, ' ', this->w); this->selectedLen = newLen;
mvhline(y+ this->oldSelected - first, x+0, ' ', this->w);
if (scrollH < oldLen) if (scrollH < oldLen)
RichString_printoffnVal(old, y+this->oldSelected - this->scrollV, x, RichString_printoffnVal(old, y+this->oldSelected - first, x,
scrollH, MIN(oldLen - scrollH, this->w)); scrollH, MIN(oldLen - scrollH, this->w));
attrset(highlight); attrset(selectionColor);
mvhline(y+this->selected - this->scrollV, x+0, ' ', this->w); mvhline(y+this->selected - first, x+0, ' ', this->w);
RichString_setAttr(&new, highlight); RichString_setAttr(&new, selectionColor);
if (scrollH < newLen) if (scrollH < newLen)
RichString_printoffnVal(new, y+this->selected - this->scrollV, x, RichString_printoffnVal(new, y+this->selected - first, x,
scrollH, MIN(newLen - scrollH, this->w)); scrollH, MIN(newLen - scrollH, this->w));
attrset(CRT_colors[RESET_COLOR]); attrset(CRT_colors[RESET_COLOR]);
RichString_end(new); RichString_end(new);
@ -341,108 +372,127 @@ void Panel_draw(Panel* this, bool focus) {
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);
switch (key) { switch (key) {
case KEY_DOWN: case KEY_DOWN:
case KEY_CTRLN: case KEY_CTRL('N'):
if (this->selected + 1 < Vector_size(this->items)) this->selected++;
this->selected++; break;
return true;
case KEY_UP: case KEY_UP:
case KEY_CTRLP: case KEY_CTRL('P'):
if (this->selected > 0) this->selected--;
this->selected--; break;
return true;
#ifdef KEY_C_DOWN #ifdef KEY_C_DOWN
case KEY_C_DOWN: case KEY_C_DOWN:
if (this->selected + 1 < Vector_size(this->items)) { this->selected++;
this->selected++; break;
if (this->scrollV < Vector_size(this->items) - this->h) {
this->scrollV++;
this->needsRedraw = true;
}
}
return true;
#endif #endif
#ifdef KEY_C_UP #ifdef KEY_C_UP
case KEY_C_UP: case KEY_C_UP:
if (this->selected > 0) { this->selected--;
this->selected--; break;
if (this->scrollV > 0) {
this->scrollV--;
this->needsRedraw = true;
}
}
return true;
#endif #endif
case KEY_LEFT: case KEY_LEFT:
case KEY_CTRLB: case KEY_CTRL('B'):
if (this->scrollH > 0) { if (this->scrollH > 0) {
this->scrollH -= CRT_scrollHAmount; this->scrollH -= MAX(CRT_scrollHAmount, 0);
this->needsRedraw = true; this->needsRedraw = true;
} }
return true; break;
case KEY_RIGHT: case KEY_RIGHT:
case KEY_CTRLF: case KEY_CTRL('F'):
this->scrollH += CRT_scrollHAmount; this->scrollH += CRT_scrollHAmount;
this->needsRedraw = true; this->needsRedraw = true;
return true; break;
case KEY_PPAGE: case KEY_PPAGE:
this->selected -= (this->h - 1); this->selected -= (this->h - 1);
this->scrollV -= (this->h - 1); this->scrollV = MAX(0, this->scrollV - this->h + 1);
if (this->selected < 0)
this->selected = 0;
if (this->scrollV < 0)
this->scrollV = 0;
this->needsRedraw = true; this->needsRedraw = true;
return true; break;
case KEY_NPAGE: case KEY_NPAGE:
this->selected += (this->h - 1); this->selected += (this->h - 1);
int size = Vector_size(this->items); this->scrollV = MAX(0, MIN(Vector_size(this->items) - this->h,
if (this->selected >= size) this->scrollV + this->h - 1));
this->selected = size - 1;
this->scrollV += (this->h - 1);
if (this->scrollV >= MAX(0, size - this->h))
this->scrollV = MAX(0, size - this->h - 1);
this->needsRedraw = true; this->needsRedraw = true;
return true; break;
case KEY_WHEELUP:
this->selected -= CRT_scrollWheelVAmount;
this->scrollV -= CRT_scrollWheelVAmount;
this->needsRedraw = true;
break;
case KEY_WHEELDOWN:
{
this->selected += CRT_scrollWheelVAmount;
this->scrollV += CRT_scrollWheelVAmount;
if (this->scrollV > Vector_size(this->items) - this->h) {
this->scrollV = Vector_size(this->items) - this->h;
}
this->needsRedraw = true;
break;
}
case KEY_HOME: case KEY_HOME:
this->selected = 0; this->selected = 0;
return true; break;
case KEY_END: case KEY_END:
this->selected = Vector_size(this->items) - 1; this->selected = size - 1;
return true; break;
case KEY_CTRL('A'):
case '^':
this->scrollH = 0;
this->needsRedraw = true;
break;
case KEY_CTRL('E'):
case '$':
this->scrollH = MAX(this->selectedLen - this->w, 0);
this->needsRedraw = true;
break;
default:
return false;
} }
return false;
// ensure selection within bounds
if (this->selected < 0 || size == 0) {
this->selected = 0;
this->needsRedraw = true;
} else if (this->selected >= size) {
this->selected = size - 1;
this->needsRedraw = true;
}
return true;
} }
HandlerResult Panel_selectByTyping(Panel* this, int ch) { HandlerResult Panel_selectByTyping(Panel* this, int ch) {
int size = Panel_size(this); int size = Panel_size(this);
if (!this->eventHandlerBuffer) if (!this->eventHandlerState)
this->eventHandlerBuffer = calloc(100, 1); this->eventHandlerState = xCalloc(100, sizeof(char));
char* buffer = this->eventHandlerState;
if (isalnum(ch)) { if (ch > 0 && ch < 255 && isalnum(ch)) {
int len = strlen(this->eventHandlerBuffer); int len = strlen(buffer);
if (len < 99) { if (len < 99) {
this->eventHandlerBuffer[len] = ch; buffer[len] = ch;
this->eventHandlerBuffer[len+1] = '\0'; buffer[len+1] = '\0';
} }
for (int try = 0; try < 2; try++) { for (int try = 0; try < 2; try++) {
len = strlen(this->eventHandlerBuffer); len = strlen(buffer);
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
char* cur = ((ListItem*) Panel_get(this, i))->value; char* cur = ((ListItem*) Panel_get(this, i))->value;
while (*cur == ' ') cur++; while (*cur == ' ') cur++;
if (strncasecmp(cur, this->eventHandlerBuffer, len) == 0) { if (strncasecmp(cur, buffer, len) == 0) {
Panel_setSelected(this, i); Panel_setSelected(this, i);
return HANDLED; return HANDLED;
} }
} }
this->eventHandlerBuffer[0] = ch; // if current word did not match,
this->eventHandlerBuffer[1] = '\0'; // retry considering the character the start of a new word.
buffer[0] = ch;
buffer[1] = '\0';
} }
return HANDLED; return HANDLED;
} else if (ch != ERR) { } else if (ch != ERR) {
this->eventHandlerBuffer[0] = '\0'; buffer[0] = '\0';
} }
if (ch == 13) { if (ch == 13) {
return BREAK_LOOP; return BREAK_LOOP;

36
Panel.h
View File

@ -13,16 +13,24 @@ in the source distribution for its full text.
#include "Object.h" #include "Object.h"
#include "Vector.h" #include "Vector.h"
#include "FunctionBar.h"
typedef struct Panel_ Panel; typedef struct Panel_ Panel;
typedef enum HandlerResult_ { typedef enum HandlerResult_ {
HANDLED, HANDLED = 0x01,
IGNORED, IGNORED = 0x02,
BREAK_LOOP BREAK_LOOP = 0x04,
REDRAW = 0x08,
RESCAN = 0x10,
SYNTH_KEY = 0x20,
} HandlerResult; } HandlerResult;
#define EVENT_SETSELECTED -1 #define EVENT_SET_SELECTED -1
#define EVENT_HEADER_CLICK(x_) (-10000 + x_)
#define EVENT_IS_HEADER_CLICK(ev_) (ev_ >= -10000 && ev_ <= -9000)
#define EVENT_HEADER_CLICK_GET_X(ev_) (ev_ + 10000)
typedef HandlerResult(*Panel_EventHandler)(Panel*, int); typedef HandlerResult(*Panel_EventHandler)(Panel*, int);
@ -37,19 +45,24 @@ typedef struct PanelClass_ {
struct Panel_ { struct Panel_ {
Object super; Object super;
PanelClass* class;
int x, y, w, h; int x, y, w, h;
WINDOW* window; WINDOW* window;
Vector* items; Vector* items;
int selected; int selected;
int oldSelected; int oldSelected;
char* eventHandlerBuffer; int selectedLen;
void* eventHandlerState;
int scrollV; int scrollV;
short scrollH; short scrollH;
bool needsRedraw; bool needsRedraw;
FunctionBar* currentBar;
FunctionBar* defaultBar;
RichString header; RichString header;
int selectionColor;
}; };
#define Panel_setDefaultBar(this_) do{ (this_)->currentBar = (this_)->defaultBar; }while(0)
#ifndef MIN #ifndef MIN
#define MIN(a,b) ((a)<(b)?(a):(b)) #define MIN(a,b) ((a)<(b)?(a):(b))
@ -58,21 +71,20 @@ struct Panel_ {
#define MAX(a,b) ((a)>(b)?(a):(b)) #define MAX(a,b) ((a)>(b)?(a):(b))
#endif #endif
#define KEY_CTRLN 0016 /* control-n key */ #define KEY_CTRL(l) ((l)-'A'+1)
#define KEY_CTRLP 0020 /* control-p key */
#define KEY_CTRLF 0006 /* control-f key */
#define KEY_CTRLB 0002 /* control-b key */
extern PanelClass Panel_class; extern PanelClass Panel_class;
Panel* Panel_new(int x, int y, int w, int h, bool owner, ObjectClass* type); Panel* Panel_new(int x, int y, int w, int h, bool owner, ObjectClass* type, FunctionBar* fuBar);
void Panel_delete(Object* cast); void Panel_delete(Object* cast);
void Panel_init(Panel* this, int x, int y, int w, int h, ObjectClass* type, bool owner); void Panel_init(Panel* this, int x, int y, int w, int h, ObjectClass* type, bool owner, FunctionBar* fuBar);
void Panel_done(Panel* this); void Panel_done(Panel* this);
void Panel_setSelectionColor(Panel* this, int color);
RichString* Panel_getHeader(Panel* this); RichString* Panel_getHeader(Panel* this);
extern void Panel_setHeader(Panel* this, const char* header); extern void Panel_setHeader(Panel* this, const char* header);

878
Process.c

File diff suppressed because it is too large Load Diff

237
Process.h
View File

@ -4,15 +4,22 @@
#define HEADER_Process #define HEADER_Process
/* /*
htop - Process.h htop - Process.h
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2015 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#ifdef HAVE_LIBHWLOC #ifdef MAJOR_IN_MKDEV
#elif defined(MAJOR_IN_SYSMACROS) || \
(defined(HAVE_SYS_SYSMACROS_H) && HAVE_SYS_SYSMACROS_H)
#endif #endif
// This works only with glibc 2.1+. On earlier versions #ifdef __ANDROID__
#define SYS_ioprio_get __NR_ioprio_get
#define SYS_ioprio_set __NR_ioprio_set
#endif
// On Linux, this works only with glibc 2.1+. On earlier versions
// the behavior is similar to have a hardcoded page size. // the behavior is similar to have a hardcoded page size.
#ifndef PAGE_SIZE #ifndef PAGE_SIZE
#define PAGE_SIZE ( sysconf(_SC_PAGESIZE) ) #define PAGE_SIZE ( sysconf(_SC_PAGESIZE) )
@ -20,137 +27,88 @@ in the source distribution for its full text.
#define PAGE_SIZE_KB ( PAGE_SIZE / ONE_K ) #define PAGE_SIZE_KB ( PAGE_SIZE / ONE_K )
#include "Object.h" #include "Object.h"
#include "Affinity.h"
#include "IOPriority.h"
#include <sys/types.h> #include <sys/types.h>
#define PROCESS_FLAG_IO 1 #define PROCESS_FLAG_IO 0x0001
#define PROCESS_FLAG_IOPRIO 2
#define PROCESS_FLAG_OPENVZ 4
#define PROCESS_FLAG_VSERVER 8
#define PROCESS_FLAG_CGROUP 16
#ifndef Process_isKernelThread typedef enum ProcessFields {
#define Process_isKernelThread(_process) (_process->pgrp == 0) NULL_PROCESSFIELD = 0,
#endif PID = 1,
COMM = 2,
#ifndef Process_isUserlandThread STATE = 3,
#define Process_isUserlandThread(_process) (_process->pid != _process->tgid) PPID = 4,
#endif PGRP = 5,
SESSION = 6,
#ifndef Process_isThread TTY_NR = 7,
#define Process_isThread(_process) (Process_isUserlandThread(_process) || Process_isKernelThread(_process)) TPGID = 8,
#endif MINFLT = 10,
MAJFLT = 12,
typedef enum ProcessField_ { PRIORITY = 18,
PID = 1, COMM, STATE, PPID, PGRP, SESSION, TTY_NR, TPGID, FLAGS, MINFLT, CMINFLT, MAJFLT, CMAJFLT, UTIME, NICE = 19,
STIME, CUTIME, CSTIME, PRIORITY, NICE, ITREALVALUE, STARTTIME, VSIZE, RSS, RLIM, STARTCODE, ENDCODE, STARTTIME = 21,
STARTSTACK, KSTKESP, KSTKEIP, SIGNAL, BLOCKED, SSIGIGNORE, SIGCATCH, WCHAN, NSWAP, CNSWAP, EXIT_SIGNAL, PROCESSOR = 38,
PROCESSOR, M_SIZE, M_RESIDENT, M_SHARE, M_TRS, M_DRS, M_LRS, M_DT, ST_UID, PERCENT_CPU, PERCENT_MEM, M_SIZE = 39,
USER, TIME, NLWP, TGID, M_RESIDENT = 40,
#ifdef HAVE_OPENVZ ST_UID = 46,
CTID, VPID, PERCENT_CPU = 47,
#endif PERCENT_MEM = 48,
#ifdef HAVE_VSERVER USER = 49,
VXID, TIME = 50,
#endif NLWP = 51,
#ifdef HAVE_TASKSTATS TGID = 52,
RCHAR, WCHAR, SYSCR, SYSCW, RBYTES, WBYTES, CNCLWB, IO_READ_RATE, IO_WRITE_RATE, IO_RATE,
#endif
#ifdef HAVE_CGROUP
CGROUP,
#endif
#ifdef HAVE_OOM
OOM,
#endif
IO_PRIORITY,
LAST_PROCESSFIELD
} ProcessField; } ProcessField;
struct ProcessList_; typedef struct ProcessPidColumn_ {
int id;
char* label;
} ProcessPidColumn;
typedef struct Process_ { typedef struct Process_ {
Object super; Object super;
struct ProcessList_ *pl; struct Settings_* settings;
unsigned long long int time;
pid_t pid; pid_t pid;
pid_t ppid;
pid_t tgid;
char* comm; char* comm;
int commLen;
int indent; int indent;
int basenameOffset;
bool updated;
char state; char state;
bool tag; bool tag;
bool showChildren; bool showChildren;
bool show; bool show;
pid_t ppid;
unsigned int pgrp; unsigned int pgrp;
unsigned int session; unsigned int session;
unsigned int tty_nr; unsigned int tty_nr;
pid_t tgid;
int tpgid; int tpgid;
unsigned long int flags;
uid_t st_uid; uid_t st_uid;
unsigned long int flags;
int processor;
float percent_cpu; float percent_cpu;
float percent_mem; float percent_mem;
char* user; char* user;
unsigned long long int utime;
unsigned long long int stime;
unsigned long long int cutime;
unsigned long long int cstime;
long int priority; long int priority;
long int nice; long int nice;
long int nlwp; long int nlwp;
IOPriority ioPriority;
char starttime_show[8]; char starttime_show[8];
time_t starttime_ctime; time_t starttime_ctime;
#ifdef HAVE_TASKSTATS
unsigned long long io_rchar;
unsigned long long io_wchar;
unsigned long long io_syscr;
unsigned long long io_syscw;
unsigned long long io_read_bytes;
unsigned long long io_write_bytes;
unsigned long long io_cancelled_write_bytes;
double io_rate_read_bps;
unsigned long long io_rate_read_time;
double io_rate_write_bps;
unsigned long long io_rate_write_time;
#endif
int processor;
long m_size; long m_size;
long m_resident; long m_resident;
long m_share;
long m_trs;
long m_drs;
long m_lrs;
long m_dt;
#ifdef HAVE_OPENVZ
unsigned int ctid;
unsigned int vpid;
#endif
#ifdef HAVE_VSERVER
unsigned int vxid;
#endif
#ifdef HAVE_CGROUP
char* cgroup;
#endif
#ifdef HAVE_OOM
unsigned int oom;
#endif
int exit_signal; int exit_signal;
int basenameOffset;
bool updated;
unsigned long int minflt; unsigned long int minflt;
unsigned long int cminflt;
unsigned long int majflt; unsigned long int majflt;
unsigned long int cmajflt;
#ifdef DEBUG #ifdef DEBUG
long int itrealvalue; long int itrealvalue;
unsigned long int vsize; unsigned long int vsize;
@ -172,15 +130,38 @@ typedef struct Process_ {
} Process; } Process;
typedef struct ProcessFieldData_ {
const char* name;
const char* title;
const char* description;
int flags;
} ProcessFieldData;
extern const char *Process_fieldNames[]; // Implemented in platform-specific code:
void Process_writeField(Process* this, RichString* str, ProcessField field);
long Process_compare(const void* v1, const void* v2);
void Process_delete(Object* cast);
bool Process_isThread(Process* this);
extern ProcessFieldData Process_fields[];
extern ProcessPidColumn Process_pidColumns[];
extern char Process_pidFormat[20];
extern const int Process_fieldFlags[]; typedef Process*(*Process_New)(struct Settings_*);
typedef void (*Process_WriteField)(Process*, RichString*, ProcessField);
extern const char *Process_fieldTitles[]; typedef struct ProcessClass_ {
const ObjectClass super;
const Process_WriteField writeField;
} ProcessClass;
#define As_Process(this_) ((ProcessClass*)((this_)->super.klass))
#define Process_getParentPid(process_) (process_->tgid == process_->pid ? process_->ppid : process_->tgid)
#define Process_isChildOf(process_, pid_) (process_->tgid == pid_ || (process_->tgid == process_->pid && process_->ppid == pid_))
#define Process_sortState(state) ((state) == 'I' ? 0x100 : (state))
void Process_getMaxPid();
#define ONE_K 1024L #define ONE_K 1024L
#define ONE_M (ONE_K * ONE_K) #define ONE_M (ONE_K * ONE_K)
@ -190,50 +171,38 @@ void Process_getMaxPid();
#define ONE_DECIMAL_M (ONE_DECIMAL_K * ONE_DECIMAL_K) #define ONE_DECIMAL_M (ONE_DECIMAL_K * ONE_DECIMAL_K)
#define ONE_DECIMAL_G (ONE_DECIMAL_M * ONE_DECIMAL_K) #define ONE_DECIMAL_G (ONE_DECIMAL_M * ONE_DECIMAL_K)
void Process_delete(Object* cast); extern char Process_pidFormat[20];
extern ObjectClass Process_class; void Process_setupColumnWidths();
Process* Process_new(struct ProcessList_ *pl); void Process_humanNumber(RichString* str, unsigned long number, bool coloring);
void Process_colorNumber(RichString* str, unsigned long long number, bool coloring);
void Process_printTime(RichString* str, unsigned long long totalHundredths);
void Process_outputRate(RichString* str, char* buffer, int n, double rate, int coloring);
void Process_writeField(Process* this, RichString* str, ProcessField field);
void Process_display(Object* cast, RichString* out);
void Process_done(Process* this);
extern ProcessClass Process_class;
void Process_init(Process* this, struct Settings_* settings);
void Process_toggleTag(Process* this); void Process_toggleTag(Process* this);
bool Process_setPriority(Process* this, int priority); bool Process_setPriority(Process* this, int priority);
bool Process_changePriorityBy(Process* this, size_t delta); bool Process_changePriorityBy(Process* this, int delta);
IOPriority Process_updateIOPriority(Process* this); void Process_sendSignal(Process* this, int sgn);
bool Process_setIOPriority(Process* this, IOPriority ioprio); long Process_pidCompare(const void* v1, const void* v2);
/* long Process_compare(const void* v1, const void* v2);
[1] Note that before kernel 2.6.26 a process that has not asked for
an io priority formally uses "none" as scheduling class, but the
io scheduler will treat such processes as if it were in the best
effort class. The priority within the best effort class will be
dynamically derived from the cpu nice level of the process:
extern io_priority;
*/
#define Process_effectiveIOPriority(p_) (IOPriority_class(p_->ioPriority) == IOPRIO_CLASS_NONE ? IOPriority_tuple(IOPRIO_CLASS_BE, (p_->nice + 20) / 5) : p_->ioPriority)
#ifdef HAVE_LIBHWLOC
Affinity* Process_getAffinity(Process* this);
bool Process_setAffinity(Process* this, Affinity* affinity);
#elif HAVE_NATIVE_AFFINITY
Affinity* Process_getAffinity(Process* this);
bool Process_setAffinity(Process* this, Affinity* affinity);
#endif
void Process_sendSignal(Process* this, size_t sgn);
int Process_pidCompare(const void* v1, const void* v2);
int Process_compare(const void* v1, const void* v2);
#endif #endif

File diff suppressed because it is too large Load Diff

View File

@ -14,17 +14,10 @@ in the source distribution for its full text.
#include "UsersTable.h" #include "UsersTable.h"
#include "Panel.h" #include "Panel.h"
#include "Process.h" #include "Process.h"
#include "Settings.h"
#ifndef PROCDIR #ifdef HAVE_LIBHWLOC
#define PROCDIR "/proc" #include <hwloc.h>
#endif
#ifndef PROCSTATFILE
#define PROCSTATFILE PROCDIR "/stat"
#endif
#ifndef PROCMEMINFOFILE
#define PROCMEMINFOFILE PROCDIR "/meminfo"
#endif #endif
#ifndef MAX_NAME #ifndef MAX_NAME
@ -35,51 +28,9 @@ in the source distribution for its full text.
#define MAX_READ 2048 #define MAX_READ 2048
#endif #endif
#ifndef ProcessList_cpuId
#define ProcessList_cpuId(pl, cpu) ((pl)->countCPUsFromZero ? (cpu) : (cpu)+1)
#endif
typedef enum TreeStr_ {
TREE_STR_HORZ,
TREE_STR_VERT,
TREE_STR_RTEE,
TREE_STR_BEND,
TREE_STR_TEND,
TREE_STR_OPEN,
TREE_STR_SHUT,
TREE_STR_COUNT
} TreeStr;
typedef struct CPUData_ {
unsigned long long int totalTime;
unsigned long long int userTime;
unsigned long long int systemTime;
unsigned long long int systemAllTime;
unsigned long long int idleAllTime;
unsigned long long int idleTime;
unsigned long long int niceTime;
unsigned long long int ioWaitTime;
unsigned long long int irqTime;
unsigned long long int softIrqTime;
unsigned long long int stealTime;
unsigned long long int guestTime;
unsigned long long int totalPeriod;
unsigned long long int userPeriod;
unsigned long long int systemPeriod;
unsigned long long int systemAllPeriod;
unsigned long long int idleAllPeriod;
unsigned long long int idlePeriod;
unsigned long long int nicePeriod;
unsigned long long int ioWaitPeriod;
unsigned long long int irqPeriod;
unsigned long long int softIrqPeriod;
unsigned long long int stealPeriod;
unsigned long long int guestPeriod;
} CPUData;
typedef struct ProcessList_ { typedef struct ProcessList_ {
const char **treeStr; Settings* settings;
Vector* processes; Vector* processes;
Vector* processes2; Vector* processes2;
Hashtable* processTable; Hashtable* processTable;
@ -91,17 +42,15 @@ typedef struct ProcessList_ {
const char* incFilter; const char* incFilter;
Hashtable* pidWhiteList; Hashtable* pidWhiteList;
int cpuCount;
int totalTasks;
int userlandThreads;
int kernelThreads;
int runningTasks;
#ifdef HAVE_LIBHWLOC #ifdef HAVE_LIBHWLOC
hwloc_topology_t topology; hwloc_topology_t topology;
bool topologyOk; bool topologyOk;
#endif #endif
CPUData* cpus;
int totalTasks;
int runningTasks;
int userlandThreads;
int kernelThreads;
unsigned long long int totalMem; unsigned long long int totalMem;
unsigned long long int usedMem; unsigned long long int usedMem;
@ -113,76 +62,41 @@ typedef struct ProcessList_ {
unsigned long long int usedSwap; unsigned long long int usedSwap;
unsigned long long int freeSwap; unsigned long long int freeSwap;
int flags; int cpuCount;
ProcessField* fields;
ProcessField sortKey;
int direction;
bool hideThreads;
bool shadowOtherUsers;
bool showThreadNames;
bool showingThreadNames;
bool hideKernelThreads;
bool hideUserlandThreads;
bool treeView;
bool highlightBaseName;
bool highlightMegabytes;
bool highlightThreads;
bool detailedCPUTime;
bool countCPUsFromZero;
bool updateProcessNames;
bool accountGuestInCPUMeter;
bool userOnly;
} ProcessList; } ProcessList;
ProcessList* ProcessList_new(UsersTable* ut, Hashtable* pidWhiteList, uid_t userId);
void ProcessList_delete(ProcessList* pl);
void ProcessList_goThroughEntries(ProcessList* pl);
extern const char *ProcessList_treeStrAscii[TREE_STR_COUNT];
extern const char *ProcessList_treeStrUtf8[TREE_STR_COUNT]; ProcessList* ProcessList_init(ProcessList* this, ObjectClass* klass, UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId);
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList); void ProcessList_done(ProcessList* this);
void ProcessList_delete(ProcessList* this);
void ProcessList_setPanel(ProcessList* this, Panel* panel); void ProcessList_setPanel(ProcessList* this, Panel* panel);
void ProcessList_invertSortOrder(ProcessList* this);
void ProcessList_printHeader(ProcessList* this, RichString* header); void ProcessList_printHeader(ProcessList* this, RichString* header);
void ProcessList_add(ProcessList* this, Process* p);
void ProcessList_remove(ProcessList* this, Process* p);
Process* ProcessList_get(ProcessList* this, int idx); Process* ProcessList_get(ProcessList* this, int idx);
int ProcessList_size(ProcessList* this); int ProcessList_size(ProcessList* this);
void ProcessList_sort(ProcessList* this); void ProcessList_sort(ProcessList* this);
#ifdef HAVE_TASKSTATS
#endif
#ifdef HAVE_OPENVZ
#endif
#ifdef HAVE_CGROUP
#endif
#ifdef HAVE_VSERVER
#endif
#ifdef HAVE_OOM
#endif
void ProcessList_scan(ProcessList* this);
ProcessField ProcessList_keyAt(ProcessList* this, int at); ProcessField ProcessList_keyAt(ProcessList* this, int at);
void ProcessList_expandTree(ProcessList* this); void ProcessList_expandTree(ProcessList* this);
void ProcessList_rebuildPanel(ProcessList* this, bool flags, int following, bool userOnly, uid_t userId, const char* incFilter); void ProcessList_rebuildPanel(ProcessList* this);
Process* ProcessList_getProcess(ProcessList* this, pid_t pid, bool* preExisting, Process_New constructor);
void ProcessList_scan(ProcessList* this);
#endif #endif

81
README
View File

@ -1,62 +1,61 @@
htop [![Build Status](https://travis-ci.org/hishamhm/htop.svg?branch=master)](https://travis-ci.org/hishamhm/htop)
[![PayPal donate](https://img.shields.io/badge/paypal-donate-green.svg)](http://hisham.hm/htop/index.php?page=donate)
[htop](http://hisham.hm/htop/)
==== ====
by Hisham Muhammad <hisham@gobolinux.org> by Hisham Muhammad <hisham@gobolinux.org> (2004 - 2016)
May, 2004 - January, 2014
Note
----
![Alert](http://i.imgur.com/ugIqB9s.png) **Mac users, [click here](https://www.bountysource.com/fundraisers/554-mac-os-x-support-in-the-official-htop-1-x-tree)!** The htop version you are using is a 5-year old fork -- help bring htop 1.x to the Mac!
Introduction Introduction
------------ ------------
This is htop, an interactive process viewer. This is `htop`, an interactive process viewer.
It requires ncurses. It is tested with Linux 2.6, It requires `ncurses`. It is developed primarily on Linux,
but is also reported to work (and was originally developed) but we also have code for running under FreeBSD and Mac OS X
with the 2.4 series. (help and testing are wanted for these platforms!)
Note that, while, htop is Linux specific -- it is based This software has evolved considerably over the years,
on the Linux /proc filesystem -- it is also reported to work and is reasonably complete, but there is always room for improvement.
with FreeBSD systems featuring a Linux-compatible /proc.
This is, however, unsupported. Contact the packager for your
system when reporting problems on platforms other than Linux.
This software has evolved considerably during the last years, Comparison between `htop` and classic `top`
and is reasonably complete, but there is still room for -------------------------------------------
improvement. Read the TODO file to see what's known to be missing.
Comparison between 'htop' and 'top' * In `htop` you can scroll the list vertically and horizontally
-----------------------------------
* In 'htop' you can scroll the list vertically and horizontally
to see all processes and full command lines. to see all processes and full command lines.
* In 'top' you are subject to a delay for each unassigned * In `top` you are subject to a delay for each unassigned
key you press (especially annoying when multi-key escape key you press (especially annoying when multi-key escape
sequences are triggered by accident). sequences are triggered by accident).
* 'htop' starts faster ('top' seems to collect data for a while * `htop` starts faster (`top` seems to collect data for a while
before displaying anything). before displaying anything).
* In 'htop' you don't need to type the process number to * In `htop` you don't need to type the process number to
kill a process, in 'top' you do. kill a process, in `top` you do.
* In 'htop' you don't need to type the process number or * In `htop` you don't need to type the process number or
the priority value to renice a process, in 'top' you do. the priority value to renice a process, in `top` you do.
* In 'htop' you can kill multiple processes at once. * In `htop` you can kill multiple processes at once.
* 'top' is older, hence, more tested. * `top` is older, hence, more tested.
Compilation instructions Compilation instructions
------------------------ ------------------------
This program is distributed as a standard autotools-based package. This program is distributed as a standard autotools-based package.
See the INSTALL file for detailed instructions, but you are See the [INSTALL](/INSTALL) file for detailed instructions.
probably used to the common `./configure`/`make`/`make install` routine.
When fetching the code from the development repository, you need When compiling from a [release tarball](https://hisham.hm/htop/releases/), run:
to run the `./autogen.sh` script, which in turn requires autotools
to be installed.
See the manual page (man htop) or the on-line help ('F1' or 'h' ./configure && make
inside htop) for a list of supported key commands.
if not all keys work check your curses configuration. For compiling sources downloaded from the Git repository, run:
./autogen.sh && ./configure && make
By default `make install` will install into `/usr/local`, for changing
the path use `./configure --prefix=/some/path`.
See the manual page (`man htop`) or the on-line help ('F1' or 'h'
inside `htop`) for a list of supported key commands.
If not all keys work check your curses configuration.
## License
GNU General Public License, version 2 (GPL-2.0)

View File

@ -6,11 +6,12 @@ in the source distribution for its full text.
*/ */
#include "RichString.h" #include "RichString.h"
#include "XAlloc.h"
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#define RICHSTRING_MAXLEN 300 #define RICHSTRING_MAXLEN 350
/*{ /*{
#include "config.h" #include "config.h"
@ -29,18 +30,22 @@ in the source distribution for its full text.
#include <curses.h> #include <curses.h>
#endif #endif
#ifdef HAVE_LIBNCURSESW
#include <wctype.h>
#endif
#define RichString_size(this) ((this)->chlen) #define RichString_size(this) ((this)->chlen)
#define RichString_sizeVal(this) ((this).chlen) #define RichString_sizeVal(this) ((this).chlen)
#define RichString_begin(this) RichString (this); (this).chlen = 0; (this).chptr = (this).chstr; #define RichString_begin(this) RichString (this); memset(&this, 0, sizeof(RichString)); (this).chptr = (this).chstr;
#define RichString_beginAllocated(this) (this).chlen = 0; (this).chptr = (this).chstr; #define RichString_beginAllocated(this) memset(&this, 0, sizeof(RichString)); (this).chptr = (this).chstr;
#define RichString_end(this) RichString_prune(&(this)); #define RichString_end(this) RichString_prune(&(this));
#ifdef HAVE_LIBNCURSESW #ifdef HAVE_LIBNCURSESW
#define RichString_printVal(this, y, x) mvadd_wchstr(y, x, (this).chptr) #define RichString_printVal(this, y, x) mvadd_wchstr(y, x, (this).chptr)
#define RichString_printoffnVal(this, y, x, off, n) mvadd_wchnstr(y, x, (this).chptr + off, n) #define RichString_printoffnVal(this, y, x, off, n) mvadd_wchnstr(y, x, (this).chptr + off, n)
#define RichString_getCharVal(this, i) ((this).chptr[i].chars[0] & 255) #define RichString_getCharVal(this, i) ((this).chptr[i].chars[0] & 255)
#define RichString_setChar(this, at, ch) do{ (this)->chptr[(at)].chars[0] = ch; } while(0) #define RichString_setChar(this, at, ch) do{ (this)->chptr[(at)] = (CharType) { .chars = { ch, 0 } }; } while(0)
#define CharType cchar_t #define CharType cchar_t
#else #else
#define RichString_printVal(this, y, x) mvaddchstr(y, x, (this).chptr) #define RichString_printVal(this, y, x) mvaddchstr(y, x, (this).chptr)
@ -52,56 +57,57 @@ in the source distribution for its full text.
typedef struct RichString_ { typedef struct RichString_ {
int chlen; int chlen;
CharType chstr[RICHSTRING_MAXLEN+1];
CharType* chptr; CharType* chptr;
CharType chstr[RICHSTRING_MAXLEN+1];
} RichString; } RichString;
}*/ }*/
#ifndef MIN #ifndef CLAMP
#define MIN(a,b) ((a)<(b)?(a):(b)) #define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif #endif
#define charBytes(n) (sizeof(CharType) * (n)) #define charBytes(n) (sizeof(CharType) * (n))
static inline void RichString_setLen(RichString* this, int len) { static void RichString_extendLen(RichString* this, int len) {
if (this->chlen <= RICHSTRING_MAXLEN) { if (this->chlen <= RICHSTRING_MAXLEN) {
if (len > RICHSTRING_MAXLEN) { if (len > RICHSTRING_MAXLEN) {
this->chptr = malloc(charBytes(len+1)); this->chptr = xMalloc(charBytes(len + 1));
memcpy(this->chptr, this->chstr, charBytes(this->chlen+1)); memcpy(this->chptr, this->chstr, charBytes(this->chlen));
} }
} else { } else {
if (len <= RICHSTRING_MAXLEN) { if (len <= RICHSTRING_MAXLEN) {
memcpy(this->chstr, this->chptr, charBytes(this->chlen)); memcpy(this->chstr, this->chptr, charBytes(len));
free(this->chptr); free(this->chptr);
this->chptr = this->chstr; this->chptr = this->chstr;
} else { } else {
this->chptr = realloc(this->chptr, charBytes(len+1)); this->chptr = xRealloc(this->chptr, charBytes(len + 1));
} }
} }
RichString_setChar(this, len, 0); RichString_setChar(this, len, 0);
this->chlen = len; this->chlen = len;
} }
#define RichString_setLen(this, len) do{ if(len < RICHSTRING_MAXLEN && this->chlen < RICHSTRING_MAXLEN) { RichString_setChar(this,len,0); this->chlen=len; } else RichString_extendLen(this,len); }while(0)
#ifdef HAVE_LIBNCURSESW #ifdef HAVE_LIBNCURSESW
static inline void RichString_writeFrom(RichString* this, int attrs, const char* data_c, int from, int len) { static inline void RichString_writeFrom(RichString* this, int attrs, const char* data_c, int from, int len) {
wchar_t data[len+1]; wchar_t data[len+1];
len = mbstowcs(data, data_c, len); len = mbstowcs(data, data_c, len);
if (len<0) if (len < 0)
return; return;
int newLen = from + len; int newLen = from + len;
RichString_setLen(this, newLen); RichString_setLen(this, newLen);
memset(&this->chptr[from], 0, sizeof(CharType) * (newLen - from));
for (int i = from, j = 0; i < newLen; i++, j++) { for (int i = from, j = 0; i < newLen; i++, j++) {
this->chptr[i].chars[0] = data[j]; this->chptr[i] = (CharType) { .attr = attrs & 0xffffff, .chars = { (iswprint(data[j]) ? data[j] : '?') } };
this->chptr[i].attr = attrs;
} }
this->chptr[newLen].chars[0] = 0;
} }
inline void RichString_setAttrn(RichString* this, int attrs, int start, int finish) { inline void RichString_setAttrn(RichString* this, int attrs, int start, int finish) {
cchar_t* ch = this->chptr + start; cchar_t* ch = this->chptr + start;
finish = CLAMP(finish, 0, this->chlen - 1);
for (int i = start; i <= finish; i++) { for (int i = start; i <= finish; i++) {
ch->attr = attrs; ch->attr = attrs;
ch++; ch++;
@ -125,12 +131,13 @@ static inline void RichString_writeFrom(RichString* this, int attrs, const char*
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] = (isprint(data_c[j]) ? data_c[j] : '?') | attrs; this->chptr[i] = (data_c[j] >= 32 ? data_c[j] : '?') | attrs;
this->chptr[newLen] = 0; this->chptr[newLen] = 0;
} }
void RichString_setAttrn(RichString* this, int attrs, int start, int finish) { void RichString_setAttrn(RichString* this, int attrs, int start, int finish) {
chtype* ch = this->chptr + start; chtype* ch = this->chptr + start;
finish = CLAMP(finish, 0, this->chlen - 1);
for (int i = start; i <= finish; i++) { for (int i = start; i <= finish; i++) {
*ch = (*ch & 0xff) | attrs; *ch = (*ch & 0xff) | attrs;
ch++; ch++;
@ -152,9 +159,8 @@ int RichString_findChar(RichString* this, char c, int start) {
void RichString_prune(RichString* this) { void RichString_prune(RichString* this) {
if (this->chlen > RICHSTRING_MAXLEN) if (this->chlen > RICHSTRING_MAXLEN)
free(this->chptr); free(this->chptr);
memset(this, 0, sizeof(RichString));
this->chptr = this->chstr; this->chptr = this->chstr;
this->chlen = 0;
RichString_setChar(this, 0, 0);
} }
void RichString_setAttr(RichString* this, int attrs) { void RichString_setAttr(RichString* this, int attrs) {

View File

@ -9,7 +9,7 @@ Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#define RICHSTRING_MAXLEN 300 #define RICHSTRING_MAXLEN 350
#include "config.h" #include "config.h"
#include <ctype.h> #include <ctype.h>
@ -27,18 +27,22 @@ in the source distribution for its full text.
#include <curses.h> #include <curses.h>
#endif #endif
#ifdef HAVE_LIBNCURSESW
#include <wctype.h>
#endif
#define RichString_size(this) ((this)->chlen) #define RichString_size(this) ((this)->chlen)
#define RichString_sizeVal(this) ((this).chlen) #define RichString_sizeVal(this) ((this).chlen)
#define RichString_begin(this) RichString (this); (this).chlen = 0; (this).chptr = (this).chstr; #define RichString_begin(this) RichString (this); memset(&this, 0, sizeof(RichString)); (this).chptr = (this).chstr;
#define RichString_beginAllocated(this) (this).chlen = 0; (this).chptr = (this).chstr; #define RichString_beginAllocated(this) memset(&this, 0, sizeof(RichString)); (this).chptr = (this).chstr;
#define RichString_end(this) RichString_prune(&(this)); #define RichString_end(this) RichString_prune(&(this));
#ifdef HAVE_LIBNCURSESW #ifdef HAVE_LIBNCURSESW
#define RichString_printVal(this, y, x) mvadd_wchstr(y, x, (this).chptr) #define RichString_printVal(this, y, x) mvadd_wchstr(y, x, (this).chptr)
#define RichString_printoffnVal(this, y, x, off, n) mvadd_wchnstr(y, x, (this).chptr + off, n) #define RichString_printoffnVal(this, y, x, off, n) mvadd_wchnstr(y, x, (this).chptr + off, n)
#define RichString_getCharVal(this, i) ((this).chptr[i].chars[0] & 255) #define RichString_getCharVal(this, i) ((this).chptr[i].chars[0] & 255)
#define RichString_setChar(this, at, ch) do{ (this)->chptr[(at)].chars[0] = ch; } while(0) #define RichString_setChar(this, at, ch) do{ (this)->chptr[(at)] = (CharType) { .chars = { ch, 0 } }; } while(0)
#define CharType cchar_t #define CharType cchar_t
#else #else
#define RichString_printVal(this, y, x) mvaddchstr(y, x, (this).chptr) #define RichString_printVal(this, y, x) mvaddchstr(y, x, (this).chptr)
@ -50,17 +54,19 @@ in the source distribution for its full text.
typedef struct RichString_ { typedef struct RichString_ {
int chlen; int chlen;
CharType chstr[RICHSTRING_MAXLEN+1];
CharType* chptr; CharType* chptr;
CharType chstr[RICHSTRING_MAXLEN+1];
} RichString; } RichString;
#ifndef MIN #ifndef CLAMP
#define MIN(a,b) ((a)<(b)?(a):(b)) #define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif #endif
#define charBytes(n) (sizeof(CharType) * (n)) #define charBytes(n) (sizeof(CharType) * (n))
#define RichString_setLen(this, len) do{ if(len < RICHSTRING_MAXLEN && this->chlen < RICHSTRING_MAXLEN) { RichString_setChar(this,len,0); this->chlen=len; } else RichString_extendLen(this,len); }while(0)
#ifdef HAVE_LIBNCURSESW #ifdef HAVE_LIBNCURSESW
extern void RichString_setAttrn(RichString* this, int attrs, int start, int finish); extern void RichString_setAttrn(RichString* this, int attrs, int start, int finish);

View File

@ -6,9 +6,10 @@ in the source distribution for its full text.
*/ */
#include "ScreenManager.h" #include "ScreenManager.h"
#include "ProcessList.h"
#include "Panel.h"
#include "Object.h" #include "Object.h"
#include "CRT.h"
#include <assert.h> #include <assert.h>
#include <time.h> #include <time.h>
@ -19,6 +20,8 @@ in the source distribution for its full text.
#include "FunctionBar.h" #include "FunctionBar.h"
#include "Vector.h" #include "Vector.h"
#include "Header.h" #include "Header.h"
#include "Settings.h"
#include "Panel.h"
typedef enum Orientation_ { typedef enum Orientation_ {
VERTICAL, VERTICAL,
@ -32,30 +35,27 @@ typedef struct ScreenManager_ {
int y2; int y2;
Orientation orientation; Orientation orientation;
Vector* panels; Vector* panels;
Vector* fuBars;
int panelCount; int panelCount;
const FunctionBar* fuBar;
const Header* header; const Header* header;
time_t lastScan; const Settings* settings;
bool owner; bool owner;
bool allowFocusChange; bool allowFocusChange;
} ScreenManager; } ScreenManager;
}*/ }*/
ScreenManager* ScreenManager_new(int x1, int y1, int x2, int y2, Orientation orientation, const Header* header, bool owner) { ScreenManager* ScreenManager_new(int x1, int y1, int x2, int y2, Orientation orientation, const Header* header, const Settings* settings, bool owner) {
ScreenManager* this; ScreenManager* this;
this = malloc(sizeof(ScreenManager)); this = xMalloc(sizeof(ScreenManager));
this->x1 = x1; this->x1 = x1;
this->y1 = y1; this->y1 = y1;
this->x2 = x2; this->x2 = x2;
this->y2 = y2; this->y2 = y2;
this->fuBar = NULL;
this->orientation = orientation; this->orientation = orientation;
this->panels = Vector_new(Class(Panel), owner, DEFAULT_SIZE); this->panels = Vector_new(Class(Panel), owner, DEFAULT_SIZE);
this->fuBars = Vector_new(Class(FunctionBar), true, DEFAULT_SIZE);
this->panelCount = 0; this->panelCount = 0;
this->header = header; this->header = header;
this->settings = settings;
this->owner = owner; this->owner = owner;
this->allowFocusChange = true; this->allowFocusChange = true;
return this; return this;
@ -63,7 +63,6 @@ ScreenManager* ScreenManager_new(int x1, int y1, int x2, int y2, Orientation ori
void ScreenManager_delete(ScreenManager* this) { void ScreenManager_delete(ScreenManager* this) {
Vector_delete(this->panels); Vector_delete(this->panels);
Vector_delete(this->fuBars);
free(this); free(this);
} }
@ -71,27 +70,23 @@ inline int ScreenManager_size(ScreenManager* this) {
return this->panelCount; return this->panelCount;
} }
void ScreenManager_add(ScreenManager* this, Panel* item, FunctionBar* fuBar, int size) { void ScreenManager_add(ScreenManager* this, Panel* item, int size) {
if (this->orientation == HORIZONTAL) { if (this->orientation == HORIZONTAL) {
int lastX = 0; int lastX = 0;
if (this->panelCount > 0) { if (this->panelCount > 0) {
Panel* last = (Panel*) Vector_get(this->panels, this->panelCount - 1); Panel* last = (Panel*) Vector_get(this->panels, this->panelCount - 1);
lastX = last->x + last->w + 1; lastX = last->x + last->w + 1;
} }
int height = LINES - this->y1 + this->y2;
if (size > 0) { if (size > 0) {
Panel_resize(item, size, LINES-this->y1+this->y2); Panel_resize(item, size, height);
} else { } else {
Panel_resize(item, COLS-this->x1+this->x2-lastX, LINES-this->y1+this->y2); Panel_resize(item, COLS-this->x1+this->x2-lastX, height);
} }
Panel_move(item, lastX, this->y1); Panel_move(item, lastX, this->y1);
} }
// TODO: VERTICAL // TODO: VERTICAL
Vector_add(this->panels, item); Vector_add(this->panels, item);
if (fuBar)
Vector_add(this->fuBars, fuBar);
else
Vector_add(this->fuBars, FunctionBar_new(NULL, NULL, NULL));
if (!this->fuBar && fuBar) this->fuBar = fuBar;
item->needsRedraw = true; item->needsRedraw = true;
this->panelCount++; this->panelCount++;
} }
@ -99,8 +94,6 @@ void ScreenManager_add(ScreenManager* this, Panel* item, FunctionBar* fuBar, int
Panel* ScreenManager_remove(ScreenManager* this, int idx) { Panel* ScreenManager_remove(ScreenManager* this, int idx) {
assert(this->panelCount > idx); assert(this->panelCount > idx);
Panel* panel = (Panel*) Vector_remove(this->panels, idx); Panel* panel = (Panel*) Vector_remove(this->panels, idx);
Vector_remove(this->fuBars, idx);
this->fuBar = NULL;
this->panelCount--; this->panelCount--;
return panel; return panel;
} }
@ -111,118 +104,206 @@ void ScreenManager_resize(ScreenManager* this, int x1, int y1, int x2, int y2) {
this->x2 = x2; this->x2 = x2;
this->y2 = y2; this->y2 = y2;
int panels = this->panelCount; int panels = this->panelCount;
int lastX = 0; if (this->orientation == HORIZONTAL) {
for (int i = 0; i < panels - 1; i++) { int lastX = 0;
Panel* panel = (Panel*) Vector_get(this->panels, i); for (int i = 0; i < panels - 1; i++) {
Panel_resize(panel, panel->w, LINES-y1+y2); Panel* panel = (Panel*) Vector_get(this->panels, i);
Panel_resize(panel, panel->w, LINES-y1+y2);
Panel_move(panel, lastX, y1);
lastX = panel->x + panel->w + 1;
}
Panel* panel = (Panel*) Vector_get(this->panels, panels-1);
Panel_resize(panel, COLS-x1+x2-lastX, LINES-y1+y2);
Panel_move(panel, lastX, y1); Panel_move(panel, lastX, y1);
lastX = panel->x + panel->w + 1;
} }
Panel* panel = (Panel*) Vector_get(this->panels, panels-1); // TODO: VERTICAL
Panel_resize(panel, COLS-x1+x2-lastX, LINES-y1+y2); }
Panel_move(panel, lastX, y1);
static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTimeout, bool* redraw, bool *rescan, bool *timedOut) {
ProcessList* pl = this->header->pl;
struct timeval tv;
gettimeofday(&tv, NULL);
double newTime = ((double)tv.tv_sec * 10) + ((double)tv.tv_usec / 100000);
*timedOut = (newTime - *oldTime > this->settings->delay);
*rescan = *rescan || *timedOut;
if (newTime < *oldTime) *rescan = true; // clock was adjusted?
if (*rescan) {
*oldTime = newTime;
ProcessList_scan(pl);
if (*sortTimeout == 0 || this->settings->treeView) {
ProcessList_sort(pl);
*sortTimeout = 1;
}
*redraw = true;
}
if (*redraw) {
ProcessList_rebuildPanel(pl);
Header_draw(this->header);
}
*rescan = false;
}
static void ScreenManager_drawPanels(ScreenManager* this, int focus) {
int nPanels = this->panelCount;
for (int i = 0; i < nPanels; i++) {
Panel* panel = (Panel*) Vector_get(this->panels, i);
Panel_draw(panel, i == focus);
if (i < nPanels) {
if (this->orientation == HORIZONTAL) {
mvvline(panel->y, panel->x+panel->w, ' ', panel->h+1);
}
}
}
}
static Panel* setCurrentPanel(Panel* panel) {
FunctionBar_draw(panel->currentBar, NULL);
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 = (Panel*) Vector_get(this->panels, focus); Panel* panelFocus = setCurrentPanel((Panel*) Vector_get(this->panels, focus));
if (this->fuBar)
FunctionBar_draw(this->fuBar, NULL);
this->lastScan = 0; double oldTime = 0.0;
int ch = ERR;
int closeTimeout = 0;
bool timedOut = true;
bool redraw = true;
bool rescan = false;
int sortTimeout = 0;
int resetSortTimeout = 5;
int ch = 0;
while (!quit) { while (!quit) {
int panels = this->panelCount;
if (this->header) { if (this->header) {
time_t now = time(NULL); checkRecalculation(this, &oldTime, &sortTimeout, &redraw, &rescan, &timedOut);
if (now > this->lastScan) {
ProcessList_scan(this->header->pl);
ProcessList_sort(this->header->pl);
this->lastScan = now;
}
Header_draw(this->header);
ProcessList_rebuildPanel(this->header->pl, false, false, false, false, NULL);
} }
for (int i = 0; i < panels; i++) {
Panel* panel = (Panel*) Vector_get(this->panels, i);
Panel_draw(panel, i == focus);
if (i < panels) {
if (this->orientation == HORIZONTAL) {
mvvline(panel->y, panel->x+panel->w, ' ', panel->h+1);
}
}
}
FunctionBar* bar = (FunctionBar*) Vector_get(this->fuBars, focus);
if (bar)
this->fuBar = bar;
if (this->fuBar)
FunctionBar_draw(this->fuBar, NULL);
if (redraw) {
ScreenManager_drawPanels(this, focus);
}
int prevCh = ch;
set_escdelay(25);
ch = getch(); ch = getch();
HandlerResult result = IGNORED;
if (ch == KEY_MOUSE) { if (ch == KEY_MOUSE) {
ch = ERR;
MEVENT mevent; MEVENT mevent;
int ok = getmouse(&mevent); int ok = getmouse(&mevent);
if (ok == OK) { if (ok == OK) {
if (mevent.y == LINES - 1) { if (mevent.bstate & BUTTON1_RELEASED) {
ch = FunctionBar_synthesizeEvent(this->fuBar, mevent.x); if (mevent.y == LINES - 1) {
} else { ch = FunctionBar_synthesizeEvent(panelFocus->currentBar, mevent.x);
for (int i = 0; i < this->panelCount; i++) { } else {
Panel* panel = (Panel*) Vector_get(this->panels, i); for (int i = 0; i < this->panelCount; i++) {
if (mevent.x > panel->x && mevent.x <= panel->x+panel->w && Panel* panel = (Panel*) Vector_get(this->panels, i);
mevent.y > panel->y && mevent.y <= panel->y+panel->h && if (mevent.x >= panel->x && mevent.x <= panel->x+panel->w) {
(this->allowFocusChange || panelFocus == panel) ) { if (mevent.y == panel->y) {
focus = i; ch = EVENT_HEADER_CLICK(mevent.x - panel->x);
panelFocus = panel; break;
Panel_setSelected(panel, mevent.y - panel->y + panel->scrollV - 1); } else if (mevent.y > panel->y && mevent.y <= panel->y+panel->h) {
break; ch = KEY_MOUSE;
if (panel == panelFocus || this->allowFocusChange) {
focus = i;
panelFocus = setCurrentPanel(panel);
Object* oldSelection = Panel_getSelected(panel);
Panel_setSelected(panel, mevent.y - panel->y + panel->scrollV - 1);
if (Panel_getSelected(panel) == oldSelection) {
ch = KEY_RECLICK;
}
}
break;
}
}
} }
} }
#if NCURSES_MOUSE_VERSION > 1
} else if (mevent.bstate & BUTTON4_PRESSED) {
ch = KEY_WHEELUP;
} else if (mevent.bstate & BUTTON5_PRESSED) {
ch = KEY_WHEELDOWN;
#endif
} }
} }
} }
if (ch == ERR) {
sortTimeout--;
if (prevCh == ch && !timedOut) {
closeTimeout++;
if (closeTimeout == 100) {
break;
}
} else
closeTimeout = 0;
redraw = false;
continue;
}
switch (ch) {
case KEY_ALT('H'): ch = KEY_LEFT; break;
case KEY_ALT('J'): ch = KEY_DOWN; break;
case KEY_ALT('K'): ch = KEY_UP; break;
case KEY_ALT('L'): ch = KEY_RIGHT; break;
}
redraw = true;
if (Panel_eventHandlerFn(panelFocus)) { if (Panel_eventHandlerFn(panelFocus)) {
HandlerResult result = Panel_eventHandler(panelFocus, ch); result = Panel_eventHandler(panelFocus, ch);
if (result == HANDLED) { }
continue; if (result & SYNTH_KEY) {
} else if (result == BREAK_LOOP) { ch = result >> 16;
quit = true; }
continue; if (result & REDRAW) {
} sortTimeout = 0;
}
if (result & RESCAN) {
rescan = true;
sortTimeout = 0;
}
if (result & HANDLED) {
continue;
} else if (result & BREAK_LOOP) {
quit = true;
continue;
} }
switch (ch) { switch (ch) {
case ERR:
continue;
case KEY_RESIZE: case KEY_RESIZE:
{ {
ScreenManager_resize(this, this->x1, this->y1, this->x2, this->y2); ScreenManager_resize(this, this->x1, this->y1, this->x2, this->y2);
continue; continue;
} }
case KEY_LEFT: case KEY_LEFT:
case KEY_CTRLB: case KEY_CTRL('B'):
if (this->panelCount < 2) {
goto defaultHandler;
}
if (!this->allowFocusChange) if (!this->allowFocusChange)
break; break;
tryLeft: tryLeft:
if (focus > 0) if (focus > 0)
focus--; focus--;
panelFocus = (Panel*) Vector_get(this->panels, focus); panelFocus = setCurrentPanel((Panel*) Vector_get(this->panels, focus));
if (Panel_size(panelFocus) == 0 && focus > 0) if (Panel_size(panelFocus) == 0 && focus > 0)
goto tryLeft; goto tryLeft;
break; break;
case KEY_RIGHT: case KEY_RIGHT:
case KEY_CTRLF: case KEY_CTRL('F'):
case 9: case 9:
if (this->panelCount < 2) {
goto defaultHandler;
}
if (!this->allowFocusChange) if (!this->allowFocusChange)
break; break;
tryRight: tryRight:
if (focus < this->panelCount - 1) if (focus < this->panelCount - 1)
focus++; focus++;
panelFocus = (Panel*) Vector_get(this->panels, focus); panelFocus = setCurrentPanel((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;
break; break;
@ -232,11 +313,15 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
quit = true; quit = true;
continue; continue;
default: default:
defaultHandler:
sortTimeout = resetSortTimeout;
Panel_onKey(panelFocus, ch); Panel_onKey(panelFocus, ch);
break; break;
} }
} }
*lastFocus = panelFocus; if (lastFocus)
*lastKey = ch; *lastFocus = panelFocus;
if (lastKey)
*lastKey = ch;
} }

View File

@ -12,6 +12,8 @@ in the source distribution for its full text.
#include "FunctionBar.h" #include "FunctionBar.h"
#include "Vector.h" #include "Vector.h"
#include "Header.h" #include "Header.h"
#include "Settings.h"
#include "Panel.h"
typedef enum Orientation_ { typedef enum Orientation_ {
VERTICAL, VERTICAL,
@ -25,23 +27,21 @@ typedef struct ScreenManager_ {
int y2; int y2;
Orientation orientation; Orientation orientation;
Vector* panels; Vector* panels;
Vector* fuBars;
int panelCount; int panelCount;
const FunctionBar* fuBar;
const Header* header; const Header* header;
time_t lastScan; const Settings* settings;
bool owner; bool owner;
bool allowFocusChange; bool allowFocusChange;
} ScreenManager; } ScreenManager;
ScreenManager* ScreenManager_new(int x1, int y1, int x2, int y2, Orientation orientation, const Header* header, bool owner); ScreenManager* ScreenManager_new(int x1, int y1, int x2, int y2, Orientation orientation, const Header* header, const Settings* settings, bool owner);
void ScreenManager_delete(ScreenManager* this); void ScreenManager_delete(ScreenManager* this);
extern int ScreenManager_size(ScreenManager* this); extern int ScreenManager_size(ScreenManager* this);
void ScreenManager_add(ScreenManager* this, Panel* item, FunctionBar* fuBar, int size); void ScreenManager_add(ScreenManager* this, Panel* item, int size);
Panel* ScreenManager_remove(ScreenManager* this, int idx); Panel* ScreenManager_remove(ScreenManager* this, int idx);

View File

@ -6,9 +6,11 @@ in the source distribution for its full text.
*/ */
#include "Settings.h" #include "Settings.h"
#include "Platform.h"
#include "String.h" #include "StringUtils.h"
#include "Vector.h" #include "Vector.h"
#include "CRT.h"
#include <sys/stat.h> #include <sys/stat.h>
#include <stdlib.h> #include <stdlib.h>
@ -18,215 +20,345 @@ in the source distribution for its full text.
#define DEFAULT_DELAY 15 #define DEFAULT_DELAY 15
/*{ /*{
#include "ProcessList.h" #include "Process.h"
#include "Header.h"
#include <stdbool.h> #include <stdbool.h>
typedef struct {
int len;
char** names;
int* modes;
} MeterColumnSettings;
typedef struct Settings_ { typedef struct Settings_ {
char* userSettings; char* filename;
ProcessList* pl;
Header* header; MeterColumnSettings columns[2];
ProcessField* fields;
int flags;
int colorScheme; int colorScheme;
int delay; int delay;
int cpuCount;
int direction;
ProcessField sortKey;
bool countCPUsFromZero;
bool detailedCPUTime;
bool treeView;
bool showProgramPath;
bool hideThreads;
bool shadowOtherUsers;
bool showThreadNames;
bool hideKernelThreads;
bool hideUserlandThreads;
bool highlightBaseName;
bool highlightMegabytes;
bool highlightThreads;
bool updateProcessNames;
bool accountGuestInCPUMeter;
bool headerMargin;
bool changed; bool changed;
} Settings; } Settings;
#ifndef Settings_cpuId
#define Settings_cpuId(settings, cpu) ((settings)->countCPUsFromZero ? (cpu) : (cpu)+1)
#endif
}*/ }*/
void Settings_delete(Settings* this) { void Settings_delete(Settings* this) {
free(this->userSettings); free(this->filename);
free(this->fields);
for (unsigned int i = 0; i < (sizeof(this->columns)/sizeof(MeterColumnSettings)); i++) {
String_freeArray(this->columns[i].names);
free(this->columns[i].modes);
}
free(this); free(this);
} }
static void Settings_readMeters(Settings* this, char* line, HeaderSide side) { static void Settings_readMeters(Settings* this, char* line, int column) {
char* trim = String_trim(line); char* trim = String_trim(line);
int nIds; int nIds;
char** ids = String_split(trim, ' ', &nIds); char** ids = String_split(trim, ' ', &nIds);
free(trim); free(trim);
for (int i = 0; ids[i]; i++) { this->columns[column].names = ids;
Header_createMeter(this->header, ids[i], side);
}
String_freeArray(ids);
} }
static void Settings_readMeterModes(Settings* this, char* line, HeaderSide side) { static void Settings_readMeterModes(Settings* this, char* line, int column) {
char* trim = String_trim(line); char* trim = String_trim(line);
int nIds; int nIds;
char** ids = String_split(trim, ' ', &nIds); char** ids = String_split(trim, ' ', &nIds);
free(trim); free(trim);
int len = 0;
for (int i = 0; ids[i]; i++) { for (int i = 0; ids[i]; i++) {
int mode = atoi(ids[i]); len++;
Header_setMode(this->header, i, mode, side);
} }
this->columns[column].len = len;
int* modes = xCalloc(len, sizeof(int));
for (int i = 0; i < len; i++) {
modes[i] = atoi(ids[i]);
}
String_freeArray(ids);
this->columns[column].modes = modes;
}
static void Settings_defaultMeters(Settings* this) {
int sizes[] = { 3, 3 };
if (this->cpuCount > 4) {
sizes[1]++;
}
for (int i = 0; i < 2; i++) {
this->columns[i].names = xCalloc(sizes[i] + 1, sizeof(char*));
this->columns[i].modes = xCalloc(sizes[i], sizeof(int));
this->columns[i].len = sizes[i];
}
int r = 0;
if (this->cpuCount > 8) {
this->columns[0].names[0] = xStrdup("LeftCPUs2");
this->columns[0].modes[0] = BAR_METERMODE;
this->columns[1].names[r] = xStrdup("RightCPUs2");
this->columns[1].modes[r++] = BAR_METERMODE;
} else if (this->cpuCount > 4) {
this->columns[0].names[0] = xStrdup("LeftCPUs");
this->columns[0].modes[0] = BAR_METERMODE;
this->columns[1].names[r] = xStrdup("RightCPUs");
this->columns[1].modes[r++] = BAR_METERMODE;
} else {
this->columns[0].names[0] = xStrdup("AllCPUs");
this->columns[0].modes[0] = BAR_METERMODE;
}
this->columns[0].names[1] = xStrdup("Memory");
this->columns[0].modes[1] = BAR_METERMODE;
this->columns[0].names[2] = xStrdup("Swap");
this->columns[0].modes[2] = BAR_METERMODE;
this->columns[1].names[r] = xStrdup("Tasks");
this->columns[1].modes[r++] = TEXT_METERMODE;
this->columns[1].names[r] = xStrdup("LoadAverage");
this->columns[1].modes[r++] = TEXT_METERMODE;
this->columns[1].names[r] = xStrdup("Uptime");
this->columns[1].modes[r++] = TEXT_METERMODE;
}
static void readFields(ProcessField* fields, int* flags, const char* line) {
char* trim = String_trim(line);
int nIds;
char** ids = String_split(trim, ' ', &nIds);
free(trim);
int i, j;
*flags = 0;
for (j = 0, i = 0; i < Platform_numberOfFields && ids[i]; i++) {
// This "+1" is for compatibility with the older enum format.
int id = atoi(ids[i]) + 1;
if (id > 0 && Process_fields[id].name && id < Platform_numberOfFields) {
fields[j] = id;
*flags |= Process_fields[id].flags;
j++;
}
}
fields[j] = NULL_PROCESSFIELD;
String_freeArray(ids); String_freeArray(ids);
} }
static bool Settings_read(Settings* this, const char* fileName, int cpuCount) { static bool Settings_read(Settings* this, const char* fileName) {
FILE* fd = fopen(fileName, "r"); FILE* fd;
CRT_dropPrivileges();
fd = fopen(fileName, "r");
CRT_restorePrivileges();
if (!fd) if (!fd)
return false; return false;
const int maxLine = 2048; bool didReadMeters = false;
char buffer[maxLine]; bool didReadFields = false;
bool readMeters = false; for (;;) {
while (fgets(buffer, maxLine, fd)) { char* line = String_readLine(fd);
if (!line) {
break;
}
int nOptions; int nOptions;
char** option = String_split(buffer, '=', &nOptions); char** option = String_split(line, '=', &nOptions);
free (line);
if (nOptions < 2) { if (nOptions < 2) {
String_freeArray(option); String_freeArray(option);
continue; continue;
} }
if (String_eq(option[0], "fields")) { if (String_eq(option[0], "fields")) {
char* trim = String_trim(option[1]); readFields(this->fields, &(this->flags), option[1]);
int nIds; didReadFields = true;
char** ids = String_split(trim, ' ', &nIds);
free(trim);
int i, j;
this->pl->flags = 0;
for (j = 0, i = 0; i < LAST_PROCESSFIELD && ids[i]; i++) {
// This "+1" is for compatibility with the older enum format.
int id = atoi(ids[i]) + 1;
if (id > 0 && id < LAST_PROCESSFIELD) {
this->pl->fields[j] = id;
this->pl->flags |= Process_fieldFlags[id];
j++;
}
}
this->pl->fields[j] = (ProcessField) NULL;
String_freeArray(ids);
} 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->pl->sortKey = atoi(option[1]) + 1; this->sortKey = atoi(option[1]) + 1;
} else if (String_eq(option[0], "sort_direction")) { } else if (String_eq(option[0], "sort_direction")) {
this->pl->direction = atoi(option[1]); this->direction = atoi(option[1]);
} else if (String_eq(option[0], "tree_view")) { } else if (String_eq(option[0], "tree_view")) {
this->pl->treeView = atoi(option[1]); this->treeView = atoi(option[1]);
} else if (String_eq(option[0], "hide_threads")) { } else if (String_eq(option[0], "hide_threads")) {
this->pl->hideThreads = atoi(option[1]); this->hideThreads = atoi(option[1]);
} else if (String_eq(option[0], "hide_kernel_threads")) { } else if (String_eq(option[0], "hide_kernel_threads")) {
this->pl->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")) {
this->pl->hideUserlandThreads = atoi(option[1]); this->hideUserlandThreads = atoi(option[1]);
} else if (String_eq(option[0], "shadow_other_users")) { } else if (String_eq(option[0], "shadow_other_users")) {
this->pl->shadowOtherUsers = atoi(option[1]); this->shadowOtherUsers = atoi(option[1]);
} else if (String_eq(option[0], "show_thread_names")) { } else if (String_eq(option[0], "show_thread_names")) {
this->pl->showThreadNames = atoi(option[1]); this->showThreadNames = atoi(option[1]);
} else if (String_eq(option[0], "show_program_path")) {
this->showProgramPath = atoi(option[1]);
} else if (String_eq(option[0], "highlight_base_name")) { } else if (String_eq(option[0], "highlight_base_name")) {
this->pl->highlightBaseName = atoi(option[1]); this->highlightBaseName = atoi(option[1]);
} else if (String_eq(option[0], "highlight_megabytes")) { } else if (String_eq(option[0], "highlight_megabytes")) {
this->pl->highlightMegabytes = atoi(option[1]); this->highlightMegabytes = atoi(option[1]);
} else if (String_eq(option[0], "highlight_threads")) { } else if (String_eq(option[0], "highlight_threads")) {
this->pl->highlightThreads = atoi(option[1]); this->highlightThreads = atoi(option[1]);
} else if (String_eq(option[0], "header_margin")) { } else if (String_eq(option[0], "header_margin")) {
this->header->margin = atoi(option[1]); this->headerMargin = atoi(option[1]);
} else if (String_eq(option[0], "expand_system_time")) { } else if (String_eq(option[0], "expand_system_time")) {
// Compatibility option. // Compatibility option.
this->pl->detailedCPUTime = atoi(option[1]); this->detailedCPUTime = atoi(option[1]);
} else if (String_eq(option[0], "detailed_cpu_time")) { } else if (String_eq(option[0], "detailed_cpu_time")) {
this->pl->detailedCPUTime = atoi(option[1]); this->detailedCPUTime = atoi(option[1]);
} else if (String_eq(option[0], "cpu_count_from_zero")) { } else if (String_eq(option[0], "cpu_count_from_zero")) {
this->pl->countCPUsFromZero = atoi(option[1]); this->countCPUsFromZero = atoi(option[1]);
} else if (String_eq(option[0], "update_process_names")) { } else if (String_eq(option[0], "update_process_names")) {
this->pl->updateProcessNames = atoi(option[1]); this->updateProcessNames = atoi(option[1]);
} else if (String_eq(option[0], "account_guest_in_cpu_meter")) { } else if (String_eq(option[0], "account_guest_in_cpu_meter")) {
this->pl->accountGuestInCPUMeter = atoi(option[1]); this->accountGuestInCPUMeter = atoi(option[1]);
} else if (String_eq(option[0], "delay")) { } else if (String_eq(option[0], "delay")) {
this->delay = atoi(option[1]); this->delay = atoi(option[1]);
} else if (String_eq(option[0], "color_scheme")) { } else if (String_eq(option[0], "color_scheme")) {
this->colorScheme = atoi(option[1]); this->colorScheme = atoi(option[1]);
if (this->colorScheme < 0) this->colorScheme = 0; if (this->colorScheme < 0 || this->colorScheme >= LAST_COLORSCHEME) this->colorScheme = 0;
if (this->colorScheme > 5) this->colorScheme = 5;
} else if (String_eq(option[0], "left_meters")) { } else if (String_eq(option[0], "left_meters")) {
Settings_readMeters(this, option[1], LEFT_HEADER); Settings_readMeters(this, option[1], 0);
readMeters = true; didReadMeters = true;
} else if (String_eq(option[0], "right_meters")) { } else if (String_eq(option[0], "right_meters")) {
Settings_readMeters(this, option[1], RIGHT_HEADER); Settings_readMeters(this, option[1], 1);
readMeters = true; didReadMeters = true;
} else if (String_eq(option[0], "left_meter_modes")) { } else if (String_eq(option[0], "left_meter_modes")) {
Settings_readMeterModes(this, option[1], LEFT_HEADER); Settings_readMeterModes(this, option[1], 0);
readMeters = true; didReadMeters = true;
} else if (String_eq(option[0], "right_meter_modes")) { } else if (String_eq(option[0], "right_meter_modes")) {
Settings_readMeterModes(this, option[1], RIGHT_HEADER); Settings_readMeterModes(this, option[1], 1);
readMeters = true; didReadMeters = true;
} }
String_freeArray(option); String_freeArray(option);
} }
fclose(fd); fclose(fd);
if (!readMeters) { if (!didReadMeters) {
Header_defaultMeters(this->header, cpuCount); Settings_defaultMeters(this);
} }
return true; return didReadFields;
}
static void writeFields(FILE* fd, ProcessField* fields, const char* name) {
fprintf(fd, "%s=", name);
const char* sep = "";
for (int i = 0; fields[i]; i++) {
// This "-1" is for compatibility with the older enum format.
fprintf(fd, "%s%d", sep, (int) fields[i]-1);
sep = " ";
}
fprintf(fd, "\n");
}
static void writeMeters(Settings* this, FILE* fd, int column) {
const char* sep = "";
for (int i = 0; i < this->columns[column].len; i++) {
fprintf(fd, "%s%s", sep, this->columns[column].names[i]);
sep = " ";
}
fprintf(fd, "\n");
}
static void writeMeterModes(Settings* this, FILE* fd, int column) {
const char* sep = "";
for (int i = 0; i < this->columns[column].len; i++) {
fprintf(fd, "%s%d", sep, this->columns[column].modes[i]);
sep = " ";
}
fprintf(fd, "\n");
} }
bool Settings_write(Settings* this) { bool Settings_write(Settings* this) {
// TODO: implement File object and make
// file I/O object-oriented.
FILE* fd; FILE* fd;
fd = fopen(this->userSettings, "w");
CRT_dropPrivileges();
fd = fopen(this->filename, "w");
CRT_restorePrivileges();
if (fd == NULL) { if (fd == NULL) {
return false; return false;
} }
fprintf(fd, "# Beware! This file is rewritten by htop when settings are changed in the interface.\n"); fprintf(fd, "# Beware! This file is rewritten by htop when settings are changed in the interface.\n");
fprintf(fd, "# The parser is also very primitive, and not human-friendly.\n"); fprintf(fd, "# The parser is also very primitive, and not human-friendly.\n");
fprintf(fd, "fields="); writeFields(fd, this->fields, "fields");
for (int i = 0; this->pl->fields[i]; i++) {
// This "-1" is for compatibility with the older enum format.
fprintf(fd, "%d ", (int) this->pl->fields[i]-1);
}
fprintf(fd, "\n");
// 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->pl->sortKey-1); fprintf(fd, "sort_key=%d\n", (int) this->sortKey-1);
fprintf(fd, "sort_direction=%d\n", (int) this->pl->direction); fprintf(fd, "sort_direction=%d\n", (int) this->direction);
fprintf(fd, "hide_threads=%d\n", (int) this->pl->hideThreads); fprintf(fd, "hide_threads=%d\n", (int) this->hideThreads);
fprintf(fd, "hide_kernel_threads=%d\n", (int) this->pl->hideKernelThreads); fprintf(fd, "hide_kernel_threads=%d\n", (int) this->hideKernelThreads);
fprintf(fd, "hide_userland_threads=%d\n", (int) this->pl->hideUserlandThreads); fprintf(fd, "hide_userland_threads=%d\n", (int) this->hideUserlandThreads);
fprintf(fd, "shadow_other_users=%d\n", (int) this->pl->shadowOtherUsers); fprintf(fd, "shadow_other_users=%d\n", (int) this->shadowOtherUsers);
fprintf(fd, "show_thread_names=%d\n", (int) this->pl->showThreadNames); fprintf(fd, "show_thread_names=%d\n", (int) this->showThreadNames);
fprintf(fd, "highlight_base_name=%d\n", (int) this->pl->highlightBaseName); fprintf(fd, "show_program_path=%d\n", (int) this->showProgramPath);
fprintf(fd, "highlight_megabytes=%d\n", (int) this->pl->highlightMegabytes); fprintf(fd, "highlight_base_name=%d\n", (int) this->highlightBaseName);
fprintf(fd, "highlight_threads=%d\n", (int) this->pl->highlightThreads); fprintf(fd, "highlight_megabytes=%d\n", (int) this->highlightMegabytes);
fprintf(fd, "tree_view=%d\n", (int) this->pl->treeView); fprintf(fd, "highlight_threads=%d\n", (int) this->highlightThreads);
fprintf(fd, "header_margin=%d\n", (int) this->header->margin); fprintf(fd, "tree_view=%d\n", (int) this->treeView);
fprintf(fd, "detailed_cpu_time=%d\n", (int) this->pl->detailedCPUTime); fprintf(fd, "header_margin=%d\n", (int) this->headerMargin);
fprintf(fd, "cpu_count_from_zero=%d\n", (int) this->pl->countCPUsFromZero); fprintf(fd, "detailed_cpu_time=%d\n", (int) this->detailedCPUTime);
fprintf(fd, "update_process_names=%d\n", (int) this->pl->updateProcessNames); fprintf(fd, "cpu_count_from_zero=%d\n", (int) this->countCPUsFromZero);
fprintf(fd, "account_guest_in_cpu_meter=%d\n", (int) this->pl->accountGuestInCPUMeter); fprintf(fd, "update_process_names=%d\n", (int) this->updateProcessNames);
fprintf(fd, "account_guest_in_cpu_meter=%d\n", (int) this->accountGuestInCPUMeter);
fprintf(fd, "color_scheme=%d\n", (int) this->colorScheme); fprintf(fd, "color_scheme=%d\n", (int) this->colorScheme);
fprintf(fd, "delay=%d\n", (int) this->delay); fprintf(fd, "delay=%d\n", (int) this->delay);
fprintf(fd, "left_meters="); fprintf(fd, "left_meters="); writeMeters(this, fd, 0);
for (int i = 0; i < Header_size(this->header, LEFT_HEADER); i++) { fprintf(fd, "left_meter_modes="); writeMeterModes(this, fd, 0);
char* name = Header_readMeterName(this->header, i, LEFT_HEADER); fprintf(fd, "right_meters="); writeMeters(this, fd, 1);
fprintf(fd, "%s ", name); fprintf(fd, "right_meter_modes="); writeMeterModes(this, fd, 1);
free(name);
}
fprintf(fd, "\n");
fprintf(fd, "left_meter_modes=");
for (int i = 0; i < Header_size(this->header, LEFT_HEADER); i++)
fprintf(fd, "%d ", Header_readMeterMode(this->header, i, LEFT_HEADER));
fprintf(fd, "\n");
fprintf(fd, "right_meters=");
for (int i = 0; i < Header_size(this->header, RIGHT_HEADER); i++) {
char* name = Header_readMeterName(this->header, i, RIGHT_HEADER);
fprintf(fd, "%s ", name);
free(name);
}
fprintf(fd, "\n");
fprintf(fd, "right_meter_modes=");
for (int i = 0; i < Header_size(this->header, RIGHT_HEADER); i++)
fprintf(fd, "%d ", Header_readMeterMode(this->header, i, RIGHT_HEADER));
fprintf(fd, "\n");
fclose(fd); fclose(fd);
return true; return true;
} }
Settings* Settings_new(ProcessList* pl, Header* header, int cpuCount) { Settings* Settings_new(int cpuCount) {
Settings* this = malloc(sizeof(Settings));
this->pl = pl; Settings* this = xCalloc(1, sizeof(Settings));
this->header = header;
this->sortKey = PERCENT_CPU;
this->direction = 1;
this->hideThreads = false;
this->shadowOtherUsers = false;
this->showThreadNames = false;
this->hideKernelThreads = false;
this->hideUserlandThreads = false;
this->treeView = false;
this->highlightBaseName = false;
this->highlightMegabytes = false;
this->detailedCPUTime = false;
this->countCPUsFromZero = false;
this->updateProcessNames = false;
this->cpuCount = cpuCount;
this->showProgramPath = true;
this->highlightThreads = true;
this->fields = xCalloc(Platform_numberOfFields+1, sizeof(ProcessField));
// TODO: turn 'fields' into a Vector,
// (and ProcessFields into proper objects).
this->flags = 0;
ProcessField* defaults = Platform_defaultFields;
for (int i = 0; defaults[i]; i++) {
this->fields[i] = defaults[i];
this->flags |= Process_fields[defaults[i]].flags;
}
char* legacyDotfile = NULL; char* legacyDotfile = NULL;
char* rcfile = getenv("HTOPRC"); char* rcfile = getenv("HTOPRC");
if (rcfile) { if (rcfile) {
this->userSettings = strdup(rcfile); this->filename = xStrdup(rcfile);
} else { } else {
const char* home = getenv("HOME"); const char* home = getenv("HOME");
if (!home) home = ""; if (!home) home = "";
@ -234,51 +366,65 @@ Settings* Settings_new(ProcessList* pl, Header* header, int cpuCount) {
char* configDir = NULL; char* configDir = NULL;
char* htopDir = NULL; char* htopDir = NULL;
if (xdgConfigHome) { if (xdgConfigHome) {
this->userSettings = String_cat(xdgConfigHome, "/htop/htoprc"); this->filename = String_cat(xdgConfigHome, "/htop/htoprc");
configDir = strdup(xdgConfigHome); configDir = xStrdup(xdgConfigHome);
htopDir = String_cat(xdgConfigHome, "/htop"); htopDir = String_cat(xdgConfigHome, "/htop");
} else { } else {
this->userSettings = String_cat(home, "/.config/htop/htoprc"); this->filename = String_cat(home, "/.config/htop/htoprc");
configDir = String_cat(home, "/.config"); configDir = String_cat(home, "/.config");
htopDir = String_cat(home, "/.config/htop"); htopDir = String_cat(home, "/.config/htop");
} }
legacyDotfile = String_cat(home, "/.htoprc"); legacyDotfile = String_cat(home, "/.htoprc");
CRT_dropPrivileges();
(void) mkdir(configDir, 0700); (void) mkdir(configDir, 0700);
(void) mkdir(htopDir, 0700); (void) mkdir(htopDir, 0700);
free(htopDir); free(htopDir);
free(configDir); free(configDir);
struct stat st; struct stat st;
if (lstat(legacyDotfile, &st) != 0) { int err = lstat(legacyDotfile, &st);
st.st_mode = 0; if (err || S_ISLNK(st.st_mode)) {
}
if (access(legacyDotfile, R_OK) != 0 || S_ISLNK(st.st_mode)) {
free(legacyDotfile); free(legacyDotfile);
legacyDotfile = NULL; legacyDotfile = NULL;
} }
CRT_restorePrivileges();
} }
this->colorScheme = 0; this->colorScheme = 0;
this->changed = false; this->changed = false;
this->delay = DEFAULT_DELAY; this->delay = DEFAULT_DELAY;
bool ok = Settings_read(this, legacyDotfile ? legacyDotfile : this->userSettings, cpuCount); bool ok = false;
if (ok) { if (legacyDotfile) {
if (legacyDotfile) { ok = Settings_read(this, legacyDotfile);
if (ok) {
// Transition to new location and delete old configuration file // Transition to new location and delete old configuration file
if (Settings_write(this)) if (Settings_write(this))
unlink(legacyDotfile); unlink(legacyDotfile);
} }
} else { free(legacyDotfile);
}
if (!ok) {
ok = Settings_read(this, this->filename);
}
if (!ok) {
this->changed = true; this->changed = true;
// TODO: how to get SYSCONFDIR correctly through Autoconf? // TODO: how to get SYSCONFDIR correctly through Autoconf?
char* systemSettings = String_cat(SYSCONFDIR, "/htoprc"); char* systemSettings = String_cat(SYSCONFDIR, "/htoprc");
ok = Settings_read(this, systemSettings, cpuCount); ok = Settings_read(this, systemSettings);
free(systemSettings); free(systemSettings);
if (!ok) {
Header_defaultMeters(this->header, cpuCount);
pl->hideKernelThreads = true;
pl->highlightMegabytes = true;
pl->highlightThreads = false;
}
} }
free(legacyDotfile); if (!ok) {
Settings_defaultMeters(this);
this->hideKernelThreads = true;
this->highlightMegabytes = true;
this->highlightThreads = true;
this->headerMargin = true;
}
return this; return this;
} }
void Settings_invertSortOrder(Settings* this) {
if (this->direction == 1)
this->direction = -1;
else
this->direction = 1;
}

View File

@ -11,24 +11,59 @@ in the source distribution for its full text.
#define DEFAULT_DELAY 15 #define DEFAULT_DELAY 15
#include "ProcessList.h" #include "Process.h"
#include "Header.h"
#include <stdbool.h> #include <stdbool.h>
typedef struct {
int len;
char** names;
int* modes;
} MeterColumnSettings;
typedef struct Settings_ { typedef struct Settings_ {
char* userSettings; char* filename;
ProcessList* pl;
Header* header; MeterColumnSettings columns[2];
ProcessField* fields;
int flags;
int colorScheme; int colorScheme;
int delay; int delay;
int cpuCount;
int direction;
ProcessField sortKey;
bool countCPUsFromZero;
bool detailedCPUTime;
bool treeView;
bool showProgramPath;
bool hideThreads;
bool shadowOtherUsers;
bool showThreadNames;
bool hideKernelThreads;
bool hideUserlandThreads;
bool highlightBaseName;
bool highlightMegabytes;
bool highlightThreads;
bool updateProcessNames;
bool accountGuestInCPUMeter;
bool headerMargin;
bool changed; bool changed;
} Settings; } Settings;
#ifndef Settings_cpuId
#define Settings_cpuId(settings, cpu) ((settings)->countCPUsFromZero ? (cpu) : (cpu)+1)
#endif
void Settings_delete(Settings* this); void Settings_delete(Settings* this);
bool Settings_write(Settings* this); bool Settings_write(Settings* this);
Settings* Settings_new(ProcessList* pl, Header* header, int cpuCount); Settings* Settings_new(int cpuCount);
void Settings_invertSortOrder(Settings* this);
#endif #endif

View File

@ -7,12 +7,14 @@ in the source distribution for its full text.
#include "Panel.h" #include "Panel.h"
#include "SignalsPanel.h" #include "SignalsPanel.h"
#include "Platform.h"
#include "ListItem.h" #include "ListItem.h"
#include "RichString.h" #include "RichString.h"
#include <stdlib.h> #include <stdlib.h>
#include <assert.h> #include <assert.h>
#include <signal.h>
#include <ctype.h> #include <ctype.h>
@ -25,48 +27,32 @@ typedef struct SignalItem_ {
}*/ }*/
static SignalItem signals[] = {
{ .name = " 0 Cancel", .number = 0 },
{ .name = " 1 SIGHUP", .number = 1 },
{ .name = " 2 SIGINT", .number = 2 },
{ .name = " 3 SIGQUIT", .number = 3 },
{ .name = " 4 SIGILL", .number = 4 },
{ .name = " 5 SIGTRAP", .number = 5 },
{ .name = " 6 SIGABRT", .number = 6 },
{ .name = " 6 SIGIOT", .number = 6 },
{ .name = " 7 SIGBUS", .number = 7 },
{ .name = " 8 SIGFPE", .number = 8 },
{ .name = " 9 SIGKILL", .number = 9 },
{ .name = "10 SIGUSR1", .number = 10 },
{ .name = "11 SIGSEGV", .number = 11 },
{ .name = "12 SIGUSR2", .number = 12 },
{ .name = "13 SIGPIPE", .number = 13 },
{ .name = "14 SIGALRM", .number = 14 },
{ .name = "15 SIGTERM", .number = 15 },
{ .name = "16 SIGSTKFLT", .number = 16 },
{ .name = "17 SIGCHLD", .number = 17 },
{ .name = "18 SIGCONT", .number = 18 },
{ .name = "19 SIGSTOP", .number = 19 },
{ .name = "20 SIGTSTP", .number = 20 },
{ .name = "21 SIGTTIN", .number = 21 },
{ .name = "22 SIGTTOU", .number = 22 },
{ .name = "23 SIGURG", .number = 23 },
{ .name = "24 SIGXCPU", .number = 24 },
{ .name = "25 SIGXFSZ", .number = 25 },
{ .name = "26 SIGVTALRM", .number = 26 },
{ .name = "27 SIGPROF", .number = 27 },
{ .name = "28 SIGWINCH", .number = 28 },
{ .name = "29 SIGIO", .number = 29 },
{ .name = "29 SIGPOLL", .number = 29 },
{ .name = "30 SIGPWR", .number = 30 },
{ .name = "31 SIGSYS", .number = 31 },
};
Panel* SignalsPanel_new() { Panel* SignalsPanel_new() {
Panel* this = Panel_new(1, 1, 1, 1, true, Class(ListItem)); Panel* this = Panel_new(1, 1, 1, 1, true, Class(ListItem), FunctionBar_newEnterEsc("Send ", "Cancel "));
for(unsigned int i = 0; i < sizeof(signals)/sizeof(SignalItem); i++) const int defaultSignal = SIGTERM;
Panel_set(this, i, (Object*) ListItem_new(signals[i].name, signals[i].number)); int defaultPosition = 15;
unsigned int i;
for (i = 0; i < Platform_numberOfSignals; i++) {
Panel_set(this, i, (Object*) ListItem_new(Platform_signals[i].name, Platform_signals[i].number));
// signal 15 is not always the 15th signal in the table
if (Platform_signals[i].number == defaultSignal) {
defaultPosition = i;
}
}
#if (defined(SIGRTMIN) && defined(SIGRTMAX))
if (SIGRTMAX - SIGRTMIN <= 100) {
static char buf[16];
for (int sig = SIGRTMIN; sig <= SIGRTMAX; i++, sig++) {
int n = sig - SIGRTMIN;
xSnprintf(buf, 16, "%2d SIGRTMIN%-+3d", sig, n);
if (n == 0) {
buf[11] = '\0';
}
Panel_set(this, i, (Object*) ListItem_new(buf, sig));
}
}
#endif
Panel_setHeader(this, "Send signal:"); Panel_setHeader(this, "Send signal:");
Panel_setSelected(this, 16); // 16th item is SIGTERM Panel_setSelected(this, defaultPosition);
return this; return this;
} }

View File

@ -1,28 +1,35 @@
/* /*
htop - String.c htop - StringUtils.c
(C) 2004-2011 Hisham H. Muhammad (C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "String.h" #include "StringUtils.h"
#include "XAlloc.h"
#include "config.h" #include "config.h"
#include <string.h> #include <string.h>
#include <strings.h> #include <strings.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h>
/*{ /*{
#define String_startsWith(s, match) (strstr((s), (match)) == (s)) #include <stdio.h>
#define String_startsWith(s, match) (strncmp((s),(match),strlen(match)) == 0)
#define String_contains_i(s1, s2) (strcasestr(s1, s2) != NULL) #define String_contains_i(s1, s2) (strcasestr(s1, s2) != NULL)
}*/ }*/
/*
* String_startsWith gives better performance if strlen(match) can be computed
* at compile time (e.g. when they are immutable string literals). :)
*/
char* String_cat(const char* s1, const char* s2) { char* String_cat(const char* s1, const char* s2) {
int l1 = strlen(s1); int l1 = strlen(s1);
int l2 = strlen(s2); int l2 = strlen(s2);
char* out = malloc(l1 + l2 + 1); char* out = xMalloc(l1 + l2 + 1);
strncpy(out, s1, l1); strncpy(out, s1, l1);
strncpy(out+l1, s2, l2+1); strncpy(out+l1, s2, l2+1);
return out; return out;
@ -36,7 +43,7 @@ char* String_trim(const char* in) {
while (len > 0 && (in[len-1] == ' ' || in[len-1] == '\t' || in[len-1] == '\n')) { while (len > 0 && (in[len-1] == ' ' || in[len-1] == '\t' || in[len-1] == '\n')) {
len--; len--;
} }
char* out = malloc(len+1); char* out = xMalloc(len+1);
strncpy(out, in, len); strncpy(out, in, len);
out[len] = '\0'; out[len] = '\0';
return out; return out;
@ -55,46 +62,40 @@ inline int String_eq(const char* s1, const char* s2) {
char** String_split(const char* s, char sep, int* n) { char** String_split(const char* s, char sep, int* n) {
*n = 0; *n = 0;
const int rate = 10; const int rate = 10;
char** out = (char**) malloc(sizeof(char*) * rate); char** out = xCalloc(rate, sizeof(char*));
int ctr = 0; int ctr = 0;
int blocks = rate; int blocks = rate;
char* where; char* where;
while ((where = strchr(s, sep)) != NULL) { while ((where = strchr(s, sep)) != NULL) {
int size = where - s; int size = where - s;
char* token = (char*) malloc(size + 1); char* token = xMalloc(size + 1);
strncpy(token, s, size); strncpy(token, s, size);
token[size] = '\0'; token[size] = '\0';
out[ctr] = token; out[ctr] = token;
ctr++; ctr++;
if (ctr == blocks) { if (ctr == blocks) {
blocks += rate; blocks += rate;
char** newOut = (char**) realloc(out, sizeof(char*) * blocks); out = (char**) xRealloc(out, sizeof(char*) * blocks);
if (newOut) {
out = newOut;
} else {
blocks -= rate;
break;
}
} }
s += size + 1; s += size + 1;
} }
if (s[0] != '\0') { if (s[0] != '\0') {
int size = strlen(s); int size = strlen(s);
char* token = (char*) malloc(size + 1); char* token = xMalloc(size + 1);
strncpy(token, s, size + 1); strncpy(token, s, size + 1);
out[ctr] = token; out[ctr] = token;
ctr++; ctr++;
} }
char** newOut = realloc(out, sizeof(char*) * (ctr + 1)); out = xRealloc(out, sizeof(char*) * (ctr + 1));
if (newOut) {
out = newOut;
}
out[ctr] = NULL; out[ctr] = NULL;
*n = ctr; *n = ctr;
return out; return out;
} }
void String_freeArray(char** s) { void String_freeArray(char** s) {
if (!s) {
return;
}
for (int i = 0; s[i] != NULL; i++) { for (int i = 0; s[i] != NULL; i++) {
free(s[i]); free(s[i]);
} }
@ -125,5 +126,31 @@ char* String_getToken(const char* line, const unsigned short int numMatch) {
} }
match[foundCount] = '\0'; match[foundCount] = '\0';
return((char*)strdup(match)); return((char*)xStrdup(match));
}
char* String_readLine(FILE* fd) {
const int step = 1024;
int bufSize = step;
char* buffer = xMalloc(step + 1);
char* at = buffer;
for (;;) {
char* ok = fgets(at, step + 1, fd);
if (!ok) {
free(buffer);
return NULL;
}
char* newLine = strrchr(at, '\n');
if (newLine) {
*newLine = '\0';
return buffer;
} else {
if (feof(fd)) {
return buffer;
}
}
bufSize += step;
buffer = xRealloc(buffer, bufSize + 1);
at = buffer + bufSize - step;
}
} }

View File

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

View File

@ -8,7 +8,7 @@ in the source distribution for its full text.
#include "SwapMeter.h" #include "SwapMeter.h"
#include "CRT.h" #include "CRT.h"
#include "ProcessList.h" #include "Platform.h"
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -20,37 +20,31 @@ in the source distribution for its full text.
#include "Meter.h" #include "Meter.h"
}*/ }*/
#define KILOBYTE 1
#define MEGABYTE 1024
#define GIGABYTE 1048576
int SwapMeter_attributes[] = { int SwapMeter_attributes[] = {
SWAP SWAP
}; };
/* NOTE: Value is in kilobytes */ static void SwapMeter_updateValues(Meter* this, char* buffer, int size) {
static void SwapMeter_humanNumber(char* buffer, const long int* value) { int written;
sprintf(buffer, "%ldM ", *value / MEGABYTE); Platform_setSwapValues(this);
}
static void SwapMeter_setValues(Meter* this, char* buffer, int len) { written = Meter_humanUnit(buffer, this->values[0], size);
long int usedSwap = this->pl->usedSwap; buffer += written;
this->total = this->pl->totalSwap; if ((size -= written) > 0) {
this->values[0] = usedSwap; *buffer++ = '/';
size--;
snprintf(buffer, len, "%ld/%ldMB", (long int) usedSwap / MEGABYTE, (long int) this->total / MEGABYTE); Meter_humanUnit(buffer, this->total, size);
}
} }
static void SwapMeter_display(Object* cast, RichString* out) { static void SwapMeter_display(Object* cast, RichString* out) {
char buffer[50]; char buffer[50];
Meter* this = (Meter*)cast; Meter* this = (Meter*)cast;
long int swap = (long int) this->values[0];
long int total = (long int) this->total;
RichString_write(out, CRT_colors[METER_TEXT], ":"); RichString_write(out, CRT_colors[METER_TEXT], ":");
SwapMeter_humanNumber(buffer, &total); Meter_humanUnit(buffer, this->total, 50);
RichString_append(out, CRT_colors[METER_VALUE], buffer); RichString_append(out, CRT_colors[METER_VALUE], buffer);
SwapMeter_humanNumber(buffer, &swap); Meter_humanUnit(buffer, this->values[0], 50);
RichString_append(out, CRT_colors[METER_TEXT], "used:"); RichString_append(out, CRT_colors[METER_TEXT], " used:");
RichString_append(out, CRT_colors[METER_VALUE], buffer); RichString_append(out, CRT_colors[METER_VALUE], buffer);
} }
@ -60,8 +54,9 @@ MeterClass SwapMeter_class = {
.delete = Meter_delete, .delete = Meter_delete,
.display = SwapMeter_display, .display = SwapMeter_display,
}, },
.setValues = SwapMeter_setValues, .updateValues = SwapMeter_updateValues,
.defaultMode = BAR_METERMODE, .defaultMode = BAR_METERMODE,
.maxItems = 1,
.total = 100.0, .total = 100.0,
.attributes = SwapMeter_attributes, .attributes = SwapMeter_attributes,
.name = "Swap", .name = "Swap",

View File

@ -11,13 +11,8 @@ in the source distribution for its full text.
#include "Meter.h" #include "Meter.h"
#define KILOBYTE 1
#define MEGABYTE 1024
#define GIGABYTE 1048576
extern int SwapMeter_attributes[]; extern int SwapMeter_attributes[];
/* NOTE: Value is in kilobytes */
extern MeterClass SwapMeter_class; extern MeterClass SwapMeter_class;
#endif #endif

155
TESTPLAN Normal file
View File

@ -0,0 +1,155 @@
Main screen:
For all views, all modes:
Mouse click header - nothing happens.
Mouse click on ProcessList title bar - exit Tree view, update FunctionBar, title bar updates, sort by clicked field.
*** FAILING: wrong FB update depending on mode; does not change sort in wip branch
click on same entry - invert sort.
click on another entry - sort another field.
Mouse click on a process - select that process.
for each entry in FunctionBar:
Mouse click entry - perform action of associated key.
In Normal mode, Sorted view:
<+> or <-> - do nothing.
<F6> - enter SortBy screen.
In Normal mode, Tree view:
select process - update F6 in FunctionBar if subtree is collapsed or expanded.
<F6>, <+> or <-> - expand/collapse subtree.
In Normal mode, either Sorted or Tree view:
<F3>, </> - activate Search mode.
<F4>, <\> - activate Filter mode.
<F7>, <]> - as root only, decrease process NICE value.
<F8>, <[> - increase process NICE value.
<a> - enter Affinity screen.
<b> - do nothing.
<c> - select process and all its children.
<d>, <e>, <f>, <g> - do nothing.
<F1>, <h>, <?> - enter Help screen.
<i> - on Linux, enter IOPriority screen.
<j> - do nothing.
<F9>, <k> - enter Kill screen.
<l> - enter LSOF screen.
<m>, <n>, <o>, <p> - do nothing.
<F10>, <q> - quit program.
<r> - do nothing.
<s> - enter STrace screen.
<F5>, <t> - toggle between Tree and Sorted view, update F5 in FunctionBar, follow process
<u> - enter User screen.
<v>, <w>, <x>, <y>, <z> - do nothing.
<A>, <B> - do nothing.
<F2>, <C>, <S> - enter Setup screen.
<D>, <E> - do nothing.
<F> - follow process.
<G> - do nothing.
<H> - toggle show/hide userland threads.
<I> - invert sort order.
<J> - do nothing.
<K> - toggle show/hide kernel threads.
<L> - do nothing.
<M> - enter Sorted view, update function bar, sort by MEM%.
<N>, <O> - do nothing.
<P> - enter Sorted view, update function bar, sort by CPU%.
<Q>, <R> - do nothing.
<T> - enter Sorted view, update function bar, sort by TIME.
<U> - untag all processes.
<V>, <W>, <X>, <Y>, <Z> - do nothing.
<<>, <>>, <,>, <.> - enter SortBy screen.
space - tag current process, move down cursor.
numbers - incremental PID search.
In Search mode:
TODO
In Filter mode:
TODO
Setup screen:
TODO
SortBy screen:
TODO
User screen:
TODO
Kill screen:
TODO
Affinity screen:
TODO
Help screen:
any key - back to Main screen.
IOPriority screen:
TODO
STrace screen:
TODO
LSOF screen:
TODO

View File

@ -7,7 +7,7 @@ in the source distribution for its full text.
#include "TasksMeter.h" #include "TasksMeter.h"
#include "ProcessList.h" #include "Platform.h"
#include "CRT.h" #include "CRT.h"
/*{ /*{
@ -15,42 +15,53 @@ in the source distribution for its full text.
}*/ }*/
int TasksMeter_attributes[] = { int TasksMeter_attributes[] = {
TASKS_RUNNING CPU_KERNEL, PROCESS_THREAD, PROCESS, TASKS_RUNNING
}; };
static void TasksMeter_setValues(Meter* this, char* buffer, int len) { static void TasksMeter_updateValues(Meter* this, char* buffer, int len) {
ProcessList* pl = this->pl; ProcessList* pl = this->pl;
this->total = pl->totalTasks; this->values[0] = pl->kernelThreads;
this->values[0] = pl->runningTasks; this->values[1] = pl->userlandThreads;
snprintf(buffer, len, "%d/%d", (int) this->values[0], (int) this->total); this->values[2] = pl->totalTasks - pl->kernelThreads - pl->userlandThreads;
this->values[3] = MIN(pl->runningTasks, pl->cpuCount);
if (pl->totalTasks > this->total) {
this->total = pl->totalTasks;
}
if (this->pl->settings->hideKernelThreads) {
this->values[0] = 0;
}
xSnprintf(buffer, len, "%d/%d", (int) this->values[3], (int) this->total);
} }
static void TasksMeter_display(Object* cast, RichString* out) { static void TasksMeter_display(Object* cast, RichString* out) {
Meter* this = (Meter*)cast; Meter* this = (Meter*)cast;
ProcessList* pl = this->pl; Settings* settings = this->pl->settings;
char buffer[20]; char buffer[20];
sprintf(buffer, "%d", (int)(this->total - pl->userlandThreads - pl->kernelThreads));
int processes = (int) this->values[2];
xSnprintf(buffer, sizeof(buffer), "%d", processes);
RichString_write(out, CRT_colors[METER_VALUE], buffer); RichString_write(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 (pl->highlightThreads) { if (settings->highlightThreads) {
threadValueColor = CRT_colors[PROCESS_THREAD_BASENAME]; threadValueColor = CRT_colors[PROCESS_THREAD_BASENAME];
threadCaptionColor = CRT_colors[PROCESS_THREAD]; threadCaptionColor = CRT_colors[PROCESS_THREAD];
} }
if (!pl->hideUserlandThreads) { if (!settings->hideUserlandThreads) {
RichString_append(out, CRT_colors[METER_TEXT], ", "); RichString_append(out, CRT_colors[METER_TEXT], ", ");
sprintf(buffer, "%d", (int)pl->userlandThreads); xSnprintf(buffer, sizeof(buffer), "%d", (int)this->values[1]);
RichString_append(out, threadValueColor, buffer); RichString_append(out, threadValueColor, buffer);
RichString_append(out, threadCaptionColor, " thr"); RichString_append(out, threadCaptionColor, " thr");
} }
if (!pl->hideKernelThreads) { if (!settings->hideKernelThreads) {
RichString_append(out, CRT_colors[METER_TEXT], ", "); RichString_append(out, CRT_colors[METER_TEXT], ", ");
sprintf(buffer, "%d", (int)pl->kernelThreads); xSnprintf(buffer, sizeof(buffer), "%d", (int)this->values[0]);
RichString_append(out, threadValueColor, buffer); RichString_append(out, threadValueColor, buffer);
RichString_append(out, threadCaptionColor, " kthr"); RichString_append(out, threadCaptionColor, " kthr");
} }
RichString_append(out, CRT_colors[METER_TEXT], "; "); RichString_append(out, CRT_colors[METER_TEXT], "; ");
sprintf(buffer, "%d", (int)this->values[0]); xSnprintf(buffer, sizeof(buffer), "%d", (int)this->values[3]);
RichString_append(out, CRT_colors[TASKS_RUNNING], buffer); RichString_append(out, CRT_colors[TASKS_RUNNING], buffer);
RichString_append(out, CRT_colors[METER_TEXT], " running"); RichString_append(out, CRT_colors[METER_TEXT], " running");
} }
@ -61,8 +72,9 @@ MeterClass TasksMeter_class = {
.delete = Meter_delete, .delete = Meter_delete,
.display = TasksMeter_display, .display = TasksMeter_display,
}, },
.setValues = TasksMeter_setValues, .updateValues = TasksMeter_updateValues,
.defaultMode = TEXT_METERMODE, .defaultMode = TEXT_METERMODE,
.maxItems = 4,
.total = 100.0, .total = 100.0,
.attributes = TasksMeter_attributes, .attributes = TasksMeter_attributes,
.name = "Tasks", .name = "Tasks",

View File

@ -8,10 +8,12 @@ in the source distribution for its full text.
#include "TraceScreen.h" #include "TraceScreen.h"
#include "CRT.h" #include "CRT.h"
#include "InfoScreen.h"
#include "ProcessList.h" #include "ProcessList.h"
#include "ListItem.h" #include "ListItem.h"
#include "IncSet.h" #include "IncSet.h"
#include "String.h" #include "StringUtils.h"
#include "FunctionBar.h"
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
@ -20,210 +22,153 @@ in the source distribution for its full text.
#include <stdbool.h> #include <stdbool.h>
#include <unistd.h> #include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
#include <sys/time.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <signal.h> #include <signal.h>
/*{ /*{
#include "Process.h" #include "InfoScreen.h"
#include "Panel.h"
#include "FunctionBar.h"
typedef struct TraceScreen_ { typedef struct TraceScreen_ {
Process* process; InfoScreen super;
Panel* display;
bool tracing; bool tracing;
int fdpair[2];
int child;
FILE* strace;
int fd_strace;
bool contLine;
bool follow;
} TraceScreen; } TraceScreen;
}*/ }*/
static const char* tsFunctions[] = {"Search ", "Filter ", "AutoScroll ", "Stop Tracing ", "Done ", NULL}; static const char* const TraceScreenFunctions[] = {"Search ", "Filter ", "AutoScroll ", "Stop Tracing ", "Done ", NULL};
static const char* tsKeys[] = {"F3", "F4", "F8", "F9", "Esc"}; static const char* const TraceScreenKeys[] = {"F3", "F4", "F8", "F9", "Esc"};
static int tsEvents[] = {KEY_F(3), KEY_F(4), KEY_F(8), KEY_F(9), 27}; static int TraceScreenEvents[] = {KEY_F(3), KEY_F(4), KEY_F(8), KEY_F(9), 27};
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) {
TraceScreen* this = (TraceScreen*) malloc(sizeof(TraceScreen)); TraceScreen* this = xMalloc(sizeof(TraceScreen));
this->process = process; Object_setClass(this, Class(TraceScreen));
this->display = Panel_new(0, 1, COLS, LINES-2, false, Class(ListItem));
this->tracing = true; this->tracing = true;
return this; this->contLine = false;
this->follow = false;
FunctionBar* fuBar = FunctionBar_new(TraceScreenFunctions, TraceScreenKeys, TraceScreenEvents);
CRT_disableDelay();
return (TraceScreen*) InfoScreen_init(&this->super, process, fuBar, LINES-2, "");
} }
void TraceScreen_delete(TraceScreen* this) { void TraceScreen_delete(Object* cast) {
Panel_delete((Object*)this->display); TraceScreen* this = (TraceScreen*) cast;
free(this); if (this->child > 0) {
kill(this->child, SIGTERM);
waitpid(this->child, NULL, 0);
fclose(this->strace);
}
CRT_enableDelay();
free(InfoScreen_done((InfoScreen*)cast));
} }
static void TraceScreen_draw(TraceScreen* this, IncSet* inc) { void TraceScreen_draw(InfoScreen* this) {
attrset(CRT_colors[PANEL_HEADER_FOCUS]); attrset(CRT_colors[PANEL_HEADER_FOCUS]);
mvhline(0, 0, ' ', COLS); mvhline(0, 0, ' ', COLS);
mvprintw(0, 0, "Trace of process %d - %s", this->process->pid, this->process->comm); mvprintw(0, 0, "Trace of process %d - %s", this->process->pid, this->process->comm);
attrset(CRT_colors[DEFAULT_COLOR]); attrset(CRT_colors[DEFAULT_COLOR]);
IncSet_drawBar(inc); IncSet_drawBar(this->inc);
} }
static inline void addLine(const char* line, Vector* lines, Panel* panel, const char* incFilter) { bool TraceScreen_forkTracer(TraceScreen* this) {
Vector_add(lines, (Object*) ListItem_new(line, 0));
if (!incFilter || String_contains_i(line, incFilter))
Panel_add(panel, (Object*)Vector_get(lines, Vector_size(lines)-1));
}
static inline void appendLine(const char* line, Vector* lines, Panel* panel, const char* incFilter) {
ListItem* last = (ListItem*)Vector_get(lines, Vector_size(lines)-1);
ListItem_append(last, line);
if (incFilter && Panel_get(panel, Panel_size(panel)-1) != (Object*)last && String_contains_i(line, incFilter))
Panel_add(panel, (Object*)last);
}
void TraceScreen_run(TraceScreen* this) {
// if (this->process->pid == getpid()) return;
char buffer[1001]; char buffer[1001];
int fdpair[2]; int error = pipe(this->fdpair);
int err = pipe(fdpair); if (error == -1) return false;
if (err == -1) return; this->child = fork();
int child = fork(); if (this->child == -1) return false;
if (child == -1) return; if (this->child == 0) {
if (child == 0) { CRT_dropPrivileges();
dup2(fdpair[1], STDERR_FILENO); dup2(this->fdpair[1], STDERR_FILENO);
int ok = fcntl(fdpair[1], F_SETFL, O_NONBLOCK); int ok = fcntl(this->fdpair[1], F_SETFL, O_NONBLOCK);
if (ok != -1) { if (ok != -1) {
sprintf(buffer, "%d", this->process->pid); xSnprintf(buffer, sizeof(buffer), "%d", this->super.process->pid);
execlp("strace", "strace", "-p", buffer, NULL); execlp("strace", "strace", "-s", "512", "-p", buffer, NULL);
} }
const char* message = "Could not execute 'strace'. Please make sure it is available in your $PATH."; const char* message = "Could not execute 'strace'. Please make sure it is available in your $PATH.";
write(fdpair[1], message, strlen(message)); ssize_t written = write(this->fdpair[1], message, strlen(message));
(void) written;
exit(1); exit(1);
} }
fcntl(fdpair[0], F_SETFL, O_NONBLOCK); int ok = fcntl(this->fdpair[0], F_SETFL, O_NONBLOCK);
FILE* strace = fdopen(fdpair[0], "r"); if (ok == -1) return false;
Panel* panel = this->display; this->strace = fdopen(this->fdpair[0], "r");
int fd_strace = fileno(strace); this->fd_strace = fileno(this->strace);
CRT_disableDelay(); return true;
bool contLine = false; }
bool follow = false;
bool looping = true;
FunctionBar* bar = FunctionBar_new(tsFunctions, tsKeys, tsEvents); void TraceScreen_updateTrace(InfoScreen* super) {
IncSet* inc = IncSet_new(bar); TraceScreen* this = (TraceScreen*) super;
char buffer[1001];
Vector* lines = Vector_new(panel->items->type, true, DEFAULT_SIZE); fd_set fds;
FD_ZERO(&fds);
TraceScreen_draw(this, inc); // FD_SET(STDIN_FILENO, &fds);
FD_SET(this->fd_strace, &fds);
while (looping) { struct timeval tv;
tv.tv_sec = 0; tv.tv_usec = 500;
Panel_draw(panel, true); int ready = select(this->fd_strace+1, &fds, NULL, NULL, &tv);
const char* incFilter = IncSet_filter(inc); int nread = 0;
if (ready > 0 && FD_ISSET(this->fd_strace, &fds))
if (inc->active) nread = fread(buffer, 1, 1000, this->strace);
move(LINES-1, CRT_cursorX); if (nread && this->tracing) {
int ch = getch(); char* line = buffer;
buffer[nread] = '\0';
if (ch == ERR) { for (int i = 0; i < nread; i++) {
fd_set fds; if (buffer[i] == '\n') {
FD_ZERO(&fds); buffer[i] = '\0';
// FD_SET(STDIN_FILENO, &fds); if (this->contLine) {
FD_SET(fd_strace, &fds); InfoScreen_appendLine(&this->super, line);
struct timeval tv; this->contLine = false;
tv.tv_sec = 0; tv.tv_usec = 500; } else {
int ready = select(fd_strace+1, &fds, NULL, NULL, &tv); InfoScreen_addLine(&this->super, line);
int nread = 0;
if (ready > 0 && FD_ISSET(fd_strace, &fds))
nread = fread(buffer, 1, 1000, strace);
if (nread && this->tracing) {
char* line = buffer;
buffer[nread] = '\0';
for (int i = 0; i < nread; i++) {
if (buffer[i] == '\n') {
buffer[i] = '\0';
if (contLine) {
appendLine(line, lines, panel, incFilter);
contLine = false;
} else {
addLine(line, lines, panel, incFilter);
}
line = buffer+i+1;
}
} }
if (line < buffer+nread) { line = buffer+i+1;
addLine(line, lines, panel, incFilter);
buffer[nread] = '\0';
contLine = true;
}
if (follow)
Panel_setSelected(panel, Panel_size(panel)-1);
} }
} }
if (line < buffer+nread) {
if (ch == KEY_MOUSE) { InfoScreen_addLine(&this->super, line);
MEVENT mevent; buffer[nread] = '\0';
int ok = getmouse(&mevent); this->contLine = true;
if (ok == OK)
if (mevent.y >= panel->y && mevent.y < LINES - 1) {
Panel_setSelected(panel, mevent.y - panel->y + panel->scrollV);
follow = false;
ch = 0;
} if (mevent.y == LINES - 1)
ch = FunctionBar_synthesizeEvent(inc->bar, mevent.x);
} }
if (this->follow)
Panel_setSelected(this->super.display, Panel_size(this->super.display)-1);
}
}
if (inc->active) { bool TraceScreen_onKey(InfoScreen* super, int ch) {
IncSet_handleKey(inc, ch, panel, IncSet_getListItemValue, lines); TraceScreen* this = (TraceScreen*) super;
continue; switch(ch) {
}
switch(ch) {
case ERR:
continue;
case KEY_HOME:
Panel_setSelected(panel, 0);
break;
case KEY_END:
Panel_setSelected(panel, Panel_size(panel)-1);
break;
case KEY_F(3):
case '/':
IncSet_activate(inc, INC_SEARCH);
break;
case KEY_F(4):
case '\\':
IncSet_activate(inc, INC_FILTER);
break;
case 'f': case 'f':
case KEY_F(8): case KEY_F(8):
follow = !follow; this->follow = !(this->follow);
if (follow) if (this->follow)
Panel_setSelected(panel, Panel_size(panel)-1); Panel_setSelected(super->display, Panel_size(super->display)-1);
break; return true;
case 't': case 't':
case KEY_F(9): case KEY_F(9):
this->tracing = !this->tracing; this->tracing = !this->tracing;
FunctionBar_setLabel(bar, KEY_F(9), this->tracing?"Stop Tracing ":"Resume Tracing "); FunctionBar_setLabel(super->display->defaultBar, KEY_F(9), this->tracing?"Stop Tracing ":"Resume Tracing ");
TraceScreen_draw(this, inc); InfoScreen_draw(this);
break; return true;
case 'q':
case 27:
case KEY_F(10):
looping = false;
break;
case KEY_RESIZE:
Panel_resize(panel, COLS, LINES-2);
TraceScreen_draw(this, inc);
break;
default:
follow = false;
Panel_onKey(panel, ch);
}
} }
this->follow = false;
IncSet_delete(inc); return false;
FunctionBar_delete((Object*)bar);
Vector_delete(lines);
kill(child, SIGTERM);
waitpid(child, NULL, 0);
fclose(strace);
CRT_enableDelay();
} }

View File

@ -9,21 +9,32 @@ Released under the GNU GPL, see the COPYING file
in the source distribution for its full text. in the source distribution for its full text.
*/ */
#include "Process.h" #include "InfoScreen.h"
#include "Panel.h"
#include "FunctionBar.h"
typedef struct TraceScreen_ { typedef struct TraceScreen_ {
Process* process; InfoScreen super;
Panel* display;
bool tracing; bool tracing;
int fdpair[2];
int child;
FILE* strace;
int fd_strace;
bool contLine;
bool follow;
} TraceScreen; } TraceScreen;
extern InfoScreenClass TraceScreen_class;
TraceScreen* TraceScreen_new(Process* process); TraceScreen* TraceScreen_new(Process* process);
void TraceScreen_delete(TraceScreen* this); void TraceScreen_delete(Object* cast);
void TraceScreen_run(TraceScreen* this); void TraceScreen_draw(InfoScreen* this);
bool TraceScreen_forkTracer(TraceScreen* this);
void TraceScreen_updateTrace(InfoScreen* super);
bool TraceScreen_onKey(InfoScreen* super, int ch);
#endif #endif

View File

@ -6,12 +6,9 @@ in the source distribution for its full text.
*/ */
#include "UptimeMeter.h" #include "UptimeMeter.h"
#include "Platform.h"
#include "ProcessList.h"
#include "CRT.h" #include "CRT.h"
#include <math.h>
/*{ /*{
#include "Meter.h" #include "Meter.h"
}*/ }*/
@ -20,14 +17,12 @@ int UptimeMeter_attributes[] = {
UPTIME UPTIME
}; };
static void UptimeMeter_setValues(Meter* this, char* buffer, int len) { static void UptimeMeter_updateValues(Meter* this, char* buffer, int len) {
double uptime = 0; int totalseconds = Platform_getUptime();
FILE* fd = fopen(PROCDIR "/uptime", "r"); if (totalseconds == -1) {
if (fd) { xSnprintf(buffer, len, "(unknown)");
fscanf(fd, "%64lf", &uptime); return;
fclose(fd);
} }
int totalseconds = (int) ceil(uptime);
int seconds = totalseconds % 60; int seconds = totalseconds % 60;
int minutes = (totalseconds/60) % 60; int minutes = (totalseconds/60) % 60;
int hours = (totalseconds/3600) % 24; int hours = (totalseconds/3600) % 24;
@ -36,17 +31,17 @@ static void UptimeMeter_setValues(Meter* this, char* buffer, int len) {
if (days > this->total) { if (days > this->total) {
this->total = days; this->total = days;
} }
char daysbuf[15]; char daysbuf[32];
if (days > 100) { if (days > 100) {
sprintf(daysbuf, "%d days(!), ", days); xSnprintf(daysbuf, sizeof(daysbuf), "%d days(!), ", days);
} else if (days > 1) { } else if (days > 1) {
sprintf(daysbuf, "%d days, ", days); xSnprintf(daysbuf, sizeof(daysbuf), "%d days, ", days);
} else if (days == 1) { } else if (days == 1) {
sprintf(daysbuf, "1 day, "); xSnprintf(daysbuf, sizeof(daysbuf), "1 day, ");
} else { } else {
daysbuf[0] = '\0'; daysbuf[0] = '\0';
} }
snprintf(buffer, len, "%s%02d:%02d:%02d", daysbuf, hours, minutes, seconds); xSnprintf(buffer, len, "%s%02d:%02d:%02d", daysbuf, hours, minutes, seconds);
} }
MeterClass UptimeMeter_class = { MeterClass UptimeMeter_class = {
@ -54,8 +49,9 @@ MeterClass UptimeMeter_class = {
.extends = Class(Meter), .extends = Class(Meter),
.delete = Meter_delete .delete = Meter_delete
}, },
.setValues = UptimeMeter_setValues, .updateValues = UptimeMeter_updateValues,
.defaultMode = TEXT_METERMODE, .defaultMode = TEXT_METERMODE,
.maxItems = 1,
.total = 100.0, .total = 100.0,
.attributes = UptimeMeter_attributes, .attributes = UptimeMeter_attributes,
.name = "Uptime", .name = "Uptime",

View File

@ -6,6 +6,7 @@ in the source distribution for its full text.
*/ */
#include "UsersTable.h" #include "UsersTable.h"
#include "XAlloc.h"
#include "config.h" #include "config.h"
@ -27,7 +28,7 @@ typedef struct UsersTable_ {
UsersTable* UsersTable_new() { UsersTable* UsersTable_new() {
UsersTable* this; UsersTable* this;
this = malloc(sizeof(UsersTable)); this = xMalloc(sizeof(UsersTable));
this->users = Hashtable_new(20, true); this->users = Hashtable_new(20, true);
return this; return this;
} }
@ -42,7 +43,7 @@ char* UsersTable_getRef(UsersTable* this, unsigned int uid) {
if (name == NULL) { if (name == NULL) {
struct passwd* userData = getpwuid(uid); struct passwd* userData = getpwuid(uid);
if (userData != NULL) { if (userData != NULL) {
name = strdup(userData->pw_name); name = xStrdup(userData->pw_name);
Hashtable_put(this->users, uid, name); Hashtable_put(this->users, uid, name);
} }
} }

View File

@ -37,9 +37,9 @@ Vector* Vector_new(ObjectClass* type, bool owner, int size) {
if (size == DEFAULT_SIZE) if (size == DEFAULT_SIZE)
size = 10; size = 10;
this = (Vector*) malloc(sizeof(Vector)); this = xMalloc(sizeof(Vector));
this->growthRate = size; this->growthRate = size;
this->array = (Object**) calloc(size, sizeof(Object*)); this->array = (Object**) xCalloc(size, sizeof(Object*));
this->arraySize = size; this->arraySize = size;
this->items = 0; this->items = 0;
this->type = type; this->type = type;
@ -179,7 +179,7 @@ static void Vector_checkArraySize(Vector* this) {
//int i; //int i;
//i = this->arraySize; //i = this->arraySize;
this->arraySize = this->items + this->growthRate; this->arraySize = this->items + this->growthRate;
this->array = (Object**) realloc(this->array, sizeof(Object*) * this->arraySize); this->array = (Object**) xRealloc(this->array, sizeof(Object*) * this->arraySize);
//for (; i < this->arraySize; i++) //for (; i < this->arraySize; i++)
// this->array[i] = NULL; // this->array[i] = NULL;
} }
@ -189,10 +189,13 @@ static void Vector_checkArraySize(Vector* this) {
void Vector_insert(Vector* this, int idx, void* data_) { void Vector_insert(Vector* this, int idx, void* data_) {
Object* data = data_; Object* data = data_;
assert(idx >= 0); assert(idx >= 0);
assert(idx <= this->items);
assert(Object_isA(data, this->type)); assert(Object_isA(data, this->type));
assert(Vector_isConsistent(this)); assert(Vector_isConsistent(this));
if (idx > this->items) {
idx = this->items;
}
Vector_checkArraySize(this); Vector_checkArraySize(this);
//assert(this->array[this->items] == NULL); //assert(this->array[this->items] == NULL);
for (int i = this->items; i > idx; i--) { for (int i = this->items; i > idx; i--) {
@ -281,11 +284,19 @@ inline Object* Vector_get(Vector* this, int idx) {
#endif #endif
#ifdef DEBUG
inline int Vector_size(Vector* this) { inline int Vector_size(Vector* this) {
assert(Vector_isConsistent(this)); assert(Vector_isConsistent(this));
return this->items; return this->items;
} }
#else
#define Vector_size(v_) ((v_)->items)
#endif
/* /*
static void Vector_merge(Vector* this, Vector* v2) { static void Vector_merge(Vector* this, Vector* v2) {

View File

@ -70,8 +70,16 @@ extern Object* Vector_get(Vector* this, int idx);
#endif #endif
#ifdef DEBUG
extern int Vector_size(Vector* this); extern int Vector_size(Vector* this);
#else
#define Vector_size(v_) ((v_)->items)
#endif
/* /*
*/ */

71
XAlloc.c Normal file
View File

@ -0,0 +1,71 @@
#include "XAlloc.h"
#include "RichString.h"
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stdlib.h>
#include <string.h>
/*{
#include <err.h>
#include <assert.h>
#include <stdlib.h>
}*/
static inline void fail() {
curs_set(1);
endwin();
err(1, NULL);
}
void* xMalloc(size_t size) {
void* data = malloc(size);
if (!data && size > 0) {
fail();
}
return data;
}
void* xCalloc(size_t nmemb, size_t size) {
void* data = calloc(nmemb, size);
if (!data && nmemb > 0 && size > 0) {
fail();
}
return data;
}
void* xRealloc(void* ptr, size_t size) {
void* data = realloc(ptr, size);
if (!data && size > 0) {
fail();
}
return data;
}
#define xSnprintf(fmt, len, ...) do { int _l=len; int _n=snprintf(fmt, _l, __VA_ARGS__); if (!(_n > -1 && _n < _l)) { curs_set(1); endwin(); err(1, NULL); } } while(0)
#undef xStrdup
#undef xStrdup_
#ifdef NDEBUG
# define xStrdup_ xStrdup
#else
# define xStrdup(str_) (assert(str_), xStrdup_(str_))
#endif
#ifndef __has_attribute // Clang's macro
# define __has_attribute(x) 0
#endif
#if (__has_attribute(nonnull) || \
((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)))
char* xStrdup_(const char* str) __attribute__((nonnull));
#endif // __has_attribute(nonnull) || GNU C 3.3 or later
char* xStrdup_(const char* str) {
char* data = strdup(str);
if (!data) {
fail();
}
return data;
}

40
XAlloc.h Normal file
View File

@ -0,0 +1,40 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_XAlloc
#define HEADER_XAlloc
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <err.h>
#include <assert.h>
#include <stdlib.h>
void* xMalloc(size_t size);
void* xCalloc(size_t nmemb, size_t size);
void* xRealloc(void* ptr, size_t size);
#define xSnprintf(fmt, len, ...) do { int _l=len; int _n=snprintf(fmt, _l, __VA_ARGS__); if (!(_n > -1 && _n < _l)) { curs_set(1); endwin(); err(1, NULL); } } while(0)
#undef xStrdup
#undef xStrdup_
#ifdef NDEBUG
# define xStrdup_ xStrdup
#else
# define xStrdup(str_) (assert(str_), xStrdup_(str_))
#endif
#ifndef __has_attribute // Clang's macro
# define __has_attribute(x) 0
#endif
#if (__has_attribute(nonnull) || \
((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)))
char* xStrdup_(const char* str) __attribute__((nonnull));
#endif // __has_attribute(nonnull) || GNU C 3.3 or later
char* xStrdup_(const char* str);
#endif

View File

@ -1,9 +1,3 @@
#!/bin/sh #!/bin/sh
mkdir -p m4
aclocal -I m4 autoreconf --install --force
autoconf
autoheader
libtoolize --copy --force
automake --add-missing --copy

View File

@ -2,29 +2,61 @@
# 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],[1.0.3],[hisham@gobolinux.org]) AC_INIT([htop],[2.2.0],[hisham@gobolinux.org])
# The following two lines are required by hwloc scripts SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:-$(date +%s)}"
AC_USE_SYSTEM_EXTENSIONS year=$(date -u -d "@$SOURCE_DATE_EPOCH" "+%Y" 2>/dev/null || date -u -r "$SOURCE_DATE_EPOCH" "+%Y" 2>/dev/null || date -u "+%Y")
AC_CONFIG_SRCDIR([htop.c])
AC_CONFIG_AUX_DIR([.])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_MACRO_DIR([m4])
# Required by hwloc scripts
AC_CANONICAL_TARGET AC_CANONICAL_TARGET
AM_INIT_AUTOMAKE([1.11]) AM_INIT_AUTOMAKE([1.11])
AC_CONFIG_SRCDIR([htop.c])
AC_CONFIG_HEADER([config.h])
AC_CONFIG_MACRO_DIR([m4])
# Checks for programs. # Checks for programs.
# ----------------------------------------------------------------------
AC_PROG_CC AC_PROG_CC
AM_PROG_CC_C_O AM_PROG_CC_C_O
AC_DISABLE_SHARED # Required by hwloc scripts
AC_ENABLE_STATIC AC_USE_SYSTEM_EXTENSIONS
AC_PROG_LIBTOOL
# Checks for platform.
# ----------------------------------------------------------------------
case "$target_os" in
linux*|gnu*)
my_htop_platform=linux
;;
freebsd*|kfreebsd*)
my_htop_platform=freebsd
;;
openbsd*)
my_htop_platform=openbsd
;;
dragonfly*)
my_htop_platform=dragonflybsd
;;
darwin*)
my_htop_platform=darwin
;;
solaris*)
my_htop_platform=solaris
;;
*)
my_htop_platform=unsupported
;;
esac
# Checks for libraries. # Checks for libraries.
# ----------------------------------------------------------------------
AC_CHECK_LIB([m], [ceil], [], [missing_libraries="$missing_libraries libm"]) AC_CHECK_LIB([m], [ceil], [], [missing_libraries="$missing_libraries libm"])
# Checks for header files. # Checks for header files.
# ----------------------------------------------------------------------
AC_HEADER_DIRENT AC_HEADER_DIRENT
AC_HEADER_STDC AC_HEADER_STDC
AC_CHECK_HEADERS([stdlib.h string.h strings.h sys/param.h sys/time.h unistd.h],[:],[ AC_CHECK_HEADERS([stdlib.h string.h strings.h sys/param.h sys/time.h unistd.h],[:],[
@ -32,13 +64,25 @@ AC_CHECK_HEADERS([stdlib.h string.h strings.h sys/param.h sys/time.h unistd.h],[
]) ])
AC_CHECK_HEADERS([execinfo.h],[:],[:]) AC_CHECK_HEADERS([execinfo.h],[:],[:])
AC_HEADER_MAJOR
dnl glibc 2.25 deprecates 'major' and 'minor' in <sys/types.h> and requires to
dnl include <sys/sysmacros.h>. However the logic in AC_HEADER_MAJOR has not yet
dnl been updated in Autoconf 2.69, so use a workaround:
m4_version_prereq([2.70], [],
[if test "x$ac_cv_header_sys_mkdev_h" = xno; then
AC_CHECK_HEADER(sys/sysmacros.h, [AC_DEFINE(MAJOR_IN_SYSMACROS, 1,
[Define to 1 if `major', `minor', and `makedev' are declared in <sys/sysmacros.h>.])])
fi])
# Checks for typedefs, structures, and compiler characteristics. # Checks for typedefs, structures, and compiler characteristics.
# ----------------------------------------------------------------------
AC_HEADER_STDBOOL AC_HEADER_STDBOOL
AC_C_CONST AC_C_CONST
AC_TYPE_PID_T AC_TYPE_PID_T
AC_TYPE_UID_T AC_TYPE_UID_T
# Checks for library functions. # Checks for library functions and compiler features.
# ----------------------------------------------------------------------
AC_FUNC_CLOSEDIR_VOID AC_FUNC_CLOSEDIR_VOID
AC_TYPE_SIGNAL AC_TYPE_SIGNAL
AC_FUNC_STAT AC_FUNC_STAT
@ -47,78 +91,164 @@ AC_CHECK_FUNCS([memmove strncasecmp strstr strdup])
save_cflags="${CFLAGS}" save_cflags="${CFLAGS}"
CFLAGS="${CFLAGS} -std=c99" CFLAGS="${CFLAGS} -std=c99"
AC_MSG_CHECKING([whether gcc -std=c99 option works]) AC_MSG_CHECKING([whether gcc -std=c99 option works])
AC_TRY_COMPILE(AC_INCLUDES_DEFAULT, [char *a; a = strdup("foo"); int i = 0; i++; // C99], AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
AC_MSG_RESULT([yes]), [AC_INCLUDES_DEFAULT], [[char *a; a = strdup("foo"); int i = 0; i++; // C99]])],
AC_MSG_ERROR([htop is written in C99. A newer version of gcc is required.])) [AC_MSG_RESULT([yes])],
[AC_MSG_ERROR([htop is written in C99. A newer version of gcc is required.])])
CFLAGS="$save_cflags" CFLAGS="$save_cflags"
save_cflags="${CFLAGS}"
CFLAGS="$CFLAGS -Wextra"
AC_MSG_CHECKING([if compiler supports -Wextra])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [])],[
wextra_flag=-Wextra
AC_MSG_RESULT([yes])
],[
wextra_flag=
AC_MSG_RESULT([no])
])
CFLAGS="$save_cflags"
AC_SUBST(wextra_flag)
# Checks for features and flags.
# ----------------------------------------------------------------------
PROCDIR=/proc PROCDIR=/proc
AC_ARG_WITH(proc, [ --with-proc=DIR Location of a Linux-compatible proc filesystem (default=/proc).],
if test -n "$withval"; then AC_ARG_ENABLE(proc, [AS_HELP_STRING([--enable-proc], [use Linux-compatible proc filesystem])], enable_proc="yes", enable_proc="no")
AC_DEFINE_UNQUOTED(PROCDIR, "$withval", [Path of proc filesystem]) if test "x$enable_proc" = xyes; then
PROCDIR="$withval" # An enabled proc assumes we're emulating Linux.
fi, my_htop_platform=linux
AC_DEFINE(PROCDIR, "/proc", [Path of proc filesystem])) AC_DEFINE(HAVE_PROC, 1, [Define if using a Linux-compatible proc filesystem.])
fi
AC_ARG_ENABLE(openvz, [AC_HELP_STRING([--enable-openvz], [enable OpenVZ support])], ,enable_openvz="no") AC_ARG_WITH(proc, [AS_HELP_STRING([--with-proc=DIR], [Location of a Linux-compatible proc filesystem (default=/proc).])],
if test -n "$withval"; then
AC_DEFINE_UNQUOTED(PROCDIR, "$withval", [Path of proc filesystem])
PROCDIR="$withval"
fi,
AC_DEFINE(PROCDIR, "/proc", [Path of proc filesystem]))
if test "x$cross_compiling" = xno; then
if test "x$enable_proc" = xyes; then
AC_CHECK_FILE($PROCDIR/stat,,AC_MSG_ERROR(Cannot find $PROCDIR/stat. Make sure you have a Linux-compatible /proc filesystem mounted. See the file README for help.))
AC_CHECK_FILE($PROCDIR/meminfo,,AC_MSG_ERROR(Cannot find $PROCDIR/meminfo. Make sure you have a Linux-compatible /proc filesystem mounted. See the file README for help.))
fi
fi
AC_ARG_ENABLE(openvz, [AS_HELP_STRING([--enable-openvz], [enable OpenVZ support])], ,enable_openvz="no")
if test "x$enable_openvz" = xyes; then if test "x$enable_openvz" = xyes; then
AC_DEFINE(HAVE_OPENVZ, 1, [Define if openvz support enabled.]) AC_DEFINE(HAVE_OPENVZ, 1, [Define if openvz support enabled.])
fi fi
AC_ARG_ENABLE(cgroup, [AC_HELP_STRING([--enable-cgroup], [enable cgroups support])], ,enable_cgroup="no") AC_ARG_ENABLE(cgroup, [AS_HELP_STRING([--enable-cgroup], [enable cgroups support])], ,enable_cgroup="no")
if test "x$enable_cgroup" = xyes; then if test "x$enable_cgroup" = xyes; then
AC_DEFINE(HAVE_CGROUP, 1, [Define if cgroup support enabled.]) AC_DEFINE(HAVE_CGROUP, 1, [Define if cgroup support enabled.])
fi fi
AC_ARG_ENABLE(vserver, [AC_HELP_STRING([--enable-vserver], [enable VServer support])], ,enable_vserver="no") AC_ARG_ENABLE(vserver, [AS_HELP_STRING([--enable-vserver], [enable VServer support])], ,enable_vserver="no")
if test "x$enable_vserver" = xyes; then if test "x$enable_vserver" = xyes; then
AC_DEFINE(HAVE_VSERVER, 1, [Define if vserver support enabled.]) AC_DEFINE(HAVE_VSERVER, 1, [Define if vserver support enabled.])
fi fi
AC_ARG_ENABLE(ancient_vserver, [AC_HELP_STRING([--enable-ancient-vserver], [enable ancient VServer support (implies --enable-vserver)])], ,enable_ancient_vserver="no") AC_ARG_ENABLE(ancient_vserver, [AS_HELP_STRING([--enable-ancient-vserver], [enable ancient VServer support (implies --enable-vserver)])], ,enable_ancient_vserver="no")
if test "x$enable_ancient_vserver" = xyes; then if test "x$enable_ancient_vserver" = xyes; then
AC_DEFINE(HAVE_VSERVER, 1, [Define if vserver support enabled.]) AC_DEFINE(HAVE_VSERVER, 1, [Define if vserver support enabled.])
AC_DEFINE(HAVE_ANCIENT_VSERVER, 1, [Define if ancient vserver support enabled.]) AC_DEFINE(HAVE_ANCIENT_VSERVER, 1, [Define if ancient vserver support enabled.])
fi fi
AC_ARG_ENABLE(taskstats, [AC_HELP_STRING([--enable-taskstats], [enable per-task IO Stats (taskstats kernel sup required)])], ,enable_taskstats="yes") AC_ARG_ENABLE(taskstats, [AS_HELP_STRING([--enable-taskstats], [enable per-task IO Stats (taskstats kernel sup required)])], ,enable_taskstats="yes")
if test "x$enable_taskstats" = xyes; then if test "x$enable_taskstats" = xyes; then
AC_DEFINE(HAVE_TASKSTATS, 1, [Define if taskstats support enabled.]) AC_DEFINE(HAVE_TASKSTATS, 1, [Define if taskstats support enabled.])
fi fi
AC_ARG_ENABLE(unicode, [AC_HELP_STRING([--enable-unicode], [enable Unicode support])], ,enable_unicode="yes") # HTOP_CHECK_SCRIPT(LIBNAME, FUNCTION, DEFINE, CONFIG_SCRIPT, ELSE_PART)
m4_define([HTOP_CHECK_SCRIPT],
[
if test ! -z "m4_toupper($HTOP_[$1]_CONFIG_SCRIPT)"; then
# to be used to set the path to ncurses*-config when cross-compiling
htop_config_script_libs=$(m4_toupper($HTOP_[$1]_CONFIG_SCRIPT) --libs 2> /dev/null)
htop_config_script_cflags=$(m4_toupper($HTOP_[$1]_CONFIG_SCRIPT) --cflags 2> /dev/null)
else
htop_config_script_libs=$([$4] --libs 2> /dev/null)
htop_config_script_cflags=$([$4] --cflags 2> /dev/null)
fi
htop_script_success=no
htop_save_LDFLAGS="$LDFLAGS"
htop_save_CFLAGS="$CFLAGS"
if test ! "x$htop_config_script_libs" = x; then
LDFLAGS="$htop_config_script_libs $LDFLAGS"
CFLAGS="$htop_config_script_cflags $CFLAGS"
AC_CHECK_LIB([$1], [$2], [
AC_DEFINE([$3], 1, [The library is present.])
LIBS="$htop_config_script_libs $LIBS "
htop_script_success=yes
], [
CFLAGS="$htop_save_CFLAGS"
])
LDFLAGS="$htop_save_LDFLAGS"
fi
if test "x$htop_script_success" = xno; then
[$5]
fi
])
# HTOP_CHECK_LIB(LIBNAME, FUNCTION, DEFINE, ELSE_PART)
m4_define([HTOP_CHECK_LIB],
[
AC_CHECK_LIB([$1], [$2], [
AC_DEFINE([$3], 1, [The library is present.])
LIBS="-l[$1] $LIBS "
], [$4])
])
AC_ARG_ENABLE(unicode, [AS_HELP_STRING([--enable-unicode], [enable Unicode support])], ,enable_unicode="yes")
if test "x$enable_unicode" = xyes; then if test "x$enable_unicode" = xyes; then
AC_CHECK_LIB([ncursesw], [refresh], [], [ HTOP_CHECK_SCRIPT([ncursesw6], [addnwstr], [HAVE_LIBNCURSESW], "ncursesw6-config",
missing_libraries="$missing_libraries libncursesw" HTOP_CHECK_SCRIPT([ncursesw], [addnwstr], [HAVE_LIBNCURSESW], "ncursesw6-config",
AC_MSG_ERROR([You may want to use --disable-unicode or install libncursesw.]) HTOP_CHECK_SCRIPT([ncursesw], [addnwstr], [HAVE_LIBNCURSESW], "ncursesw5-config",
]) HTOP_CHECK_SCRIPT([ncurses], [addnwstr], [HAVE_LIBNCURSESW], "ncurses5-config",
HTOP_CHECK_LIB([ncursesw6], [addnwstr], [HAVE_LIBNCURSESW],
HTOP_CHECK_LIB([ncursesw], [addnwstr], [HAVE_LIBNCURSESW],
HTOP_CHECK_LIB([ncurses], [addnwstr], [HAVE_LIBNCURSESW],
missing_libraries="$missing_libraries 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"])])])])
else else
AC_CHECK_LIB([ncurses], [refresh], [], [missing_libraries="$missing_libraries libncurses"]) HTOP_CHECK_SCRIPT([ncurses6], [refresh], [HAVE_LIBNCURSES], "ncurses6-config",
HTOP_CHECK_SCRIPT([ncurses], [refresh], [HAVE_LIBNCURSES], "ncurses5-config",
HTOP_CHECK_LIB([ncurses6], [refresh], [HAVE_LIBNCURSES],
HTOP_CHECK_LIB([ncurses], [refresh], [HAVE_LIBNCURSES],
missing_libraries="$missing_libraries libncurses"
))))
AC_CHECK_HEADERS([curses.h],[:], AC_CHECK_HEADERS([curses.h],[:],
[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"])])])])
fi fi
if test ! -z "$missing_libraries"; then if test "$my_htop_platform" = "freebsd"; then
AC_MSG_ERROR([missing libraries: $missing_libraries]) AC_CHECK_LIB([kvm], [kvm_open], [], [missing_libraries="$missing_libraries libkvm"])
fi
if test ! -z "$missing_headers"; then
AC_MSG_ERROR([missing headers: $missing_headers])
fi fi
if test "x$cross_compiling" = xno; then if test "$my_htop_platform" = "openbsd"; then
AC_CHECK_FILE($PROCDIR/stat,,AC_MSG_ERROR(Cannot find /proc/stat. Make sure you have a Linux-compatible /proc filesystem mounted. See the file README for help.)) AC_CHECK_LIB([kvm], [kvm_open], [], [missing_libraries="$missing_libraries libkvm"])
AC_CHECK_FILE($PROCDIR/meminfo,,AC_MSG_ERROR(Cannot find /proc/meminfo. Make sure you have a Linux-compatible /proc filesystem mounted. See the file README for help.))
fi fi
AC_ARG_ENABLE(native_affinity, [AC_HELP_STRING([--enable-native-affinity], [enable native sched_setaffinity and sched_getaffinity for affinity support, disables hwloc])], ,enable_native_affinity="yes") if test "$my_htop_platform" = "solaris"; then
if test "x$enable_native_affinity" = xyes -a "x$cross_compiling" = xno; then AC_CHECK_LIB([kstat], [kstat_open], [], [missing_libraries="$missing_libraries libkstat"])
AC_CHECK_LIB([proc], [Pgrab_error], [], [missing_libraries="$missing_libraries libproc"])
AC_CHECK_LIB([malloc], [free], [], [missing_libraries="$missing_libraries libmalloc"])
fi
AC_ARG_ENABLE(linux_affinity, [AS_HELP_STRING([--enable-linux-affinity], [enable Linux sched_setaffinity and sched_getaffinity for affinity support, disables hwloc])], ,enable_linux_affinity="yes")
if test "x$enable_linux_affinity" = xyes -a "x$cross_compiling" = xno; then
AC_MSG_CHECKING([for usable sched_setaffinity]) AC_MSG_CHECKING([for usable sched_setaffinity])
AC_RUN_IFELSE([ AC_RUN_IFELSE([
AC_LANG_PROGRAM([[ AC_LANG_PROGRAM([[
@ -131,24 +261,76 @@ if test "x$enable_native_affinity" = xyes -a "x$cross_compiling" = xno; then
if (errno == ENOSYS) return 1; if (errno == ENOSYS) return 1;
]])], ]])],
[AC_MSG_RESULT([yes])], [AC_MSG_RESULT([yes])],
[enable_native_affinity=no [enable_linux_affinity=no
AC_MSG_RESULT([no])]) AC_MSG_RESULT([no])])
fi fi
if test "x$enable_native_affinity" = xyes; then if test "x$enable_linux_affinity" = xyes; then
AC_DEFINE(HAVE_NATIVE_AFFINITY, 1, [Define if native 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
AC_ARG_ENABLE(hwloc, [AC_HELP_STRING([--enable-hwloc], [enable hwloc support for CPU affinity])],, enable_hwloc="no") AC_ARG_ENABLE(hwloc, [AS_HELP_STRING([--enable-hwloc], [enable hwloc support for CPU affinity])],, enable_hwloc="no")
if test "x$enable_hwloc" = xyes if test "x$enable_hwloc" = xyes
then then
AC_CHECK_LIB([hwloc], [hwloc_linux_get_tid_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
AC_ARG_ENABLE(oom, [AC_HELP_STRING([--enable-oom], [enable OOM score reporting])], ,enable_oom="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_oom" = xyes; then if test "x$enable_setuid" = xyes
AC_DEFINE(HAVE_OOM, 1, [Define if OOM score support enabled.]) then
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")
if test "x$enable_delayacct" = xyes
then
m4_ifdef([PKG_PROG_PKG_CONFIG], [
PKG_PROG_PKG_CONFIG()
PKG_CHECK_MODULES(LIBNL3, libnl-3.0, [], [missing_libraries="$missing_libraries libnl-3"])
PKG_CHECK_MODULES(LIBNL3GENL, libnl-genl-3.0, [], [missing_libraries="$missing_libraries libnl-genl-3"])
CFLAGS="$CFLAGS $LIBNL3_CFLAGS $LIBNL3GENL_CFLAGS"
LIBS="$LIBS $LIBNL3_LIBS $LIBNL3GENL_LIBS"
AC_DEFINE(HAVE_DELAYACCT, 1, [Define if delay accounting support should be enabled.])
], [
AC_MSG_ERROR([htop on Linux requires pkg-config for checking delayacct requirements. Please install pkg-config and run ./autogen.sh to rebuild the configure script.])
])
fi
# Bail out on errors.
# ----------------------------------------------------------------------
if test ! -z "$missing_libraries"; then
AC_MSG_ERROR([missing libraries: $missing_libraries])
fi
if test ! -z "$missing_headers"; then
AC_MSG_ERROR([missing headers: $missing_headers])
fi
AC_DEFINE_UNQUOTED(COPYRIGHT, "(C) 2004-$year Hisham Muhammad", [Copyright message.])
# We're done, let's go!
# ----------------------------------------------------------------------
AM_CONDITIONAL([HTOP_LINUX], [test "$my_htop_platform" = linux])
AM_CONDITIONAL([HTOP_FREEBSD], [test "$my_htop_platform" = freebsd])
AM_CONDITIONAL([HTOP_DRAGONFLYBSD], [test "$my_htop_platform" = dragonflybsd])
AM_CONDITIONAL([HTOP_OPENBSD], [test "$my_htop_platform" = openbsd])
AM_CONDITIONAL([HTOP_DARWIN], [test "$my_htop_platform" = darwin])
AM_CONDITIONAL([HTOP_SOLARIS], [test "$my_htop_platform" = solaris])
AM_CONDITIONAL([HTOP_UNSUPPORTED], [test "$my_htop_platform" = unsupported])
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"
then
echo ""
echo "****************************************************************"
echo "WARNING! This platform is not currently supported by htop."
echo ""
echo "The code will build, but it will produce a dummy version of htop"
echo "which shows no processes, using the files from the unsupported/"
echo "directory. This is meant to be a skeleton, to be used as a"
echo "starting point if you are porting htop to a new platform."
echo "****************************************************************"
echo ""
fi

75
darwin/Battery.c Normal file
View File

@ -0,0 +1,75 @@
#include "BatteryMeter.h"
#include <CoreFoundation/CoreFoundation.h>
#include <CoreFoundation/CFString.h>
#include <IOKit/ps/IOPowerSources.h>
#include <IOKit/ps/IOPSKeys.h>
void Battery_getData(double* level, ACPresence* isOnAC) {
CFTypeRef power_sources = IOPSCopyPowerSourcesInfo();
*level = -1;
*isOnAC = AC_ERROR;
if(NULL == power_sources) {
return;
}
if(power_sources != NULL) {
CFArrayRef list = IOPSCopyPowerSourcesList(power_sources);
CFDictionaryRef battery = NULL;
int len;
if(NULL == list) {
CFRelease(power_sources);
return;
}
len = CFArrayGetCount(list);
/* Get the battery */
for(int i = 0; i < len && battery == NULL; ++i) {
CFDictionaryRef candidate = IOPSGetPowerSourceDescription(power_sources,
CFArrayGetValueAtIndex(list, i)); /* GET rule */
CFStringRef type;
if(NULL != candidate) {
type = (CFStringRef) CFDictionaryGetValue(candidate,
CFSTR(kIOPSTransportTypeKey)); /* GET rule */
if(kCFCompareEqualTo == CFStringCompare(type, CFSTR(kIOPSInternalType), 0)) {
CFRetain(candidate);
battery = candidate;
}
}
}
if(NULL != battery) {
/* Determine the AC state */
CFStringRef power_state = CFDictionaryGetValue(battery, CFSTR(kIOPSPowerSourceStateKey));
*isOnAC = (kCFCompareEqualTo == CFStringCompare(power_state, CFSTR(kIOPSACPowerValue), 0))
? AC_PRESENT
: AC_ABSENT;
/* Get the percentage remaining */
double current;
double max;
CFNumberGetValue(CFDictionaryGetValue(battery, CFSTR(kIOPSCurrentCapacityKey)),
kCFNumberDoubleType, &current);
CFNumberGetValue(CFDictionaryGetValue(battery, CFSTR(kIOPSMaxCapacityKey)),
kCFNumberDoubleType, &max);
*level = (current * 100.0) / max;
CFRelease(battery);
}
CFRelease(list);
CFRelease(power_sources);
}
}

9
darwin/Battery.h Normal file
View File

@ -0,0 +1,9 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_Battery
#define HEADER_Battery
void Battery_getData(double* level, ACPresence* isOnAC);
#endif

35
darwin/DarwinCRT.c Normal file
View File

@ -0,0 +1,35 @@
/*
htop - DarwinCRT.c
(C) 2014 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "config.h"
#include "CRT.h"
#include <stdio.h>
#include <stdlib.h>
#include <execinfo.h>
void CRT_handleSIGSEGV(int sgn) {
(void) sgn;
CRT_done();
#ifdef __APPLE__
fprintf(stderr, "\n\nhtop " VERSION " aborting. Please report bug at http://hisham.hm/htop\n");
#ifdef HAVE_EXECINFO_H
size_t size = backtrace(backtraceArray, sizeof(backtraceArray) / sizeof(void *));
fprintf(stderr, "\n Please include in your report the following backtrace: \n");
backtrace_symbols_fd(backtraceArray, size, 2);
fprintf(stderr, "\nAdditionally, in order to make the above backtrace useful,");
fprintf(stderr, "\nplease also run the following command to generate a disassembly of your binary:");
fprintf(stderr, "\n\n otool -tvV `which htop` > ~/htop.otool");
fprintf(stderr, "\n\nand then attach the file ~/htop.otool to your bug report.");
fprintf(stderr, "\n\nThank you for helping to improve htop!\n\n");
#endif
#else
fprintf(stderr, "\nUnfortunately, you seem to be using an unsupported platform!");
fprintf(stderr, "\nPlease contact your platform package maintainer!\n\n");
#endif
abort();
}

15
darwin/DarwinCRT.h Normal file
View File

@ -0,0 +1,15 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_DarwinCRT
#define HEADER_DarwinCRT
/*
htop - DarwinCRT.h
(C) 2014 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
void CRT_handleSIGSEGV(int sgn);
#endif

363
darwin/DarwinProcess.c Normal file
View File

@ -0,0 +1,363 @@
/*
htop - DarwinProcess.c
(C) 2015 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "Process.h"
#include "DarwinProcess.h"
#include <stdlib.h>
#include <libproc.h>
#include <string.h>
#include <stdio.h>
#include <mach/mach.h>
/*{
#include "Settings.h"
#include "DarwinProcessList.h"
#include <sys/sysctl.h>
typedef struct DarwinProcess_ {
Process super;
uint64_t utime;
uint64_t stime;
bool taskAccess;
} DarwinProcess;
}*/
ProcessClass DarwinProcess_class = {
.super = {
.extends = Class(Process),
.display = Process_display,
.delete = Process_delete,
.compare = Process_compare
},
.writeField = Process_writeField,
};
DarwinProcess* DarwinProcess_new(Settings* settings) {
DarwinProcess* this = xCalloc(1, sizeof(DarwinProcess));
Object_setClass(this, Class(DarwinProcess));
Process_init(&this->super, settings);
this->utime = 0;
this->stime = 0;
this->taskAccess = true;
return this;
}
void Process_delete(Object* cast) {
DarwinProcess* this = (DarwinProcess*) cast;
Process_done(&this->super);
// free platform-specific fields here
free(this);
}
bool Process_isThread(Process* this) {
(void) this;
return false;
}
void DarwinProcess_setStartTime(Process *proc, struct extern_proc *ep, time_t now) {
struct tm date;
proc->starttime_ctime = ep->p_starttime.tv_sec;
(void) localtime_r(&proc->starttime_ctime, &date);
strftime(proc->starttime_show, 7, ((proc->starttime_ctime > now - 86400) ? "%R " : "%b%d "), &date);
}
char *DarwinProcess_getCmdLine(struct kinfo_proc* k, int* basenameOffset) {
/* This function is from the old Mac version of htop. Originally from ps? */
int mib[3], argmax, nargs, c = 0;
size_t size;
char *procargs, *sp, *np, *cp, *retval;
/* Get the maximum process arguments size. */
mib[0] = CTL_KERN;
mib[1] = KERN_ARGMAX;
size = sizeof( argmax );
if ( sysctl( mib, 2, &argmax, &size, NULL, 0 ) == -1 ) {
goto ERROR_A;
}
/* Allocate space for the arguments. */
procargs = ( char * ) xMalloc( argmax );
if ( procargs == NULL ) {
goto ERROR_A;
}
/*
* Make a sysctl() call to get the raw argument space of the process.
* The layout is documented in start.s, which is part of the Csu
* project. In summary, it looks like:
*
* /---------------\ 0x00000000
* : :
* : :
* |---------------|
* | argc |
* |---------------|
* | arg[0] |
* |---------------|
* : :
* : :
* |---------------|
* | arg[argc - 1] |
* |---------------|
* | 0 |
* |---------------|
* | env[0] |
* |---------------|
* : :
* : :
* |---------------|
* | env[n] |
* |---------------|
* | 0 |
* |---------------| <-- Beginning of data returned by sysctl() is here.
* | argc |
* |---------------|
* | exec_path |
* |:::::::::::::::|
* | |
* | String area. |
* | |
* |---------------| <-- Top of stack.
* : :
* : :
* \---------------/ 0xffffffff
*/
mib[0] = CTL_KERN;
mib[1] = KERN_PROCARGS2;
mib[2] = k->kp_proc.p_pid;
size = ( size_t ) argmax;
if ( sysctl( mib, 3, procargs, &size, NULL, 0 ) == -1 ) {
goto ERROR_B;
}
memcpy( &nargs, procargs, sizeof( nargs ) );
cp = procargs + sizeof( nargs );
/* Skip the saved exec_path. */
for ( ; cp < &procargs[size]; cp++ ) {
if ( *cp == '\0' ) {
/* End of exec_path reached. */
break;
}
}
if ( cp == &procargs[size] ) {
goto ERROR_B;
}
/* Skip trailing '\0' characters. */
for ( ; cp < &procargs[size]; cp++ ) {
if ( *cp != '\0' ) {
/* Beginning of first argument reached. */
break;
}
}
if ( cp == &procargs[size] ) {
goto ERROR_B;
}
/* Save where the argv[0] string starts. */
sp = cp;
*basenameOffset = 0;
for ( np = NULL; c < nargs && cp < &procargs[size]; cp++ ) {
if ( *cp == '\0' ) {
c++;
if ( np != NULL ) {
/* Convert previous '\0'. */
*np = ' ';
}
/* Note location of current '\0'. */
np = cp;
if (*basenameOffset == 0) {
*basenameOffset = cp - sp;
}
}
}
/*
* sp points to the beginning of the arguments/environment string, and
* np should point to the '\0' terminator for the string.
*/
if ( np == NULL || np == sp ) {
/* Empty or unterminated string. */
goto ERROR_B;
}
if (*basenameOffset == 0) {
*basenameOffset = np - sp;
}
/* Make a copy of the string. */
retval = xStrdup(sp);
/* Clean up. */
free( procargs );
return retval;
ERROR_B:
free( procargs );
ERROR_A:
retval = xStrdup(k->kp_proc.p_comm);
*basenameOffset = strlen(retval);
return retval;
}
void DarwinProcess_setFromKInfoProc(Process *proc, struct kinfo_proc *ps, time_t now, bool exists) {
struct extern_proc *ep = &ps->kp_proc;
/* UNSET HERE :
*
* processor
* user (set at ProcessList level)
* nlwp
* percent_cpu
* percent_mem
* m_size
* m_resident
* minflt
* majflt
*/
/* First, the "immutable" parts */
if(!exists) {
/* Set the PID/PGID/etc. */
proc->pid = ep->p_pid;
proc->ppid = ps->kp_eproc.e_ppid;
proc->pgrp = ps->kp_eproc.e_pgid;
proc->session = 0; /* TODO Get the session id */
proc->tpgid = ps->kp_eproc.e_tpgid;
proc->tgid = proc->pid;
proc->st_uid = ps->kp_eproc.e_ucred.cr_uid;
/* e_tdev = (major << 24) | (minor & 0xffffff) */
/* e_tdev == -1 for "no device" */
proc->tty_nr = ps->kp_eproc.e_tdev & 0xff; /* TODO tty_nr is unsigned */
DarwinProcess_setStartTime(proc, ep, now);
proc->comm = DarwinProcess_getCmdLine(ps, &(proc->basenameOffset));
}
/* Mutable information */
proc->nice = ep->p_nice;
proc->priority = ep->p_priority;
proc->state = (ep->p_stat == SZOMB) ? 'Z' : '?';
/* Make sure the updated flag is set */
proc->updated = true;
}
void DarwinProcess_setFromLibprocPidinfo(DarwinProcess *proc, DarwinProcessList *dpl) {
struct proc_taskinfo pti;
if(sizeof(pti) == proc_pidinfo(proc->super.pid, PROC_PIDTASKINFO, 0, &pti, sizeof(pti))) {
if(0 != proc->utime || 0 != proc->stime) {
uint64_t diff = (pti.pti_total_system - proc->stime)
+ (pti.pti_total_user - proc->utime);
proc->super.percent_cpu = (double)diff * (double)dpl->super.cpuCount
/ ((double)dpl->global_diff * 100000.0);
// fprintf(stderr, "%f %llu %llu %llu %llu %llu\n", proc->super.percent_cpu,
// proc->stime, proc->utime, pti.pti_total_system, pti.pti_total_user, dpl->global_diff);
// exit(7);
}
proc->super.time = (pti.pti_total_system + pti.pti_total_user) / 10000000;
proc->super.nlwp = pti.pti_threadnum;
proc->super.m_size = pti.pti_virtual_size / 1024 / PAGE_SIZE_KB;
proc->super.m_resident = pti.pti_resident_size / 1024 / PAGE_SIZE_KB;
proc->super.majflt = pti.pti_faults;
proc->super.percent_mem = (double)pti.pti_resident_size * 100.0
/ (double)dpl->host_info.max_mem;
proc->stime = pti.pti_total_system;
proc->utime = pti.pti_total_user;
dpl->super.kernelThreads += 0; /*pti.pti_threads_system;*/
dpl->super.userlandThreads += pti.pti_threadnum; /*pti.pti_threads_user;*/
dpl->super.totalTasks += pti.pti_threadnum;
dpl->super.runningTasks += pti.pti_numrunning;
}
}
/*
* Scan threads for process state information.
* Based on: http://stackoverflow.com/questions/6788274/ios-mac-cpu-usage-for-thread
* and https://github.com/max-horvath/htop-osx/blob/e86692e869e30b0bc7264b3675d2a4014866ef46/ProcessList.c
*/
void DarwinProcess_scanThreads(DarwinProcess *dp) {
Process* proc = (Process*) dp;
kern_return_t ret;
if (!dp->taskAccess) {
return;
}
if (proc->state == 'Z') {
return;
}
task_t port;
ret = task_for_pid(mach_task_self(), proc->pid, &port);
if (ret != KERN_SUCCESS) {
dp->taskAccess = false;
return;
}
task_info_data_t tinfo;
mach_msg_type_number_t task_info_count = TASK_INFO_MAX;
ret = task_info(port, TASK_BASIC_INFO, (task_info_t) tinfo, &task_info_count);
if (ret != KERN_SUCCESS) {
dp->taskAccess = false;
return;
}
thread_array_t thread_list;
mach_msg_type_number_t thread_count;
ret = task_threads(port, &thread_list, &thread_count);
if (ret != KERN_SUCCESS) {
dp->taskAccess = false;
mach_port_deallocate(mach_task_self(), port);
return;
}
integer_t run_state = 999;
for (unsigned int i = 0; i < thread_count; i++) {
thread_info_data_t thinfo;
mach_msg_type_number_t thread_info_count = THREAD_BASIC_INFO_COUNT;
ret = thread_info(thread_list[i], THREAD_BASIC_INFO, (thread_info_t)thinfo, &thread_info_count);
if (ret == KERN_SUCCESS) {
thread_basic_info_t basic_info_th = (thread_basic_info_t) thinfo;
if (basic_info_th->run_state < run_state) {
run_state = basic_info_th->run_state;
}
mach_port_deallocate(mach_task_self(), thread_list[i]);
}
}
vm_deallocate(mach_task_self(), (vm_address_t) thread_list, sizeof(thread_port_array_t) * thread_count);
mach_port_deallocate(mach_task_self(), port);
char state = '?';
switch (run_state) {
case TH_STATE_RUNNING: state = 'R'; break;
case TH_STATE_STOPPED: state = 'S'; break;
case TH_STATE_WAITING: state = 'W'; break;
case TH_STATE_UNINTERRUPTIBLE: state = 'U'; break;
case TH_STATE_HALTED: state = 'H'; break;
}
proc->state = state;
}

49
darwin/DarwinProcess.h Normal file
View File

@ -0,0 +1,49 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_DarwinProcess
#define HEADER_DarwinProcess
/*
htop - DarwinProcess.h
(C) 2015 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "Settings.h"
#include "DarwinProcessList.h"
#include <sys/sysctl.h>
typedef struct DarwinProcess_ {
Process super;
uint64_t utime;
uint64_t stime;
bool taskAccess;
} DarwinProcess;
extern ProcessClass DarwinProcess_class;
DarwinProcess* DarwinProcess_new(Settings* settings);
void Process_delete(Object* cast);
bool Process_isThread(Process* this);
void DarwinProcess_setStartTime(Process *proc, struct extern_proc *ep, time_t now);
char *DarwinProcess_getCmdLine(struct kinfo_proc* k, int* basenameOffset);
void DarwinProcess_setFromKInfoProc(Process *proc, struct kinfo_proc *ps, time_t now, bool exists);
void DarwinProcess_setFromLibprocPidinfo(DarwinProcess *proc, DarwinProcessList *dpl);
/*
* Scan threads for process state information.
* Based on: http://stackoverflow.com/questions/6788274/ios-mac-cpu-usage-for-thread
* and https://github.com/max-horvath/htop-osx/blob/e86692e869e30b0bc7264b3675d2a4014866ef46/ProcessList.c
*/
void DarwinProcess_scanThreads(DarwinProcess *dp);
#endif

224
darwin/DarwinProcessList.c Normal file
View File

@ -0,0 +1,224 @@
/*
htop - DarwinProcessList.c
(C) 2014 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "ProcessList.h"
#include "DarwinProcess.h"
#include "DarwinProcessList.h"
#include "CRT.h"
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <libproc.h>
#include <sys/mman.h>
#include <utmpx.h>
#include <err.h>
#include <sys/sysctl.h>
#include <stdbool.h>
struct kern {
short int version[3];
};
void GetKernelVersion(struct kern *k) {
static short int version_[3] = {0};
if (!version_[0]) {
// just in case it fails someday
version_[0] = version_[1] = version_[2] = -1;
char str[256] = {0};
size_t size = sizeof(str);
int ret = sysctlbyname("kern.osrelease", str, &size, NULL, 0);
if (ret == 0) sscanf(str, "%hd.%hd.%hd", &version_[0], &version_[1], &version_[2]);
}
memcpy(k->version, version_, sizeof(version_));
}
/* compare the given os version with the one installed returns:
0 if equals the installed version
positive value if less than the installed version
negative value if more than the installed version
*/
int CompareKernelVersion(short int major, short int minor, short int component) {
struct kern k;
GetKernelVersion(&k);
if ( k.version[0] != major) return k.version[0] - major;
if ( k.version[1] != minor) return k.version[1] - minor;
if ( k.version[2] != component) return k.version[2] - component;
return 0;
}
/*{
#include "ProcessList.h"
#include <mach/mach_host.h>
#include <sys/sysctl.h>
typedef struct DarwinProcessList_ {
ProcessList super;
host_basic_info_data_t host_info;
vm_statistics_data_t vm_stats;
processor_cpu_load_info_t prev_load;
processor_cpu_load_info_t curr_load;
uint64_t kernel_threads;
uint64_t user_threads;
uint64_t global_diff;
} DarwinProcessList;
}*/
void ProcessList_getHostInfo(host_basic_info_data_t *p) {
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)) {
CRT_fatalError("Unable to retrieve host info\n");
}
}
void ProcessList_freeCPULoadInfo(processor_cpu_load_info_t *p) {
if(NULL != p && NULL != *p) {
if(0 != munmap(*p, vm_page_size)) {
CRT_fatalError("Unable to free old CPU load information\n");
}
}
*p = NULL;
}
unsigned ProcessList_allocateCPULoadInfo(processor_cpu_load_info_t *p) {
mach_msg_type_number_t info_size = sizeof(processor_cpu_load_info_t);
unsigned cpu_count;
// 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)) {
CRT_fatalError("Unable to retrieve CPU info\n");
}
return cpu_count;
}
void ProcessList_getVMStats(vm_statistics_t p) {
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)
CRT_fatalError("Unable to retrieve VM statistics\n");
}
struct kinfo_proc *ProcessList_getKInfoProcs(size_t *count) {
int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0 };
struct kinfo_proc *processes = NULL;
/* Note the two calls to sysctl(). One to get length and one to get the
* data. This -does- mean that the second call could end up with a missing
* process entry or two.
*/
*count = 0;
if (sysctl(mib, 4, NULL, count, NULL, 0) < 0)
CRT_fatalError("Unable to get size of kproc_infos");
processes = xMalloc(*count);
if (processes == NULL)
CRT_fatalError("Out of memory for kproc_infos");
if (sysctl(mib, 4, processes, count, NULL, 0) < 0)
CRT_fatalError("Unable to get kinfo_procs");
*count = *count / sizeof(struct kinfo_proc);
return processes;
}
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId) {
DarwinProcessList* this = xCalloc(1, sizeof(DarwinProcessList));
ProcessList_init(&this->super, Class(Process), usersTable, pidWhiteList, userId);
/* Initialize the CPU information */
this->super.cpuCount = ProcessList_allocateCPULoadInfo(&this->prev_load);
ProcessList_getHostInfo(&this->host_info);
ProcessList_allocateCPULoadInfo(&this->curr_load);
/* Initialize the VM statistics */
ProcessList_getVMStats(&this->vm_stats);
this->super.kernelThreads = 0;
this->super.userlandThreads = 0;
this->super.totalTasks = 0;
this->super.runningTasks = 0;
return &this->super;
}
void ProcessList_delete(ProcessList* this) {
ProcessList_done(this);
free(this);
}
void ProcessList_goThroughEntries(ProcessList* super) {
DarwinProcessList *dpl = (DarwinProcessList *)super;
bool preExisting = true;
struct kinfo_proc *ps;
size_t count;
DarwinProcess *proc;
struct timeval tv;
gettimeofday(&tv, NULL); /* Start processing time */
/* Update the global data (CPU times and VM stats) */
ProcessList_freeCPULoadInfo(&dpl->prev_load);
dpl->prev_load = dpl->curr_load;
ProcessList_allocateCPULoadInfo(&dpl->curr_load);
ProcessList_getVMStats(&dpl->vm_stats);
/* Get the time difference */
dpl->global_diff = 0;
for(int i = 0; i < dpl->super.cpuCount; ++i) {
for(size_t j = 0; j < CPU_STATE_MAX; ++j) {
dpl->global_diff += dpl->curr_load[i].cpu_ticks[j] - dpl->prev_load[i].cpu_ticks[j];
}
}
/* Clear the thread counts */
super->kernelThreads = 0;
super->userlandThreads = 0;
super->totalTasks = 0;
super->runningTasks = 0;
/* We use kinfo_procs for initial data since :
*
* 1) They always succeed.
* 2) The contain the basic information.
*
* We attempt to fill-in additional information with libproc.
*/
ps = ProcessList_getKInfoProcs(&count);
for(size_t i = 0; i < count; ++i) {
proc = (DarwinProcess *)ProcessList_getProcess(super, ps[i].kp_proc.p_pid, &preExisting, (Process_New)DarwinProcess_new);
DarwinProcess_setFromKInfoProc(&proc->super, &ps[i], tv.tv_sec, preExisting);
DarwinProcess_setFromLibprocPidinfo(proc, dpl);
// Disabled for High Sierra due to bug in macOS High Sierra
bool isScanThreadSupported = ! ( CompareKernelVersion(17, 0, 0) >= 0 && CompareKernelVersion(17, 5, 0) < 0);
if (isScanThreadSupported){
DarwinProcess_scanThreads(proc);
}
super->totalTasks += 1;
if(!preExisting) {
proc->super.user = UsersTable_getRef(super->usersTable, proc->super.st_uid);
ProcessList_add(super, &proc->super);
}
}
free(ps);
}

View File

@ -0,0 +1,45 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_DarwinProcessList
#define HEADER_DarwinProcessList
/*
htop - DarwinProcessList.h
(C) 2014 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "ProcessList.h"
#include <mach/mach_host.h>
#include <sys/sysctl.h>
typedef struct DarwinProcessList_ {
ProcessList super;
host_basic_info_data_t host_info;
vm_statistics_data_t vm_stats;
processor_cpu_load_info_t prev_load;
processor_cpu_load_info_t curr_load;
uint64_t kernel_threads;
uint64_t user_threads;
uint64_t global_diff;
} DarwinProcessList;
void ProcessList_getHostInfo(host_basic_info_data_t *p);
void ProcessList_freeCPULoadInfo(processor_cpu_load_info_t *p);
unsigned ProcessList_allocateCPULoadInfo(processor_cpu_load_info_t *p);
void ProcessList_getVMStats(vm_statistics_t p);
struct kinfo_proc *ProcessList_getKInfoProcs(size_t *count);
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId);
void ProcessList_delete(ProcessList* this);
void ProcessList_goThroughEntries(ProcessList* super);
#endif

293
darwin/Platform.c Normal file
View File

@ -0,0 +1,293 @@
/*
htop - darwin/Platform.c
(C) 2014 Hisham H. Muhammad
(C) 2015 David C. Hunt
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "Platform.h"
#include "CPUMeter.h"
#include "MemoryMeter.h"
#include "SwapMeter.h"
#include "TasksMeter.h"
#include "LoadAverageMeter.h"
#include "ClockMeter.h"
#include "HostnameMeter.h"
#include "UptimeMeter.h"
#include "DarwinProcessList.h"
#include <stdlib.h>
/*{
#include "Action.h"
#include "SignalsPanel.h"
#include "CPUMeter.h"
#include "BatteryMeter.h"
#include "DarwinProcess.h"
}*/
#ifndef CLAMP
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif
ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_SIZE, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
const SignalItem Platform_signals[] = {
{ .name = " 0 Cancel", .number = 0 },
{ .name = " 1 SIGHUP", .number = 1 },
{ .name = " 2 SIGINT", .number = 2 },
{ .name = " 3 SIGQUIT", .number = 3 },
{ .name = " 4 SIGILL", .number = 4 },
{ .name = " 5 SIGTRAP", .number = 5 },
{ .name = " 6 SIGABRT", .number = 6 },
{ .name = " 6 SIGIOT", .number = 6 },
{ .name = " 7 SIGEMT", .number = 7 },
{ .name = " 8 SIGFPE", .number = 8 },
{ .name = " 9 SIGKILL", .number = 9 },
{ .name = "10 SIGBUS", .number = 10 },
{ .name = "11 SIGSEGV", .number = 11 },
{ .name = "12 SIGSYS", .number = 12 },
{ .name = "13 SIGPIPE", .number = 13 },
{ .name = "14 SIGALRM", .number = 14 },
{ .name = "15 SIGTERM", .number = 15 },
{ .name = "16 SIGURG", .number = 16 },
{ .name = "17 SIGSTOP", .number = 17 },
{ .name = "18 SIGTSTP", .number = 18 },
{ .name = "19 SIGCONT", .number = 19 },
{ .name = "20 SIGCHLD", .number = 20 },
{ .name = "21 SIGTTIN", .number = 21 },
{ .name = "22 SIGTTOU", .number = 22 },
{ .name = "23 SIGIO", .number = 23 },
{ .name = "24 SIGXCPU", .number = 24 },
{ .name = "25 SIGXFSZ", .number = 25 },
{ .name = "26 SIGVTALRM", .number = 26 },
{ .name = "27 SIGPROF", .number = 27 },
{ .name = "28 SIGWINCH", .number = 28 },
{ .name = "29 SIGINFO", .number = 29 },
{ .name = "30 SIGUSR1", .number = 30 },
{ .name = "31 SIGUSR2", .number = 31 },
};
const unsigned int Platform_numberOfSignals = sizeof(Platform_signals)/sizeof(SignalItem);
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_SIZE] = { .name = "M_SIZE", .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, },
};
MeterClass* Platform_meterTypes[] = {
&CPUMeter_class,
&ClockMeter_class,
&LoadAverageMeter_class,
&LoadMeter_class,
&MemoryMeter_class,
&SwapMeter_class,
&TasksMeter_class,
&BatteryMeter_class,
&HostnameMeter_class,
&UptimeMeter_class,
&AllCPUsMeter_class,
&AllCPUs2Meter_class,
&LeftCPUsMeter_class,
&RightCPUsMeter_class,
&LeftCPUs2Meter_class,
&RightCPUs2Meter_class,
&BlankMeter_class,
NULL
};
void Platform_setBindings(Htop_Action* keys) {
(void) keys;
}
int Platform_numberOfFields = 100;
int Platform_getUptime() {
struct timeval bootTime, currTime;
int mib[2] = { CTL_KERN, KERN_BOOTTIME };
size_t size = sizeof(bootTime);
int err = sysctl(mib, 2, &bootTime, &size, NULL, 0);
if (err) {
return -1;
}
gettimeofday(&currTime, NULL);
return (int) difftime(currTime.tv_sec, bootTime.tv_sec);
}
void Platform_getLoadAverage(double* one, double* five, double* fifteen) {
double results[3];
if(3 == getloadavg(results, 3)) {
*one = results[0];
*five = results[1];
*fifteen = results[2];
} else {
*one = 0;
*five = 0;
*fifteen = 0;
}
}
int Platform_getMaxPid() {
/* http://opensource.apple.com/source/xnu/xnu-2782.1.97/bsd/sys/proc_internal.hh */
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) {
DarwinProcessList *dpl = (DarwinProcessList *)mtr->pl;
int cpus = dpl->super.cpuCount;
double sumNice = 0.0;
double sumNormal = 0.0;
double sumKernel = 0.0;
double sumPercent = 0.0;
for (int i = 1; i <= cpus; i++) {
sumPercent += Platform_setCPUValues(mtr, i);
sumNice += mtr->values[CPU_METER_NICE];
sumNormal += mtr->values[CPU_METER_NORMAL];
sumKernel += mtr->values[CPU_METER_KERNEL];
}
mtr->values[CPU_METER_NICE] = sumNice / cpus;
mtr->values[CPU_METER_NORMAL] = sumNormal / cpus;
mtr->values[CPU_METER_KERNEL] = sumKernel / cpus;
return sumPercent / cpus;
}
double Platform_setCPUValues(Meter* mtr, int cpu) {
if (cpu == 0) {
return Platform_setCPUAverageValues(mtr);
}
DarwinProcessList *dpl = (DarwinProcessList *)mtr->pl;
processor_cpu_load_info_t prev = &dpl->prev_load[cpu-1];
processor_cpu_load_info_t curr = &dpl->curr_load[cpu-1];
double total = 0;
/* Take the sums */
for(size_t i = 0; i < CPU_STATE_MAX; ++i) {
total += (double)curr->cpu_ticks[i] - (double)prev->cpu_ticks[i];
}
mtr->values[CPU_METER_NICE]
= ((double)curr->cpu_ticks[CPU_STATE_NICE] - (double)prev->cpu_ticks[CPU_STATE_NICE])* 100.0 / total;
mtr->values[CPU_METER_NORMAL]
= ((double)curr->cpu_ticks[CPU_STATE_USER] - (double)prev->cpu_ticks[CPU_STATE_USER])* 100.0 / total;
mtr->values[CPU_METER_KERNEL]
= ((double)curr->cpu_ticks[CPU_STATE_SYSTEM] - (double)prev->cpu_ticks[CPU_STATE_SYSTEM])* 100.0 / total;
Meter_setItems(mtr, 3);
/* Convert to percent and return */
total = mtr->values[CPU_METER_NICE] + mtr->values[CPU_METER_NORMAL] + mtr->values[CPU_METER_KERNEL];
return CLAMP(total, 0.0, 100.0);
}
void Platform_setMemoryValues(Meter* mtr) {
DarwinProcessList *dpl = (DarwinProcessList *)mtr->pl;
vm_statistics_t vm = &dpl->vm_stats;
double page_K = (double)vm_page_size / (double)1024;
mtr->total = dpl->host_info.max_mem / 1024;
mtr->values[0] = (double)(vm->active_count + vm->wire_count) * page_K;
mtr->values[1] = (double)vm->purgeable_count * page_K;
mtr->values[2] = (double)vm->inactive_count * page_K;
}
void Platform_setSwapValues(Meter* mtr) {
int mib[2] = {CTL_VM, VM_SWAPUSAGE};
struct xsw_usage swapused;
size_t swlen = sizeof(swapused);
sysctl(mib, 2, &swapused, &swlen, NULL, 0);
mtr->total = swapused.xsu_total / 1024;
mtr->values[0] = swapused.xsu_used / 1024;
}
char* Platform_getProcessEnv(pid_t pid) {
char* env = NULL;
int argmax;
size_t bufsz = sizeof(argmax);
int mib[3];
mib[0] = CTL_KERN;
mib[1] = KERN_ARGMAX;
if (sysctl(mib, 2, &argmax, &bufsz, 0, 0) == 0) {
char* buf = xMalloc(argmax);
if (buf) {
mib[0] = CTL_KERN;
mib[1] = KERN_PROCARGS2;
mib[2] = pid;
size_t bufsz = argmax;
if (sysctl(mib, 3, buf, &bufsz, 0, 0) == 0) {
if (bufsz > sizeof(int)) {
char *p = buf, *endp = buf + bufsz;
int argc = *(int*)p;
p += sizeof(int);
// skip exe
p = strchr(p, 0)+1;
// skip padding
while(!*p && p < endp)
++p;
// skip argv
for (; argc-- && p < endp; p = strrchr(p, 0)+1)
;
// skip padding
while(!*p && p < endp)
++p;
size_t size = endp - p;
env = xMalloc(size+2);
memcpy(env, p, size);
env[size] = 0;
env[size+1] = 0;
}
}
free(buf);
}
}
return env;
}

53
darwin/Platform.h Normal file
View File

@ -0,0 +1,53 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_Platform
#define HEADER_Platform
/*
htop - darwin/Platform.h
(C) 2014 Hisham H. Muhammad
(C) 2015 David C. Hunt
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "Action.h"
#include "SignalsPanel.h"
#include "CPUMeter.h"
#include "BatteryMeter.h"
#include "DarwinProcess.h"
#ifndef CLAMP
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif
extern ProcessField Platform_defaultFields[];
extern const SignalItem Platform_signals[];
extern const unsigned int Platform_numberOfSignals;
extern ProcessFieldData Process_fields[];
extern MeterClass* Platform_meterTypes[];
void Platform_setBindings(Htop_Action* keys);
extern int Platform_numberOfFields;
int Platform_getUptime();
void Platform_getLoadAverage(double* one, double* five, double* fifteen);
int Platform_getMaxPid();
extern ProcessPidColumn Process_pidColumns[];
double Platform_setCPUValues(Meter* mtr, int cpu);
void Platform_setMemoryValues(Meter* mtr);
void Platform_setSwapValues(Meter* mtr);
char* Platform_getProcessEnv(pid_t pid);
#endif

26
dragonflybsd/Battery.c Normal file
View File

@ -0,0 +1,26 @@
/*
htop - dragonflybsd/Battery.c
(C) 2015 Hisham H. Muhammad
(C) 2017 Diederik de Groot
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "BatteryMeter.h"
#include <sys/sysctl.h>
void Battery_getData(double* level, ACPresence* isOnAC) {
int life;
size_t life_len = sizeof(life);
if (sysctlbyname("hw.acpi.battery.life", &life, &life_len, NULL, 0) == -1)
*level = -1;
else
*level = life;
int acline;
size_t acline_len = sizeof(acline);
if (sysctlbyname("hw.acpi.acline", &acline, &acline_len, NULL, 0) == -1)
*isOnAC = AC_ERROR;
else
*isOnAC = acline == 0 ? AC_ABSENT : AC_PRESENT;
}

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