mirror of
https://github.com/xzeldon/htop.git
synced 2025-07-14 21:14:35 +03:00
Compare commits
506 Commits
Author | SHA1 | Date | |
---|---|---|---|
6876a4b136 | |||
da7a369fa8 | |||
4ed3ab5c2c | |||
088dc5b9a7 | |||
16faf82739 | |||
df17374a92 | |||
59d0c5b26a | |||
fa48c484cc | |||
a5e2eff5e9 | |||
2bf626c4e4 | |||
fecf093367 | |||
c243db0b2c | |||
a18018ed48 | |||
db076b9c8e | |||
0679e9f45e | |||
7a4d6fa409 | |||
5b4d63d1be | |||
ec2307688e | |||
6d10736a64 | |||
711a7aacb0 | |||
a912512ac9 | |||
35d94a5ae5 | |||
ccb756d3c7 | |||
5dec9475bb | |||
c3746dc901 | |||
6e6334e603 | |||
9060a4179d | |||
7269faf651 | |||
7146059645 | |||
cf45a5d02b | |||
a905c45195 | |||
9df0f62859 | |||
68f2bfea61 | |||
b9e69223d0 | |||
edc3de7cb5 | |||
a9ddaccc63 | |||
a0c244a163 | |||
f886759022 | |||
b965417bf7 | |||
3f727d4720 | |||
d5ff5c48a8 | |||
c7f634ec21 | |||
c401ac3a98 | |||
fefff80631 | |||
edafa26f9e | |||
68460b25e3 | |||
b42c441ee0 | |||
68123adb6f | |||
ce27f8379d | |||
2d1b6f4783 | |||
fc2377f052 | |||
4b59a2e6b7 | |||
dd91e9a9da | |||
9a07ba2700 | |||
5b5836a2b1 | |||
f839095e3b | |||
6f2021f3d9 | |||
6974ce8e79 | |||
796bc36fe0 | |||
ba5ef1ac8b | |||
671282d309 | |||
a8b8f5f836 | |||
51669ecba8 | |||
02cfd38671 | |||
6d3b4a0f2e | |||
c31fd3c691 | |||
324f9d048d | |||
5b8654d341 | |||
eb4ff3c69c | |||
1bd95983b2 | |||
ee831263c3 | |||
aa0424ade8 | |||
10e9ffd8e5 | |||
97a859c5bd | |||
c85aafa608 | |||
93ca5af953 | |||
fdcdc54ec4 | |||
2e3f34f5c1 | |||
04da92dfd1 | |||
c1c4b5a1ab | |||
ed82ce6456 | |||
e341217fea | |||
44e01dd32b | |||
03705a20aa | |||
19ad28a560 | |||
97d9b320ad | |||
4f3ba680fb | |||
3fced48eea | |||
a4b650fdec | |||
72cea2881c | |||
b4884373e5 | |||
370f89c086 | |||
32faba0b6d | |||
82aa956940 | |||
2fe4a6351e | |||
90b209ee37 | |||
edf236f9fc | |||
f608fc5c8a | |||
90cc16efc0 | |||
f47e88f5e8 | |||
b148a4bed2 | |||
1fb0c720fe | |||
adcedf87f5 | |||
3451b6c6b8 | |||
af0b67ccd2 | |||
fbe3a2155f | |||
11d2206f40 | |||
41af31be7f | |||
7bfd62b8e4 | |||
c9abd788b1 | |||
0b787ca0b8 | |||
e8f27ebc26 | |||
2ab8fb83ba | |||
d45b4f4a43 | |||
df435931b6 | |||
279140db21 | |||
976c6123f4 | |||
68edf92434 | |||
0d85af2872 | |||
458749df45 | |||
e7f8d7bcc9 | |||
3bc73aa088 | |||
f21f81b2de | |||
3853978538 | |||
2b69f44a9d | |||
0daefbe4b4 | |||
9cbee01877 | |||
bf853addc3 | |||
a476490282 | |||
bf22a8fb13 | |||
09c7e3e136 | |||
4865e643ad | |||
67ca214cbe | |||
9bba1c6cf7 | |||
e7aaf79166 | |||
9f667f2c74 | |||
01f5b89278 | |||
149774209b | |||
15a71f32fe | |||
93be3211ae | |||
f0ed0fdafb | |||
865b85eb2d | |||
a0f758009b | |||
44d1200ca4 | |||
3da142b4b6 | |||
a60ff33e52 | |||
364e4e692f | |||
32414dace7 | |||
18e3fd5ce7 | |||
874fb773a7 | |||
ecb6a8da78 | |||
3bed682b1e | |||
686309e34c | |||
5fe9bcb21c | |||
336acb0309 | |||
612462e33d | |||
58a895e54c | |||
9de463e756 | |||
fa65c30976 | |||
3770769ed1 | |||
2f5b3ef733 | |||
e42ae55d69 | |||
497f468ed0 | |||
9b6cecfede | |||
3414d3b2d4 | |||
9e3b7c439c | |||
ddcfb179b4 | |||
b900e70e80 | |||
440bb87058 | |||
db98623684 | |||
4b49de44a8 | |||
30dc4a2812 | |||
07170aee4c | |||
4dce2db832 | |||
1c0bd5155f | |||
d2a476cddb | |||
6a6b09b431 | |||
8aca6fbfbd | |||
ad1ca7ee57 | |||
78793c5584 | |||
92324d3461 | |||
e3d0fc1a5a | |||
6f9b161b24 | |||
0bd1025e94 | |||
df752dd189 | |||
45ab05c56a | |||
7a8a6dd828 | |||
de1d06300d | |||
9114cf6ea3 | |||
faabbaa71e | |||
8154125d4b | |||
94d37989b4 | |||
144fd0a8d7 | |||
4bcb5d116b | |||
4d7cee56f0 | |||
9ce9557e69 | |||
b232119e4b | |||
da454997bf | |||
5abd7f2198 | |||
d4a2587568 | |||
5dfb524237 | |||
b424a5b137 | |||
d3af4e670d | |||
956b2ae70c | |||
c6f20fbcc6 | |||
0e7ae9a592 | |||
407d32e121 | |||
e1d1a5cec6 | |||
6bb59f8881 | |||
5ef8706d72 | |||
c14a45ba35 | |||
d075d49a0c | |||
f171e360e0 | |||
c752c542fe | |||
8420df62eb | |||
5e92956abc | |||
90f42695d2 | |||
c2e2556403 | |||
06073699ba | |||
b6ff5c8a2e | |||
c408add108 | |||
550a141860 | |||
3d5b6d9282 | |||
771a1be316 | |||
51ecc62d92 | |||
bf07c713ba | |||
d9feff150c | |||
72724d42f3 | |||
d445676f09 | |||
4da618030c | |||
8ff4eb72ac | |||
7892ac68fb | |||
6ad4f345dc | |||
05fb681d5c | |||
7c654559c9 | |||
7ef58f2dcf | |||
2824e2989a | |||
9a78155e17 | |||
aa8552ba88 | |||
a61a2e6d47 | |||
bcb18ef822 | |||
c0d0202440 | |||
7224d0e083 | |||
1a1fddae85 | |||
cdb660adab | |||
94a52cb5c9 | |||
666f70c58c | |||
6dc485dd20 | |||
a685661866 | |||
93a44acf7e | |||
b839987df7 | |||
d74e8b7554 | |||
10790f0a54 | |||
02431c43e1 | |||
fbec3e4005 | |||
07a4657a47 | |||
2c8353e7cf | |||
ee9e7edbc1 | |||
a62987c787 | |||
013d2efa51 | |||
27be880d0f | |||
6b57898034 | |||
906dcf5cb3 | |||
8f34225a49 | |||
fdda291a0e | |||
4676e35f42 | |||
69cfaf2381 | |||
d2ee40597c | |||
1f5f40c091 | |||
204bc710ba | |||
40ecde9d88 | |||
3f86a011e6 | |||
1b74dfe187 | |||
d9c95369bc | |||
d918cd9f2a | |||
54d7c6a080 | |||
90ae730fd4 | |||
323d7e73aa | |||
b41e4d9c54 | |||
6bbb454881 | |||
a2be57d768 | |||
436808ff99 | |||
099dab88be | |||
2d7069feb4 | |||
3db3737d75 | |||
a75b99a15e | |||
615fc934ff | |||
bd689ab0d3 | |||
d58c2f0606 | |||
5dbca0193d | |||
a05e78f531 | |||
ace5730f89 | |||
feec16cbb5 | |||
d63394b5f6 | |||
99cde7edec | |||
9a8221568a | |||
36880cd61c | |||
812cfcb94d | |||
74d061700c | |||
f3d9ecaa62 | |||
0006cc51b7 | |||
367561175a | |||
f3a37f9ef3 | |||
356488aa53 | |||
421bdeec60 | |||
f16aa483dd | |||
6c66f32fa7 | |||
75fd9edf75 | |||
8163b8164f | |||
5afb57b49e | |||
73f5ecf528 | |||
272e72680b | |||
36389fb0da | |||
5ef3c26168 | |||
3e8da0fcb6 | |||
a19b176099 | |||
9c437ceb0c | |||
7b293dc3e2 | |||
6fd5b05151 | |||
253ff23f9e | |||
d56d23d91a | |||
0ada9f325f | |||
57e0ce7b4f | |||
1cb3aee07a | |||
6ea93fc6c0 | |||
63019065dc | |||
e4e3f6c390 | |||
58ad020aca | |||
a11d01568c | |||
70fecb4984 | |||
f46fcf094e | |||
53bcc5cbff | |||
db042f259b | |||
9a893b9a07 | |||
67b815a817 | |||
ee97916fd5 | |||
1ba3915f73 | |||
16243a4a7e | |||
e942736267 | |||
9f41dc3332 | |||
1e806f9899 | |||
ac27df373a | |||
d9f2eacbc5 | |||
a4173f5209 | |||
1275139795 | |||
23797e730e | |||
0cfc9b0980 | |||
521f1343e3 | |||
350b48e44c | |||
c38819a675 | |||
d37d66bb3a | |||
3f99c2de24 | |||
bea7f8e7af | |||
9adcd9051a | |||
8ba4ef327e | |||
31e59cc60d | |||
b862e36ee7 | |||
2d1042adb3 | |||
23c5b9ce3c | |||
c5770c26af | |||
8c421d527b | |||
adaf748ab6 | |||
61ef1134d9 | |||
5b50ae3aa3 | |||
59a150e8d7 | |||
2328e52403 | |||
0bdceb858d | |||
4f9cf1490f | |||
635d4cfe60 | |||
ff4ee2eafc | |||
13b28fa9ed | |||
979aca98cc | |||
df818b9904 | |||
a40347e85b | |||
dc8124e1a1 | |||
29570c0133 | |||
3fe297aa97 | |||
88a11859a0 | |||
b4736228dc | |||
8a1112141d | |||
7b48fec59a | |||
00339087b0 | |||
2d1839289e | |||
379421d3b2 | |||
bb9a60ee8a | |||
07a6efcb22 | |||
76350c0350 | |||
12c2337939 | |||
067cd6deb8 | |||
82157f598e | |||
a73064dda9 | |||
b1befa3287 | |||
e0dec39203 | |||
84e5682473 | |||
f42090fcfd | |||
a89521ed7f | |||
135efd5705 | |||
525d37d6a4 | |||
d8d83031d9 | |||
1e57cab605 | |||
b0fd44275d | |||
61c943555b | |||
7433bf4b18 | |||
8cd90f0c4a | |||
2c6222e30a | |||
c44b2ec795 | |||
1e39c8fa4d | |||
4cb2b5fc1c | |||
f73c98abd4 | |||
f273bfd083 | |||
7ba3396a4c | |||
69d3b9ccf1 | |||
e86acd6893 | |||
3d497a3760 | |||
0d67263b36 | |||
7b1fa1bf49 | |||
fd2a0cf421 | |||
64a1ab848f | |||
cae47bb28d | |||
fd4e6b432b | |||
fd1ad863dc | |||
85a855f5b2 | |||
8fb51627b2 | |||
b612256486 | |||
ba630e8ad5 | |||
ef87877826 | |||
f3eab4e796 | |||
de3e271206 | |||
12208af777 | |||
12f5f06e88 | |||
3808b3b553 | |||
06b5828dc4 | |||
8bd543562b | |||
06b1674aa6 | |||
51e79ddc07 | |||
7bfa466abe | |||
1014e897a7 | |||
69efa94f9f | |||
04cf590967 | |||
46370231e3 | |||
8c43856380 | |||
79620d01c0 | |||
399add39ca | |||
56c4055fd1 | |||
fa499fc155 | |||
fdaa15bd8d | |||
fee744abd2 | |||
d5de1bc23d | |||
a3c8285237 | |||
03d6345c89 | |||
93378b9ee5 | |||
3acf28c259 | |||
bd694c0ce6 | |||
fd8c0611af | |||
f27bab470b | |||
34da6fdadb | |||
e54a790b14 | |||
3c61813ea6 | |||
fd45845829 | |||
92fb69f5a0 | |||
5644d0194b | |||
6dba60f6bd | |||
211121f060 | |||
d77703b3dc | |||
3035e29e74 | |||
575edffb4b | |||
759a34039c | |||
38b6a0148f | |||
f3623b7880 | |||
5e103ff9d1 | |||
0f04714a03 | |||
2ec44098f9 | |||
6f6e0ec571 | |||
d269d7247f | |||
041feeca18 | |||
074703bd5c | |||
4dadbe3b34 | |||
4531b31d92 | |||
b20bb543ce | |||
03824da684 | |||
4d85848988 | |||
71f51a20c1 | |||
1f20c0fb3d | |||
b5a5e83470 | |||
b9adc30b86 | |||
08ac22ddb9 | |||
ceee96dcba | |||
5fde0e0127 | |||
78b993dbb4 | |||
47cebafd77 | |||
c865313e2d | |||
66dd77aa6b | |||
2b62126aea | |||
960f52b783 | |||
e6d536dd3f | |||
37e186fd66 | |||
f4404effa4 | |||
d72b0a682e | |||
1b2d48bc9a | |||
d9240999e9 | |||
70f48f1f44 | |||
958112c5a3 | |||
a118928dee | |||
3715301fe3 | |||
d53398fb48 | |||
58ce887d14 |
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1 @@
|
||||
open_collective: htop
|
93
.github/workflows/ci.yml
vendored
93
.github/workflows/ci.yml
vendored
@ -13,11 +13,11 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Dependencies
|
||||
run: sudo apt-get install libncursesw5-dev
|
||||
run: sudo apt-get install --no-install-recommends libncursesw5-dev
|
||||
- name: Bootstrap
|
||||
run: ./autogen.sh
|
||||
- name: Configure
|
||||
run: ./configure --enable-werror --enable-linux-affinity --disable-unicode --without-sensors
|
||||
run: ./configure --enable-werror --enable-affinity --disable-unicode --disable-sensors
|
||||
- name: Enable compatibility modes
|
||||
run: |
|
||||
sed -i 's/#define HAVE_FSTATAT 1/#undef HAVE_FSTATAT/g' config.h
|
||||
@ -26,31 +26,72 @@ jobs:
|
||||
- name: Build
|
||||
run: make -k
|
||||
- name: Distcheck
|
||||
run: make distcheck DISTCHECK_CONFIGURE_FLAGS="--enable-werror --enable-linux-affinity --disable-unicode --without-sensors"
|
||||
run: make distcheck DISTCHECK_CONFIGURE_FLAGS="--enable-werror --enable-affinity --disable-unicode --disable-sensors"
|
||||
|
||||
build-ubuntu-latest-minimal-clang:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CC: clang-11
|
||||
CC: clang-12
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: install clang repo
|
||||
run: |
|
||||
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key 2>/dev/null | sudo apt-key add -
|
||||
sudo add-apt-repository 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-11 main' -y
|
||||
sudo add-apt-repository 'deb http://apt.llvm.org/focal/ llvm-toolchain-focal-12 main' -y
|
||||
sudo apt-get update -q
|
||||
- name: Install Dependencies
|
||||
run: sudo apt-get install clang-11 libncursesw5-dev
|
||||
run: sudo apt-get install --no-install-recommends clang-12 libncursesw5-dev
|
||||
- name: Bootstrap
|
||||
run: ./autogen.sh
|
||||
- name: Configure
|
||||
run: ./configure --enable-werror --enable-linux-affinity --disable-unicode --without-sensors
|
||||
run: ./configure --enable-werror --enable-affinity --disable-unicode --disable-sensors
|
||||
- name: Build
|
||||
run: make -k
|
||||
- name: Distcheck
|
||||
run: make distcheck DISTCHECK_CONFIGURE_FLAGS="--enable-werror --enable-linux-affinity --disable-unicode --without-sensors"
|
||||
run: make distcheck DISTCHECK_CONFIGURE_FLAGS="--enable-werror --enable-affinity --disable-unicode --disable-sensors"
|
||||
|
||||
build-ubuntu-latest-full-featured-gcc:
|
||||
runs-on: ubuntu-latest
|
||||
# Enable LTO, might trigger additional warnings on advanced inlining
|
||||
env:
|
||||
CFLAGS: -O3 -g -flto
|
||||
LDFLAGS: -O3 -g -flto -Wl,--as-needed
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Dependencies
|
||||
run: sudo apt-get install --no-install-recommends libncursesw5-dev libhwloc-dev libnl-3-dev libnl-genl-3-dev libsensors4-dev libcap-dev
|
||||
- name: Bootstrap
|
||||
run: ./autogen.sh
|
||||
- name: Configure
|
||||
run: ./configure --enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --enable-hwloc --enable-delayacct --enable-sensors --enable-capabilities
|
||||
- name: Build
|
||||
run: make -k
|
||||
- name: Distcheck
|
||||
run: make distcheck DISTCHECK_CONFIGURE_FLAGS='--enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --enable-hwloc --enable-delayacct --enable-sensors --enable-capabilities'
|
||||
|
||||
build-ubuntu-latest-full-featured-clang:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CC: clang-12
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: install clang repo
|
||||
run: |
|
||||
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key 2>/dev/null | sudo apt-key add -
|
||||
sudo add-apt-repository 'deb http://apt.llvm.org/focal/ llvm-toolchain-focal-12 main' -y
|
||||
sudo apt-get update -q
|
||||
- name: Install Dependencies
|
||||
run: sudo apt-get install --no-install-recommends clang-12 libncursesw5-dev libhwloc-dev libnl-3-dev libnl-genl-3-dev libsensors4-dev libcap-dev
|
||||
- name: Bootstrap
|
||||
run: ./autogen.sh
|
||||
- name: Configure
|
||||
run: ./configure --enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --enable-hwloc --enable-delayacct --enable-sensors --enable-capabilities
|
||||
- name: Build
|
||||
run: make -k
|
||||
- name: Distcheck
|
||||
run: make distcheck DISTCHECK_CONFIGURE_FLAGS='--enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --enable-hwloc --enable-delayacct --enable-sensors --enable-capabilities'
|
||||
|
||||
build-ubuntu-latest-gcc-static:
|
||||
runs-on: ubuntu-latest
|
||||
# Enable LTO, might trigger additional warnings on advanced inlining
|
||||
env:
|
||||
@ -59,57 +100,51 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Dependencies
|
||||
run: sudo apt-get install libncursesw5-dev libhwloc-dev libnl-3-dev libnl-genl-3-dev libsensors4-dev
|
||||
run: sudo apt-get install --no-install-recommends libncursesw5-dev libtinfo-dev libgpm-dev libsensors4-dev libcap-dev
|
||||
- name: Bootstrap
|
||||
run: ./autogen.sh
|
||||
- name: Configure
|
||||
run: ./configure --enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --enable-hwloc --enable-setuid --enable-delayacct --with-sensors
|
||||
run: ./configure --enable-static --enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --disable-hwloc --disable-delayacct --enable-sensors --enable-capabilities
|
||||
- name: Build
|
||||
run: make -k
|
||||
- name: Distcheck
|
||||
run: make distcheck DISTCHECK_CONFIGURE_FLAGS='--enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --enable-hwloc --enable-setuid --enable-delayacct --with-sensors'
|
||||
run: make distcheck DISTCHECK_CONFIGURE_FLAGS='--enable-static --enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --disable-hwloc --disable-delayacct --enable-sensors --enable-capabilities'
|
||||
|
||||
build-ubuntu-latest-full-featured-clang:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CC: clang-11
|
||||
build-ubuntu-latest-pcp:
|
||||
# Turns out 'ubuntu-latest' can be older than 20.04, we want PCP v5+
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: install clang repo
|
||||
run: |
|
||||
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key 2>/dev/null | sudo apt-key add -
|
||||
sudo add-apt-repository 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-11 main' -y
|
||||
sudo apt-get update -q
|
||||
- name: Install Dependencies
|
||||
run: sudo apt-get install clang-11 libncursesw5-dev libhwloc-dev libnl-3-dev libnl-genl-3-dev libsensors4-dev
|
||||
run: sudo apt-get install --no-install-recommends libpcp3-dev libncursesw5-dev libtinfo-dev libgpm-dev
|
||||
- name: Bootstrap
|
||||
run: ./autogen.sh
|
||||
- name: Configure
|
||||
run: ./configure --enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --enable-hwloc --enable-setuid --enable-delayacct --with-sensors
|
||||
# Until Ubuntu catches up with pcp-5.2.3+, cannot use -werror due to:
|
||||
# passing argument 2 of ‘pmLookupName’ from incompatible pointer type
|
||||
run: ./configure --enable-pcp --enable-unicode
|
||||
- name: Build
|
||||
run: make -k
|
||||
- name: Distcheck
|
||||
run: make distcheck DISTCHECK_CONFIGURE_FLAGS='--enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --enable-hwloc --enable-setuid --enable-delayacct --with-sensors'
|
||||
|
||||
build-ubuntu-latest-clang-analyzer:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CC: clang-11
|
||||
CC: clang-12
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: install clang repo
|
||||
run: |
|
||||
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key 2>/dev/null | sudo apt-key add -
|
||||
sudo add-apt-repository 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-11 main' -y
|
||||
sudo add-apt-repository 'deb http://apt.llvm.org/focal/ llvm-toolchain-focal-12 main' -y
|
||||
sudo apt-get update -q
|
||||
- name: Install Dependencies
|
||||
run: sudo apt-get install clang-11 clang-tools-11 libncursesw5-dev libhwloc-dev libnl-3-dev libnl-genl-3-dev libsensors4-dev
|
||||
run: sudo apt-get install --no-install-recommends clang-12 clang-tools-12 libncursesw5-dev libnl-3-dev libnl-genl-3-dev libsensors4-dev libcap-dev
|
||||
- name: Bootstrap
|
||||
run: ./autogen.sh
|
||||
- name: Configure
|
||||
run: scan-build-11 -analyze-headers --status-bugs ./configure --enable-debug --enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --enable-hwloc --enable-setuid --enable-delayacct --with-sensors
|
||||
run: scan-build-12 -analyze-headers --status-bugs ./configure --enable-debug --enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --enable-delayacct --enable-sensors --enable-capabilities
|
||||
- name: Build
|
||||
run: scan-build-11 -analyze-headers --status-bugs make -j"$(nproc)"
|
||||
run: scan-build-12 -analyze-headers --status-bugs make -j"$(nproc)"
|
||||
|
||||
build-macos-latest-clang:
|
||||
runs-on: macOS-latest
|
||||
|
8
.gitignore
vendored
8
.gitignore
vendored
@ -1,5 +1,6 @@
|
||||
# the binary:
|
||||
# the binaries:
|
||||
htop
|
||||
pcp-htop
|
||||
|
||||
# all object files
|
||||
*.o
|
||||
@ -36,6 +37,7 @@ config.sub
|
||||
configure
|
||||
depcomp
|
||||
htop.1
|
||||
pcp-htop.5
|
||||
install-sh
|
||||
libtool
|
||||
ltmain.sh
|
||||
@ -45,3 +47,7 @@ stamp-h1
|
||||
|
||||
# files related to valgrind/callgrind
|
||||
callgrind.out.*
|
||||
|
||||
# IDE workspace configurations
|
||||
/.idea/
|
||||
/.vscode/
|
||||
|
275
Action.c
275
Action.c
@ -13,9 +13,10 @@ in the source distribution for its full text.
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "CRT.h"
|
||||
#include "CategoriesPanel.h"
|
||||
#include "CommandScreen.h"
|
||||
#include "CRT.h"
|
||||
#include "DynamicColumn.h"
|
||||
#include "EnvScreen.h"
|
||||
#include "FunctionBar.h"
|
||||
#include "Hashtable.h"
|
||||
@ -34,26 +35,25 @@ in the source distribution for its full text.
|
||||
#include "Vector.h"
|
||||
#include "XUtils.h"
|
||||
|
||||
#if (defined(HAVE_LIBHWLOC) || defined(HAVE_LINUX_AFFINITY))
|
||||
#if (defined(HAVE_LIBHWLOC) || defined(HAVE_AFFINITY))
|
||||
#include "Affinity.h"
|
||||
#include "AffinityPanel.h"
|
||||
#endif
|
||||
|
||||
|
||||
Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess) {
|
||||
Panel* panel = st->panel;
|
||||
MainPanel* mainPanel = st->mainPanel;
|
||||
Header* header = st->header;
|
||||
Settings* settings = st->settings;
|
||||
|
||||
int y = panel->y;
|
||||
ScreenManager* scr = ScreenManager_new(header, settings, st, false);
|
||||
int y = ((Panel*)mainPanel)->y;
|
||||
ScreenManager* scr = ScreenManager_new(header, st->settings, st, false);
|
||||
scr->allowFocusChange = false;
|
||||
ScreenManager_add(scr, list, x - 1);
|
||||
ScreenManager_add(scr, panel, -1);
|
||||
ScreenManager_add(scr, list, x);
|
||||
ScreenManager_add(scr, (Panel*)mainPanel, -1);
|
||||
Panel* panelFocus;
|
||||
int ch;
|
||||
bool unfollow = false;
|
||||
int pid = followProcess ? MainPanel_selectedPid((MainPanel*)panel) : -1;
|
||||
int pid = followProcess ? MainPanel_selectedPid(mainPanel) : -1;
|
||||
if (followProcess && header->pl->following == -1) {
|
||||
header->pl->following = pid;
|
||||
unfollow = true;
|
||||
@ -63,11 +63,11 @@ Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess)
|
||||
header->pl->following = -1;
|
||||
}
|
||||
ScreenManager_delete(scr);
|
||||
Panel_move(panel, 0, y);
|
||||
Panel_resize(panel, COLS, LINES - y - 1);
|
||||
Panel_move((Panel*)mainPanel, 0, y);
|
||||
Panel_resize((Panel*)mainPanel, COLS, LINES - y - 1);
|
||||
if (panelFocus == list && ch == 13) {
|
||||
if (followProcess) {
|
||||
Process* selected = (Process*)Panel_getSelected(panel);
|
||||
const Process* selected = (const Process*)Panel_getSelected((Panel*)mainPanel);
|
||||
if (selected && selected->pid == pid)
|
||||
return Panel_getSelected(list);
|
||||
|
||||
@ -84,12 +84,8 @@ Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess)
|
||||
|
||||
static void Action_runSetup(State* st) {
|
||||
ScreenManager* scr = ScreenManager_new(st->header, st->settings, st, true);
|
||||
CategoriesPanel* panelCategories = CategoriesPanel_new(scr, st->settings, st->header, st->pl);
|
||||
ScreenManager_add(scr, (Panel*) panelCategories, 16);
|
||||
CategoriesPanel_makeMetersPage(panelCategories);
|
||||
Panel* panelFocus;
|
||||
int ch;
|
||||
ScreenManager_run(scr, &panelFocus, &ch);
|
||||
CategoriesPanel_new(scr, st->settings, st->header, st->pl);
|
||||
ScreenManager_run(scr, NULL, NULL);
|
||||
ScreenManager_delete(scr);
|
||||
if (st->settings->changed) {
|
||||
Header_writeBackToSettings(st->header);
|
||||
@ -141,7 +137,7 @@ static bool expandCollapse(Panel* panel) {
|
||||
}
|
||||
|
||||
static bool collapseIntoParent(Panel* panel) {
|
||||
Process* p = (Process*) Panel_getSelected(panel);
|
||||
const Process* p = (Process*) Panel_getSelected(panel);
|
||||
if (!p)
|
||||
return false;
|
||||
|
||||
@ -168,16 +164,25 @@ static Htop_Reaction actionSetSortColumn(State* st) {
|
||||
Htop_Reaction reaction = HTOP_OK;
|
||||
Panel* sortPanel = Panel_new(0, 0, 0, 0, Class(ListItem), true, FunctionBar_newEnterEsc("Sort ", "Cancel "));
|
||||
Panel_setHeader(sortPanel, "Sort by");
|
||||
ProcessField* fields = st->settings->fields;
|
||||
const ProcessField* fields = st->settings->fields;
|
||||
Hashtable* dynamicColumns = st->settings->dynamicColumns;
|
||||
for (int i = 0; fields[i]; i++) {
|
||||
char* name = String_trim(Process_fields[fields[i]].name);
|
||||
char* name = NULL;
|
||||
if (fields[i] >= LAST_PROCESSFIELD) {
|
||||
DynamicColumn* column = Hashtable_get(dynamicColumns, fields[i]);
|
||||
if (!column)
|
||||
continue;
|
||||
name = xStrdup(column->caption ? column->caption : column->name);
|
||||
} else {
|
||||
name = String_trim(Process_fields[fields[i]].name);
|
||||
}
|
||||
Panel_add(sortPanel, (Object*) ListItem_new(name, fields[i]));
|
||||
if (fields[i] == Settings_getActiveSortKey(st->settings))
|
||||
Panel_setSelected(sortPanel, i);
|
||||
|
||||
free(name);
|
||||
}
|
||||
ListItem* field = (ListItem*) Action_pickFromVector(st, sortPanel, 15, false);
|
||||
const ListItem* field = (const ListItem*) Action_pickFromVector(st, sortPanel, 14, false);
|
||||
if (field) {
|
||||
reaction |= Action_setSortKey(st->settings, field->key);
|
||||
}
|
||||
@ -207,12 +212,12 @@ static Htop_Reaction actionSortByTime(State* st) {
|
||||
|
||||
static Htop_Reaction actionToggleKernelThreads(State* st) {
|
||||
st->settings->hideKernelThreads = !st->settings->hideKernelThreads;
|
||||
return HTOP_RECALCULATE | HTOP_SAVE_SETTINGS;
|
||||
return HTOP_RECALCULATE | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING;
|
||||
}
|
||||
|
||||
static Htop_Reaction actionToggleUserlandThreads(State* st) {
|
||||
st->settings->hideUserlandThreads = !st->settings->hideUserlandThreads;
|
||||
return HTOP_RECALCULATE | HTOP_SAVE_SETTINGS;
|
||||
return HTOP_RECALCULATE | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING;
|
||||
}
|
||||
|
||||
static Htop_Reaction actionToggleProgramPath(State* st) {
|
||||
@ -227,34 +232,47 @@ static Htop_Reaction actionToggleMergedCommand(State* st) {
|
||||
|
||||
static Htop_Reaction actionToggleTreeView(State* st) {
|
||||
st->settings->treeView = !st->settings->treeView;
|
||||
if (st->settings->treeView) {
|
||||
st->settings->treeDirection = 1;
|
||||
}
|
||||
|
||||
ProcessList_expandTree(st->pl);
|
||||
if (!st->settings->allBranchesCollapsed)
|
||||
ProcessList_expandTree(st->pl);
|
||||
return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
|
||||
}
|
||||
|
||||
static Htop_Reaction actionExpandOrCollapseAllBranches(State* st) {
|
||||
st->settings->allBranchesCollapsed = !st->settings->allBranchesCollapsed;
|
||||
if (st->settings->allBranchesCollapsed)
|
||||
ProcessList_collapseAllBranches(st->pl);
|
||||
else
|
||||
ProcessList_expandTree(st->pl);
|
||||
return HTOP_REFRESH | HTOP_SAVE_SETTINGS;
|
||||
}
|
||||
|
||||
static Htop_Reaction actionIncFilter(State* st) {
|
||||
IncSet* inc = ((MainPanel*)st->panel)->inc;
|
||||
IncSet_activate(inc, INC_FILTER, st->panel);
|
||||
IncSet* inc = (st->mainPanel)->inc;
|
||||
IncSet_activate(inc, INC_FILTER, (Panel*)st->mainPanel);
|
||||
st->pl->incFilter = IncSet_filter(inc);
|
||||
return HTOP_REFRESH | HTOP_KEEP_FOLLOWING;
|
||||
}
|
||||
|
||||
static Htop_Reaction actionIncSearch(State* st) {
|
||||
IncSet_reset(((MainPanel*)st->panel)->inc, INC_SEARCH);
|
||||
IncSet_activate(((MainPanel*)st->panel)->inc, INC_SEARCH, st->panel);
|
||||
IncSet_reset(st->mainPanel->inc, INC_SEARCH);
|
||||
IncSet_activate(st->mainPanel->inc, INC_SEARCH, (Panel*)st->mainPanel);
|
||||
return HTOP_REFRESH | HTOP_KEEP_FOLLOWING;
|
||||
}
|
||||
|
||||
static Htop_Reaction actionHigherPriority(State* st) {
|
||||
bool changed = changePriority((MainPanel*)st->panel, -1);
|
||||
if (Settings_isReadonly())
|
||||
return HTOP_OK;
|
||||
|
||||
bool changed = changePriority(st->mainPanel, -1);
|
||||
return changed ? HTOP_REFRESH : HTOP_OK;
|
||||
}
|
||||
|
||||
static Htop_Reaction actionLowerPriority(State* st) {
|
||||
bool changed = changePriority((MainPanel*)st->panel, 1);
|
||||
if (Settings_isReadonly())
|
||||
return HTOP_OK;
|
||||
|
||||
bool changed = changePriority(st->mainPanel, 1);
|
||||
return changed ? HTOP_REFRESH : HTOP_OK;
|
||||
}
|
||||
|
||||
@ -262,11 +280,11 @@ static Htop_Reaction actionInvertSortOrder(State* st) {
|
||||
Settings_invertSortOrder(st->settings);
|
||||
if (st->pauseProcessUpdate)
|
||||
ProcessList_sort(st->pl);
|
||||
return HTOP_REFRESH | HTOP_SAVE_SETTINGS;
|
||||
return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING;
|
||||
}
|
||||
|
||||
static Htop_Reaction actionExpandOrCollapse(State* st) {
|
||||
bool changed = expandCollapse(st->panel);
|
||||
bool changed = expandCollapse((Panel*)st->mainPanel);
|
||||
return changed ? HTOP_RECALCULATE : HTOP_OK;
|
||||
}
|
||||
|
||||
@ -274,7 +292,7 @@ static Htop_Reaction actionCollapseIntoParent(State* st) {
|
||||
if (!st->settings->treeView) {
|
||||
return HTOP_OK;
|
||||
}
|
||||
bool changed = collapseIntoParent(st->panel);
|
||||
bool changed = collapseIntoParent((Panel*)st->mainPanel);
|
||||
return changed ? HTOP_RECALCULATE : HTOP_OK;
|
||||
}
|
||||
|
||||
@ -287,13 +305,14 @@ static Htop_Reaction actionQuit(ATTR_UNUSED State* st) {
|
||||
}
|
||||
|
||||
static Htop_Reaction actionSetAffinity(State* st) {
|
||||
if (st->pl->cpuCount == 1)
|
||||
if (Settings_isReadonly())
|
||||
return HTOP_OK;
|
||||
|
||||
#if (defined(HAVE_LIBHWLOC) || defined(HAVE_LINUX_AFFINITY))
|
||||
Panel* panel = st->panel;
|
||||
if (st->pl->activeCPUs == 1)
|
||||
return HTOP_OK;
|
||||
|
||||
Process* p = (Process*) Panel_getSelected(panel);
|
||||
#if (defined(HAVE_LIBHWLOC) || defined(HAVE_AFFINITY))
|
||||
const Process* p = (const Process*) Panel_getSelected((Panel*)st->mainPanel);
|
||||
if (!p)
|
||||
return HTOP_OK;
|
||||
|
||||
@ -303,33 +322,36 @@ static Htop_Reaction actionSetAffinity(State* st) {
|
||||
|
||||
int width;
|
||||
Panel* affinityPanel = AffinityPanel_new(st->pl, affinity1, &width);
|
||||
width += 1; /* we add a gap between the panels */
|
||||
Affinity_delete(affinity1);
|
||||
|
||||
void* set = Action_pickFromVector(st, affinityPanel, width, true);
|
||||
const void* set = Action_pickFromVector(st, affinityPanel, width, true);
|
||||
if (set) {
|
||||
Affinity* affinity2 = AffinityPanel_getAffinity(affinityPanel, st->pl);
|
||||
bool ok = MainPanel_foreachProcess((MainPanel*)panel, Affinity_set, (Arg) { .v = affinity2 }, NULL);
|
||||
bool ok = MainPanel_foreachProcess(st->mainPanel, Affinity_set, (Arg) { .v = affinity2 }, NULL);
|
||||
if (!ok)
|
||||
beep();
|
||||
Affinity_delete(affinity2);
|
||||
}
|
||||
Object_delete(affinityPanel);
|
||||
#endif
|
||||
return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
|
||||
#else
|
||||
return HTOP_OK;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
static Htop_Reaction actionKill(State* st) {
|
||||
if (Settings_isReadonly())
|
||||
return HTOP_OK;
|
||||
|
||||
Panel* signalsPanel = SignalsPanel_new();
|
||||
ListItem* sgn = (ListItem*) Action_pickFromVector(st, signalsPanel, 15, true);
|
||||
if (sgn) {
|
||||
if (sgn->key != 0) {
|
||||
Panel_setHeader(st->panel, "Sending...");
|
||||
Panel_draw(st->panel, false, true, true, State_hideFunctionBar(st));
|
||||
refresh();
|
||||
MainPanel_foreachProcess((MainPanel*)st->panel, Process_sendSignal, (Arg) { .i = sgn->key }, NULL);
|
||||
napms(500);
|
||||
}
|
||||
const ListItem* sgn = (ListItem*) Action_pickFromVector(st, signalsPanel, 14, true);
|
||||
if (sgn && sgn->key != 0) {
|
||||
Panel_setHeader((Panel*)st->mainPanel, "Sending...");
|
||||
Panel_draw((Panel*)st->mainPanel, false, true, true, State_hideFunctionBar(st));
|
||||
refresh();
|
||||
MainPanel_foreachProcess(st->mainPanel, Process_sendSignal, (Arg) { .i = sgn->key }, NULL);
|
||||
napms(500);
|
||||
}
|
||||
Panel_delete((Object*)signalsPanel);
|
||||
return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
|
||||
@ -342,7 +364,7 @@ static Htop_Reaction actionFilterByUser(State* st) {
|
||||
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, false);
|
||||
const ListItem* picked = (ListItem*) Action_pickFromVector(st, usersPanel, 19, false);
|
||||
if (picked) {
|
||||
if (picked == allUsers) {
|
||||
st->pl->userId = (uid_t)-1;
|
||||
@ -355,22 +377,21 @@ static Htop_Reaction actionFilterByUser(State* st) {
|
||||
}
|
||||
|
||||
Htop_Reaction Action_follow(State* st) {
|
||||
st->pl->following = MainPanel_selectedPid((MainPanel*)st->panel);
|
||||
Panel_setSelectionColor(st->panel, PANEL_SELECTION_FOLLOW);
|
||||
st->pl->following = MainPanel_selectedPid(st->mainPanel);
|
||||
Panel_setSelectionColor((Panel*)st->mainPanel, PANEL_SELECTION_FOLLOW);
|
||||
return HTOP_KEEP_FOLLOWING;
|
||||
}
|
||||
|
||||
static Htop_Reaction actionSetup(State* st) {
|
||||
Action_runSetup(st);
|
||||
// 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;
|
||||
return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR | HTOP_RESIZE;
|
||||
}
|
||||
|
||||
static Htop_Reaction actionLsof(State* st) {
|
||||
Process* p = (Process*) Panel_getSelected(st->panel);
|
||||
if (Settings_isReadonly())
|
||||
return HTOP_OK;
|
||||
|
||||
const Process* p = (Process*) Panel_getSelected((Panel*)st->mainPanel);
|
||||
if (!p)
|
||||
return HTOP_OK;
|
||||
|
||||
@ -383,8 +404,9 @@ static Htop_Reaction actionLsof(State* st) {
|
||||
}
|
||||
|
||||
static Htop_Reaction actionShowLocks(State* st) {
|
||||
Process* p = (Process*) Panel_getSelected(st->panel);
|
||||
if (!p) return HTOP_OK;
|
||||
const Process* p = (Process*) Panel_getSelected((Panel*)st->mainPanel);
|
||||
if (!p)
|
||||
return HTOP_OK;
|
||||
ProcessLocksScreen* pls = ProcessLocksScreen_new(p);
|
||||
InfoScreen_run((InfoScreen*)pls);
|
||||
ProcessLocksScreen_delete((Object*)pls);
|
||||
@ -394,7 +416,10 @@ static Htop_Reaction actionShowLocks(State* st) {
|
||||
}
|
||||
|
||||
static Htop_Reaction actionStrace(State* st) {
|
||||
Process* p = (Process*) Panel_getSelected(st->panel);
|
||||
if (Settings_isReadonly())
|
||||
return HTOP_OK;
|
||||
|
||||
const Process* p = (Process*) Panel_getSelected((Panel*)st->mainPanel);
|
||||
if (!p)
|
||||
return HTOP_OK;
|
||||
|
||||
@ -410,12 +435,12 @@ static Htop_Reaction actionStrace(State* st) {
|
||||
}
|
||||
|
||||
static Htop_Reaction actionTag(State* st) {
|
||||
Process* p = (Process*) Panel_getSelected(st->panel);
|
||||
Process* p = (Process*) Panel_getSelected((Panel*)st->mainPanel);
|
||||
if (!p)
|
||||
return HTOP_OK;
|
||||
|
||||
Process_toggleTag(p);
|
||||
Panel_onKey(st->panel, KEY_DOWN);
|
||||
Panel_onKey((Panel*)st->mainPanel, KEY_DOWN);
|
||||
return HTOP_OK;
|
||||
}
|
||||
|
||||
@ -431,49 +456,51 @@ static Htop_Reaction actionTogglePauseProcessUpdate(State* st) {
|
||||
|
||||
static const struct {
|
||||
const char* key;
|
||||
bool roInactive;
|
||||
const char* info;
|
||||
} helpLeft[] = {
|
||||
{ .key = " Arrows: ", .info = "scroll process list" },
|
||||
{ .key = " Digits: ", .info = "incremental PID search" },
|
||||
{ .key = " F3 /: ", .info = "incremental name search" },
|
||||
{ .key = " F4 \\: ",.info = "incremental name filtering" },
|
||||
{ .key = " F5 t: ", .info = "tree view" },
|
||||
{ .key = " p: ", .info = "toggle program path" },
|
||||
{ .key = " m: ", .info = "toggle merged command" },
|
||||
{ .key = " Z: ", .info = "pause/resume process updates" },
|
||||
{ .key = " u: ", .info = "show processes of a single user" },
|
||||
{ .key = " H: ", .info = "hide/show user process threads" },
|
||||
{ .key = " K: ", .info = "hide/show kernel threads" },
|
||||
{ .key = " F: ", .info = "cursor follows process" },
|
||||
{ .key = " + -: ", .info = "expand/collapse tree" },
|
||||
{ .key = "N P M T: ", .info = "sort by PID, CPU%, MEM% or TIME" },
|
||||
{ .key = " I: ", .info = "invert sort order" },
|
||||
{ .key = " F6 > .: ", .info = "select sort column" },
|
||||
{ .key = " Arrows: ", .roInactive = false, .info = "scroll process list" },
|
||||
{ .key = " Digits: ", .roInactive = false, .info = "incremental PID search" },
|
||||
{ .key = " F3 /: ", .roInactive = false, .info = "incremental name search" },
|
||||
{ .key = " F4 \\: ", .roInactive = false, .info = "incremental name filtering" },
|
||||
{ .key = " F5 t: ", .roInactive = false, .info = "tree view" },
|
||||
{ .key = " p: ", .roInactive = false, .info = "toggle program path" },
|
||||
{ .key = " m: ", .roInactive = false, .info = "toggle merged command" },
|
||||
{ .key = " Z: ", .roInactive = false, .info = "pause/resume process updates" },
|
||||
{ .key = " u: ", .roInactive = false, .info = "show processes of a single user" },
|
||||
{ .key = " H: ", .roInactive = false, .info = "hide/show user process threads" },
|
||||
{ .key = " K: ", .roInactive = false, .info = "hide/show kernel threads" },
|
||||
{ .key = " F: ", .roInactive = false, .info = "cursor follows process" },
|
||||
{ .key = " + - *: ", .roInactive = false, .info = "expand/collapse tree/toggle all" },
|
||||
{ .key = "N P M T: ", .roInactive = false, .info = "sort by PID, CPU%, MEM% or TIME" },
|
||||
{ .key = " I: ", .roInactive = false, .info = "invert sort order" },
|
||||
{ .key = " F6 > .: ", .roInactive = false, .info = "select sort column" },
|
||||
{ .key = NULL, .info = NULL }
|
||||
};
|
||||
|
||||
static const struct {
|
||||
const char* key;
|
||||
bool roInactive;
|
||||
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 (defined(HAVE_LIBHWLOC) || defined(HAVE_LINUX_AFFINITY))
|
||||
{ .key = " a: ", .info = "set CPU affinity" },
|
||||
{ .key = " Space: ", .roInactive = false, .info = "tag process" },
|
||||
{ .key = " c: ", .roInactive = false, .info = "tag process and its children" },
|
||||
{ .key = " U: ", .roInactive = false, .info = "untag all processes" },
|
||||
{ .key = " F9 k: ", .roInactive = true, .info = "kill process/tagged processes" },
|
||||
{ .key = " F7 ]: ", .roInactive = true, .info = "higher priority (root only)" },
|
||||
{ .key = " F8 [: ", .roInactive = false, .info = "lower priority (+ nice)" },
|
||||
#if (defined(HAVE_LIBHWLOC) || defined(HAVE_AFFINITY))
|
||||
{ .key = " a: ", .roInactive = true, .info = "set CPU affinity" },
|
||||
#endif
|
||||
{ .key = " e: ", .info = "show process environment" },
|
||||
{ .key = " i: ", .info = "set IO priority" },
|
||||
{ .key = " l: ", .info = "list open files with lsof" },
|
||||
{ .key = " x: ", .info = "list file locks of process" },
|
||||
{ .key = " s: ", .info = "trace syscalls with strace" },
|
||||
{ .key = " w: ", .info = "wrap process command in multiple lines" },
|
||||
{ .key = " F2 C S: ", .info = "setup" },
|
||||
{ .key = " F1 h: ", .info = "show this help screen" },
|
||||
{ .key = " F10 q: ", .info = "quit" },
|
||||
{ .key = " e: ", .roInactive = false, .info = "show process environment" },
|
||||
{ .key = " i: ", .roInactive = true, .info = "set IO priority" },
|
||||
{ .key = " l: ", .roInactive = true, .info = "list open files with lsof" },
|
||||
{ .key = " x: ", .roInactive = false, .info = "list file locks of process" },
|
||||
{ .key = " s: ", .roInactive = true, .info = "trace syscalls with strace" },
|
||||
{ .key = " w: ", .roInactive = false, .info = "wrap process command in multiple lines" },
|
||||
{ .key = " F2 C S: ", .roInactive = false, .info = "setup" },
|
||||
{ .key = " F1 h ?: ", .roInactive = false, .info = "show this help screen" },
|
||||
{ .key = " F10 q: ", .roInactive = false, .info = "quit" },
|
||||
{ .key = NULL, .info = NULL }
|
||||
};
|
||||
|
||||
@ -483,8 +510,6 @@ static inline void addattrstr( int attr, const char* str) {
|
||||
}
|
||||
|
||||
static Htop_Reaction actionHelp(State* st) {
|
||||
Settings* settings = st->settings;
|
||||
|
||||
clear();
|
||||
attrset(CRT_colors[HELP_BOLD]);
|
||||
|
||||
@ -501,7 +526,7 @@ static Htop_Reaction actionHelp(State* st) {
|
||||
mvaddstr(line++, 0, "CPU usage bar: ");
|
||||
|
||||
addattrstr(CRT_colors[BAR_BORDER], "[");
|
||||
if (settings->detailedCPUTime) {
|
||||
if (st->settings->detailedCPUTime) {
|
||||
addattrstr(CRT_colors[CPU_NICE_TEXT], "low"); addstr("/");
|
||||
addattrstr(CRT_colors[CPU_NORMAL], "normal"); addstr("/");
|
||||
addattrstr(CRT_colors[CPU_SYSTEM], "kernel"); addstr("/");
|
||||
@ -515,8 +540,8 @@ static Htop_Reaction actionHelp(State* st) {
|
||||
addattrstr(CRT_colors[CPU_NICE_TEXT], "low-priority"); addstr("/");
|
||||
addattrstr(CRT_colors[CPU_NORMAL], "normal"); addstr("/");
|
||||
addattrstr(CRT_colors[CPU_SYSTEM], "kernel"); addstr("/");
|
||||
addattrstr(CRT_colors[CPU_GUEST], "virtualiz");
|
||||
addattrstr(CRT_colors[BAR_SHADOW], " used%");
|
||||
addattrstr(CRT_colors[CPU_GUEST], "virtualized");
|
||||
addattrstr(CRT_colors[BAR_SHADOW], " used%");
|
||||
}
|
||||
addattrstr(CRT_colors[BAR_BORDER], "]");
|
||||
attrset(CRT_colors[DEFAULT_COLOR]);
|
||||
@ -524,14 +549,21 @@ static Htop_Reaction actionHelp(State* st) {
|
||||
addattrstr(CRT_colors[BAR_BORDER], "[");
|
||||
addattrstr(CRT_colors[MEMORY_USED], "used"); addstr("/");
|
||||
addattrstr(CRT_colors[MEMORY_BUFFERS_TEXT], "buffers"); addstr("/");
|
||||
addattrstr(CRT_colors[MEMORY_SHARED], "shared"); addstr("/");
|
||||
addattrstr(CRT_colors[MEMORY_CACHE], "cache");
|
||||
addattrstr(CRT_colors[BAR_SHADOW], " used/total");
|
||||
addattrstr(CRT_colors[BAR_SHADOW], " used/total");
|
||||
addattrstr(CRT_colors[BAR_BORDER], "]");
|
||||
attrset(CRT_colors[DEFAULT_COLOR]);
|
||||
mvaddstr(line++, 0, "Swap bar: ");
|
||||
addattrstr(CRT_colors[BAR_BORDER], "[");
|
||||
addattrstr(CRT_colors[SWAP], "used");
|
||||
#ifdef HTOP_LINUX
|
||||
addattrstr(CRT_colors[BAR_SHADOW], "/");
|
||||
addattrstr(CRT_colors[SWAP_CACHE], "cache");
|
||||
addattrstr(CRT_colors[BAR_SHADOW], " used/total");
|
||||
#else
|
||||
addattrstr(CRT_colors[BAR_SHADOW], " used/total");
|
||||
#endif
|
||||
addattrstr(CRT_colors[BAR_BORDER], "]");
|
||||
attrset(CRT_colors[DEFAULT_COLOR]);
|
||||
mvaddstr(line++, 0, "Type and layout of header meters are configurable in the setup screen.");
|
||||
@ -544,26 +576,28 @@ static Htop_Reaction actionHelp(State* st) {
|
||||
|
||||
line++;
|
||||
|
||||
const bool readonly = Settings_isReadonly();
|
||||
|
||||
int item;
|
||||
for (item = 0; helpLeft[item].key; item++) {
|
||||
attrset(CRT_colors[DEFAULT_COLOR]);
|
||||
attrset((helpLeft[item].roInactive && readonly) ? CRT_colors[HELP_SHADOW] : CRT_colors[DEFAULT_COLOR]);
|
||||
mvaddstr(line + item, 10, helpLeft[item].info);
|
||||
attrset(CRT_colors[HELP_BOLD]);
|
||||
attrset((helpLeft[item].roInactive && readonly) ? CRT_colors[HELP_SHADOW] : CRT_colors[HELP_BOLD]);
|
||||
mvaddstr(line + item, 1, helpLeft[item].key);
|
||||
if (String_eq(helpLeft[item].key, " H: ")) {
|
||||
attrset(CRT_colors[PROCESS_THREAD]);
|
||||
attrset((helpLeft[item].roInactive && readonly) ? CRT_colors[HELP_SHADOW] : CRT_colors[PROCESS_THREAD]);
|
||||
mvaddstr(line + item, 33, "threads");
|
||||
} else if (String_eq(helpLeft[item].key, " K: ")) {
|
||||
attrset(CRT_colors[PROCESS_THREAD]);
|
||||
attrset((helpLeft[item].roInactive && readonly) ? CRT_colors[HELP_SHADOW] : CRT_colors[PROCESS_THREAD]);
|
||||
mvaddstr(line + item, 27, "threads");
|
||||
}
|
||||
}
|
||||
int leftHelpItems = item;
|
||||
|
||||
for (item = 0; helpRight[item].key; item++) {
|
||||
attrset(CRT_colors[HELP_BOLD]);
|
||||
attrset((helpRight[item].roInactive && readonly) ? CRT_colors[HELP_SHADOW] : CRT_colors[HELP_BOLD]);
|
||||
mvaddstr(line + item, 41, helpRight[item].key);
|
||||
attrset(CRT_colors[DEFAULT_COLOR]);
|
||||
attrset((helpRight[item].roInactive && readonly) ? CRT_colors[HELP_SHADOW] : CRT_colors[DEFAULT_COLOR]);
|
||||
mvaddstr(line + item, 50, helpRight[item].info);
|
||||
}
|
||||
line += MAXIMUM(leftHelpItems, item);
|
||||
@ -576,28 +610,28 @@ static Htop_Reaction actionHelp(State* st) {
|
||||
CRT_readKey();
|
||||
clear();
|
||||
|
||||
return HTOP_RECALCULATE | HTOP_REDRAW_BAR;
|
||||
return HTOP_RECALCULATE | HTOP_REDRAW_BAR | HTOP_KEEP_FOLLOWING;
|
||||
}
|
||||
|
||||
static Htop_Reaction actionUntagAll(State* st) {
|
||||
for (int i = 0; i < Panel_size(st->panel); i++) {
|
||||
Process* p = (Process*) Panel_get(st->panel, i);
|
||||
for (int i = 0; i < Panel_size((Panel*)st->mainPanel); i++) {
|
||||
Process* p = (Process*) Panel_get((Panel*)st->mainPanel, i);
|
||||
p->tag = false;
|
||||
}
|
||||
return HTOP_REFRESH;
|
||||
}
|
||||
|
||||
static Htop_Reaction actionTagAllChildren(State* st) {
|
||||
Process* p = (Process*) Panel_getSelected(st->panel);
|
||||
Process* p = (Process*) Panel_getSelected((Panel*)st->mainPanel);
|
||||
if (!p)
|
||||
return HTOP_OK;
|
||||
|
||||
tagAllChildren(st->panel, p);
|
||||
tagAllChildren((Panel*)st->mainPanel, p);
|
||||
return HTOP_OK;
|
||||
}
|
||||
|
||||
static Htop_Reaction actionShowEnvScreen(State* st) {
|
||||
Process* p = (Process*) Panel_getSelected(st->panel);
|
||||
Process* p = (Process*) Panel_getSelected((Panel*)st->mainPanel);
|
||||
if (!p)
|
||||
return HTOP_OK;
|
||||
|
||||
@ -610,7 +644,7 @@ static Htop_Reaction actionShowEnvScreen(State* st) {
|
||||
}
|
||||
|
||||
static Htop_Reaction actionShowCommandScreen(State* st) {
|
||||
Process* p = (Process*) Panel_getSelected(st->panel);
|
||||
Process* p = (Process*) Panel_getSelected((Panel*)st->mainPanel);
|
||||
if (!p)
|
||||
return HTOP_OK;
|
||||
|
||||
@ -624,6 +658,7 @@ static Htop_Reaction actionShowCommandScreen(State* st) {
|
||||
|
||||
void Action_setBindings(Htop_Action* keys) {
|
||||
keys[' '] = actionTag;
|
||||
keys['*'] = actionExpandOrCollapseAllBranches;
|
||||
keys['+'] = actionExpandOrCollapse;
|
||||
keys[','] = actionSetSortColumn;
|
||||
keys['-'] = actionExpandOrCollapse;
|
||||
|
22
Action.h
22
Action.h
@ -20,22 +20,26 @@ in the source distribution for its full text.
|
||||
#include "Settings.h"
|
||||
#include "UsersTable.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_OK = 0x00,
|
||||
HTOP_REFRESH = 0x01,
|
||||
HTOP_RECALCULATE = 0x02 | HTOP_REFRESH,
|
||||
HTOP_SAVE_SETTINGS = 0x04,
|
||||
HTOP_KEEP_FOLLOWING = 0x08,
|
||||
HTOP_QUIT = 0x10,
|
||||
HTOP_REDRAW_BAR = 0x20,
|
||||
HTOP_UPDATE_PANELHDR = 0x40 | HTOP_REFRESH,
|
||||
HTOP_RESIZE = 0x80 | HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR,
|
||||
} Htop_Reaction;
|
||||
|
||||
struct MainPanel_; // IWYU pragma: keep
|
||||
|
||||
typedef struct State_ {
|
||||
Settings* settings;
|
||||
UsersTable* ut;
|
||||
ProcessList* pl;
|
||||
Panel* panel;
|
||||
struct MainPanel_* mainPanel;
|
||||
Header* header;
|
||||
bool pauseProcessUpdate;
|
||||
bool hideProcessSelection;
|
||||
|
26
Affinity.c
26
Affinity.c
@ -14,7 +14,7 @@ in the source distribution for its full text.
|
||||
|
||||
#include "XUtils.h"
|
||||
|
||||
#ifdef HAVE_LIBHWLOC
|
||||
#if defined(HAVE_LIBHWLOC)
|
||||
#include <hwloc.h>
|
||||
#include <hwloc/bitmap.h>
|
||||
#ifdef __linux__
|
||||
@ -22,7 +22,7 @@ in the source distribution for its full text.
|
||||
#else
|
||||
#define HTOP_HWLOC_CPUBIND_FLAG HWLOC_CPUBIND_PROCESS
|
||||
#endif
|
||||
#elif defined(HAVE_LINUX_AFFINITY)
|
||||
#elif defined(HAVE_AFFINITY)
|
||||
#include <sched.h>
|
||||
#endif
|
||||
|
||||
@ -30,7 +30,7 @@ in the source distribution for its full text.
|
||||
Affinity* Affinity_new(ProcessList* pl) {
|
||||
Affinity* this = xCalloc(1, sizeof(Affinity));
|
||||
this->size = 8;
|
||||
this->cpus = xCalloc(this->size, sizeof(int));
|
||||
this->cpus = xCalloc(this->size, sizeof(unsigned int));
|
||||
this->pl = pl;
|
||||
return this;
|
||||
}
|
||||
@ -40,26 +40,26 @@ void Affinity_delete(Affinity* this) {
|
||||
free(this);
|
||||
}
|
||||
|
||||
void Affinity_add(Affinity* this, int id) {
|
||||
void Affinity_add(Affinity* this, unsigned int id) {
|
||||
if (this->used == this->size) {
|
||||
this->size *= 2;
|
||||
this->cpus = xRealloc(this->cpus, sizeof(int) * this->size);
|
||||
this->cpus = xRealloc(this->cpus, sizeof(unsigned int) * this->size);
|
||||
}
|
||||
this->cpus[this->used] = id;
|
||||
this->used++;
|
||||
}
|
||||
|
||||
|
||||
#ifdef HAVE_LIBHWLOC
|
||||
#if defined(HAVE_LIBHWLOC)
|
||||
|
||||
Affinity* Affinity_get(Process* proc, ProcessList* pl) {
|
||||
Affinity* Affinity_get(const 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++) {
|
||||
for (unsigned int i = 0; i < pl->existingCPUs; i++) {
|
||||
Affinity_add(affinity, i);
|
||||
}
|
||||
} else {
|
||||
@ -76,7 +76,7 @@ Affinity* Affinity_get(Process* proc, ProcessList* pl) {
|
||||
bool Affinity_set(Process* proc, Arg arg) {
|
||||
Affinity* this = arg.v;
|
||||
hwloc_cpuset_t cpuset = hwloc_bitmap_alloc();
|
||||
for (int i = 0; i < this->used; i++) {
|
||||
for (unsigned 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);
|
||||
@ -84,16 +84,16 @@ bool Affinity_set(Process* proc, Arg arg) {
|
||||
return ok;
|
||||
}
|
||||
|
||||
#elif defined(HAVE_LINUX_AFFINITY)
|
||||
#elif defined(HAVE_AFFINITY)
|
||||
|
||||
Affinity* Affinity_get(Process* proc, ProcessList* pl) {
|
||||
Affinity* Affinity_get(const 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++) {
|
||||
for (unsigned int i = 0; i < pl->existingCPUs; i++) {
|
||||
if (CPU_ISSET(i, &cpuset)) {
|
||||
Affinity_add(affinity, i);
|
||||
}
|
||||
@ -105,7 +105,7 @@ bool Affinity_set(Process* proc, Arg arg) {
|
||||
Affinity* this = arg.v;
|
||||
cpu_set_t cpuset;
|
||||
CPU_ZERO(&cpuset);
|
||||
for (int i = 0; i < this->used; i++) {
|
||||
for (unsigned int i = 0; i < this->used; i++) {
|
||||
CPU_SET(this->cpus[i], &cpuset);
|
||||
}
|
||||
bool ok = (sched_setaffinity(proc->pid, sizeof(unsigned long), &cpuset) == 0);
|
||||
|
20
Affinity.h
20
Affinity.h
@ -12,7 +12,7 @@ in the source distribution for its full text.
|
||||
|
||||
#include "ProcessList.h"
|
||||
|
||||
#if defined(HAVE_LIBHWLOC) || defined(HAVE_LINUX_AFFINITY)
|
||||
#if defined(HAVE_LIBHWLOC) || defined(HAVE_AFFINITY)
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "Object.h"
|
||||
@ -20,30 +20,30 @@ in the source distribution for its full text.
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(HAVE_LIBHWLOC) && defined(HAVE_LINUX_AFFINITY)
|
||||
#error hwloc and linux affinity are mutual exclusive.
|
||||
#if defined(HAVE_LIBHWLOC) && defined(HAVE_AFFINITY)
|
||||
#error hwloc and affinity support are mutual exclusive.
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct Affinity_ {
|
||||
ProcessList* pl;
|
||||
int size;
|
||||
int used;
|
||||
int* cpus;
|
||||
unsigned int size;
|
||||
unsigned int used;
|
||||
unsigned int* cpus;
|
||||
} Affinity;
|
||||
|
||||
Affinity* Affinity_new(ProcessList* pl);
|
||||
|
||||
void Affinity_delete(Affinity* this);
|
||||
|
||||
void Affinity_add(Affinity* this, int id);
|
||||
void Affinity_add(Affinity* this, unsigned int id);
|
||||
|
||||
#if defined(HAVE_LIBHWLOC) || defined(HAVE_LINUX_AFFINITY)
|
||||
#if defined(HAVE_LIBHWLOC) || defined(HAVE_AFFINITY)
|
||||
|
||||
Affinity* Affinity_get(Process* proc, ProcessList* pl);
|
||||
Affinity* Affinity_get(const Process* proc, ProcessList* pl);
|
||||
|
||||
bool Affinity_set(Process* proc, Arg arg);
|
||||
|
||||
#endif /* HAVE_LIBHWLOC || HAVE_LINUX_AFFINITY */
|
||||
#endif /* HAVE_LIBHWLOC || HAVE_AFFINITY */
|
||||
|
||||
#endif
|
||||
|
@ -357,7 +357,7 @@ static const char* const AffinityPanelFunctions[] = {
|
||||
static const char* const AffinityPanelKeys[] = {"Enter", "Esc", "F1", "F2", "F3"};
|
||||
static const int AffinityPanelEvents[] = {13, 27, KEY_F(1), KEY_F(2), KEY_F(3)};
|
||||
|
||||
Panel* AffinityPanel_new(ProcessList* pl, Affinity* affinity, int* width) {
|
||||
Panel* AffinityPanel_new(ProcessList* pl, const Affinity* affinity, int* width) {
|
||||
AffinityPanel* this = AllocThis(AffinityPanel);
|
||||
Panel* super = (Panel*) this;
|
||||
Panel_init(super, 1, 1, 1, 1, Class(MaskItem), false, FunctionBar_new(AffinityPanelFunctions, AffinityPanelKeys, AffinityPanelEvents));
|
||||
@ -382,8 +382,11 @@ Panel* AffinityPanel_new(ProcessList* pl, Affinity* affinity, int* width) {
|
||||
|
||||
Panel_setHeader(super, "Use CPUs:");
|
||||
|
||||
int curCpu = 0;
|
||||
for (int i = 0; i < pl->cpuCount; i++) {
|
||||
unsigned int curCpu = 0;
|
||||
for (unsigned int i = 0; i < pl->existingCPUs; i++) {
|
||||
if (!ProcessList_isCPUonline(this->pl, i))
|
||||
continue;
|
||||
|
||||
char number[16];
|
||||
xSnprintf(number, 9, "CPU %d", Settings_cpuId(pl->settings, i));
|
||||
unsigned cpu_width = 4 + strlen(number);
|
||||
@ -418,17 +421,17 @@ Panel* AffinityPanel_new(ProcessList* pl, Affinity* affinity, int* width) {
|
||||
}
|
||||
|
||||
Affinity* AffinityPanel_getAffinity(Panel* super, ProcessList* pl) {
|
||||
AffinityPanel* this = (AffinityPanel*) super;
|
||||
const AffinityPanel* this = (AffinityPanel*) super;
|
||||
Affinity* affinity = Affinity_new(pl);
|
||||
|
||||
#ifdef HAVE_LIBHWLOC
|
||||
int i;
|
||||
unsigned int i;
|
||||
hwloc_bitmap_foreach_begin(i, this->workCpuset)
|
||||
Affinity_add(affinity, i);
|
||||
hwloc_bitmap_foreach_end();
|
||||
#else
|
||||
for (int i = 0; i < this->pl->cpuCount; i++) {
|
||||
MaskItem* item = (MaskItem*)Vector_get(this->cpuids, i);
|
||||
for (int i = 0; i < Vector_size(this->cpuids); i++) {
|
||||
const MaskItem* item = (const MaskItem*)Vector_get(this->cpuids, i);
|
||||
if (item->value) {
|
||||
Affinity_add(affinity, item->cpu);
|
||||
}
|
||||
|
@ -7,13 +7,14 @@ Released under the GNU GPLv2, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
#include "Panel.h"
|
||||
#include "Affinity.h"
|
||||
#include "Panel.h"
|
||||
#include "ProcessList.h"
|
||||
|
||||
|
||||
extern const PanelClass AffinityPanel_class;
|
||||
|
||||
Panel* AffinityPanel_new(ProcessList* pl, Affinity* affinity, int* width);
|
||||
Panel* AffinityPanel_new(ProcessList* pl, const Affinity* affinity, int* width);
|
||||
|
||||
Affinity* AffinityPanel_getAffinity(Panel* super, ProcessList* pl);
|
||||
|
||||
|
@ -7,15 +7,17 @@ in the source distribution for its full text.
|
||||
|
||||
#include "AvailableColumnsPanel.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "ColumnsPanel.h"
|
||||
#include "DynamicColumn.h"
|
||||
#include "FunctionBar.h"
|
||||
#include "Hashtable.h"
|
||||
#include "ListItem.h"
|
||||
#include "Object.h"
|
||||
#include "Platform.h"
|
||||
#include "Process.h"
|
||||
#include "ProvideCurses.h"
|
||||
#include "XUtils.h"
|
||||
@ -30,6 +32,15 @@ static void AvailableColumnsPanel_delete(Object* object) {
|
||||
free(this);
|
||||
}
|
||||
|
||||
static void AvailableColumnsPanel_insert(AvailableColumnsPanel* this, int at, int key) {
|
||||
const char* name;
|
||||
if (key >= LAST_PROCESSFIELD)
|
||||
name = DynamicColumn_init(key);
|
||||
else
|
||||
name = Process_fields[key].name;
|
||||
Panel_insert(this->columns, at, (Object*) ListItem_new(name, key));
|
||||
}
|
||||
|
||||
static HandlerResult AvailableColumnsPanel_eventHandler(Panel* super, int ch) {
|
||||
AvailableColumnsPanel* this = (AvailableColumnsPanel*) super;
|
||||
HandlerResult result = IGNORED;
|
||||
@ -43,10 +54,9 @@ static HandlerResult AvailableColumnsPanel_eventHandler(Panel* super, int ch) {
|
||||
if (!selected)
|
||||
break;
|
||||
|
||||
int key = selected->key;
|
||||
int at = Panel_getSelectedIndex(this->columns);
|
||||
Panel_insert(this->columns, at, (Object*) ListItem_new(Process_fields[key].name, key));
|
||||
Panel_setSelected(this->columns, at+1);
|
||||
AvailableColumnsPanel_insert(this, at, selected->key);
|
||||
Panel_setSelected(this->columns, at + 1);
|
||||
ColumnsPanel_update(this->columns);
|
||||
result = HANDLED;
|
||||
break;
|
||||
@ -69,14 +79,25 @@ const PanelClass AvailableColumnsPanel_class = {
|
||||
.eventHandler = AvailableColumnsPanel_eventHandler
|
||||
};
|
||||
|
||||
AvailableColumnsPanel* AvailableColumnsPanel_new(Panel* columns) {
|
||||
AvailableColumnsPanel* this = AllocThis(AvailableColumnsPanel);
|
||||
Panel* super = (Panel*) this;
|
||||
FunctionBar* fuBar = FunctionBar_new(AvailableColumnsFunctions, NULL, NULL);
|
||||
Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar);
|
||||
static void AvailableColumnsPanel_addDynamicColumn(ht_key_t key, void* value, void* data) {
|
||||
const DynamicColumn* column = (const DynamicColumn*) value;
|
||||
Panel* super = (Panel*) data;
|
||||
const char* title = column->caption ? column->caption : column->heading;
|
||||
if (!title)
|
||||
title = column->name; // fallback to the only mandatory field
|
||||
char description[256];
|
||||
xSnprintf(description, sizeof(description), "%s - %s", title, column->description);
|
||||
Panel_add(super, (Object*) ListItem_new(description, key));
|
||||
}
|
||||
|
||||
Panel_setHeader(super, "Available Columns");
|
||||
// Handle DynamicColumns entries in the AvailableColumnsPanel
|
||||
static void AvailableColumnsPanel_addDynamicColumns(Panel* super, Hashtable* dynamicColumns) {
|
||||
assert(dynamicColumns);
|
||||
Hashtable_foreach(dynamicColumns, AvailableColumnsPanel_addDynamicColumn, super);
|
||||
}
|
||||
|
||||
// Handle remaining Platform Meter entries in the AvailableColumnsPanel
|
||||
static void AvailableColumnsPanel_addPlatformColumn(Panel* super) {
|
||||
for (int i = 1; i < LAST_PROCESSFIELD; i++) {
|
||||
if (i != COMM && Process_fields[i].description) {
|
||||
char description[256];
|
||||
@ -84,6 +105,18 @@ AvailableColumnsPanel* AvailableColumnsPanel_new(Panel* columns) {
|
||||
Panel_add(super, (Object*) ListItem_new(description, i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AvailableColumnsPanel* AvailableColumnsPanel_new(Panel* columns, Hashtable* dynamicColumns) {
|
||||
AvailableColumnsPanel* this = AllocThis(AvailableColumnsPanel);
|
||||
Panel* super = (Panel*) this;
|
||||
FunctionBar* fuBar = FunctionBar_new(AvailableColumnsFunctions, NULL, NULL);
|
||||
Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar);
|
||||
|
||||
Panel_setHeader(super, "Available Columns");
|
||||
AvailableColumnsPanel_addPlatformColumn(super);
|
||||
AvailableColumnsPanel_addDynamicColumns(super, dynamicColumns);
|
||||
|
||||
this->columns = columns;
|
||||
return this;
|
||||
}
|
||||
|
@ -7,8 +7,10 @@ Released under the GNU GPLv2, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
#include "Hashtable.h"
|
||||
#include "Panel.h"
|
||||
|
||||
|
||||
typedef struct AvailableColumnsPanel_ {
|
||||
Panel super;
|
||||
Panel* columns;
|
||||
@ -16,6 +18,6 @@ typedef struct AvailableColumnsPanel_ {
|
||||
|
||||
extern const PanelClass AvailableColumnsPanel_class;
|
||||
|
||||
AvailableColumnsPanel* AvailableColumnsPanel_new(Panel* columns);
|
||||
AvailableColumnsPanel* AvailableColumnsPanel_new(Panel* columns, Hashtable* dynamicColumns);
|
||||
|
||||
#endif
|
||||
|
@ -12,9 +12,12 @@ in the source distribution for its full text.
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "CPUMeter.h"
|
||||
#include "DynamicMeter.h"
|
||||
#include "FunctionBar.h"
|
||||
#include "Hashtable.h"
|
||||
#include "Header.h"
|
||||
#include "ListItem.h"
|
||||
#include "Macros.h"
|
||||
#include "Meter.h"
|
||||
#include "MetersPanel.h"
|
||||
#include "Object.h"
|
||||
@ -27,14 +30,15 @@ static void AvailableMetersPanel_delete(Object* object) {
|
||||
Panel* super = (Panel*) object;
|
||||
AvailableMetersPanel* this = (AvailableMetersPanel*) object;
|
||||
Panel_done(super);
|
||||
free(this->meterPanels);
|
||||
free(this);
|
||||
}
|
||||
|
||||
static inline void AvailableMetersPanel_addMeter(Header* header, Panel* panel, const MeterClass* type, int param, int column) {
|
||||
Meter* meter = Header_addMeterByClass(header, type, param, column);
|
||||
Panel_add(panel, (Object*) Meter_toListItem(meter, false));
|
||||
Panel_setSelected(panel, Panel_size(panel) - 1);
|
||||
MetersPanel_setMoving((MetersPanel*)panel, true);
|
||||
static inline void AvailableMetersPanel_addMeter(Header* header, MetersPanel* panel, const MeterClass* type, unsigned int param, size_t column) {
|
||||
const Meter* meter = Header_addMeterByClass(header, type, param, column);
|
||||
Panel_add((Panel*)panel, (Object*) Meter_toListItem(meter, false));
|
||||
Panel_setSelected((Panel*)panel, Panel_size((Panel*)panel) - 1);
|
||||
MetersPanel_setMoving(panel, true);
|
||||
}
|
||||
|
||||
static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) {
|
||||
@ -45,7 +49,7 @@ static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) {
|
||||
if (!selected)
|
||||
return IGNORED;
|
||||
|
||||
int param = selected->key & 0xff;
|
||||
unsigned int param = selected->key & 0xffff;
|
||||
int type = selected->key >> 16;
|
||||
HandlerResult result = IGNORED;
|
||||
bool update = false;
|
||||
@ -55,7 +59,7 @@ static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) {
|
||||
case 'l':
|
||||
case 'L':
|
||||
{
|
||||
AvailableMetersPanel_addMeter(header, this->leftPanel, Platform_meterTypes[type], param, 0);
|
||||
AvailableMetersPanel_addMeter(header, this->meterPanels[0], Platform_meterTypes[type], param, 0);
|
||||
result = HANDLED;
|
||||
update = true;
|
||||
break;
|
||||
@ -67,7 +71,7 @@ static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) {
|
||||
case 'r':
|
||||
case 'R':
|
||||
{
|
||||
AvailableMetersPanel_addMeter(header, this->rightPanel, Platform_meterTypes[type], param, 1);
|
||||
AvailableMetersPanel_addMeter(header, this->meterPanels[this->columns - 1], Platform_meterTypes[type], param, this->columns - 1);
|
||||
result = (KEY_LEFT << 16) | SYNTH_KEY;
|
||||
update = true;
|
||||
break;
|
||||
@ -76,8 +80,9 @@ static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) {
|
||||
if (update) {
|
||||
this->settings->changed = true;
|
||||
Header_calculateHeight(header);
|
||||
Header_updateData(header);
|
||||
Header_draw(header);
|
||||
ScreenManager_resize(this->scr, this->scr->x1, header->height, this->scr->x2, this->scr->y2);
|
||||
ScreenManager_resize(this->scr);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -90,7 +95,51 @@ const PanelClass AvailableMetersPanel_class = {
|
||||
.eventHandler = AvailableMetersPanel_eventHandler
|
||||
};
|
||||
|
||||
AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* header, Panel* leftMeters, Panel* rightMeters, ScreenManager* scr, ProcessList* pl) {
|
||||
// Handle (&CPUMeter_class) entries in the AvailableMetersPanel
|
||||
static void AvailableMetersPanel_addCPUMeters(Panel* super, const MeterClass* type, const ProcessList* pl) {
|
||||
if (pl->existingCPUs > 1) {
|
||||
Panel_add(super, (Object*) ListItem_new("CPU average", 0));
|
||||
for (unsigned int i = 1; i <= pl->existingCPUs; i++) {
|
||||
char buffer[50];
|
||||
xSnprintf(buffer, sizeof(buffer), "%s %d", type->uiName, Settings_cpuId(pl->settings, i - 1));
|
||||
Panel_add(super, (Object*) ListItem_new(buffer, i));
|
||||
}
|
||||
} else {
|
||||
Panel_add(super, (Object*) ListItem_new(type->uiName, 1));
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
Panel* super;
|
||||
unsigned int id;
|
||||
unsigned int offset;
|
||||
} DynamicIterator;
|
||||
|
||||
static void AvailableMetersPanel_addDynamicMeter(ATTR_UNUSED ht_key_t key, void* value, void* data) {
|
||||
const DynamicMeter* meter = (const DynamicMeter*)value;
|
||||
DynamicIterator* iter = (DynamicIterator*)data;
|
||||
unsigned int identifier = (iter->offset << 16) | iter->id;
|
||||
const char* label = meter->description ? meter->description : meter->caption;
|
||||
if (!label)
|
||||
label = meter->name; /* last fallback to name, guaranteed set */
|
||||
Panel_add(iter->super, (Object*) ListItem_new(label, identifier));
|
||||
iter->id++;
|
||||
}
|
||||
|
||||
// Handle (&DynamicMeter_class) entries in the AvailableMetersPanel
|
||||
static void AvailableMetersPanel_addDynamicMeters(Panel* super, const ProcessList* pl, unsigned int offset) {
|
||||
DynamicIterator iter = { .super = super, .id = 1, .offset = offset };
|
||||
assert(pl->dynamicMeters != NULL);
|
||||
Hashtable_foreach(pl->dynamicMeters, AvailableMetersPanel_addDynamicMeter, &iter);
|
||||
}
|
||||
|
||||
// Handle remaining Platform Meter entries in the AvailableMetersPanel
|
||||
static void AvailableMetersPanel_addPlatformMeter(Panel* super, const MeterClass* type, unsigned int offset) {
|
||||
const char* label = type->description ? type->description : type->uiName;
|
||||
Panel_add(super, (Object*) ListItem_new(label, offset << 16));
|
||||
}
|
||||
|
||||
AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* header, size_t columns, MetersPanel** meterPanels, ScreenManager* scr, const ProcessList* pl) {
|
||||
AvailableMetersPanel* this = AllocThis(AvailableMetersPanel);
|
||||
Panel* super = (Panel*) this;
|
||||
FunctionBar* fuBar = FunctionBar_newEnterEsc("Add ", "Done ");
|
||||
@ -98,31 +147,24 @@ AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* heade
|
||||
|
||||
this->settings = settings;
|
||||
this->header = header;
|
||||
this->leftPanel = leftMeters;
|
||||
this->rightPanel = rightMeters;
|
||||
this->columns = columns;
|
||||
this->meterPanels = meterPanels;
|
||||
this->scr = scr;
|
||||
|
||||
Panel_setHeader(super, "Available meters");
|
||||
// Platform_meterTypes[0] should be always (&CPUMeter_class), which we will
|
||||
// handle separately in the code below.
|
||||
for (int i = 1; Platform_meterTypes[i]; i++) {
|
||||
// Platform_meterTypes[0] should be always (&CPUMeter_class) which we will
|
||||
// handle separately in the code below. Likewise, identifiers for Dynamic
|
||||
// Meters are handled separately - similar to CPUs, this allows generation
|
||||
// of multiple different Meters (also using 'param' to distinguish them).
|
||||
for (unsigned int i = 1; Platform_meterTypes[i]; i++) {
|
||||
const MeterClass* type = Platform_meterTypes[i];
|
||||
assert(type != &CPUMeter_class);
|
||||
const char* label = type->description ? type->description : type->uiName;
|
||||
Panel_add(super, (Object*) ListItem_new(label, i << 16));
|
||||
}
|
||||
// Handle (&CPUMeter_class)
|
||||
const MeterClass* type = &CPUMeter_class;
|
||||
int cpus = pl->cpuCount;
|
||||
if (cpus > 1) {
|
||||
Panel_add(super, (Object*) ListItem_new("CPU average", 0));
|
||||
for (int i = 1; i <= cpus; i++) {
|
||||
char buffer[50];
|
||||
xSnprintf(buffer, sizeof(buffer), "%s %d", type->uiName, Settings_cpuId(this->settings, i - 1));
|
||||
Panel_add(super, (Object*) ListItem_new(buffer, i));
|
||||
}
|
||||
} else {
|
||||
Panel_add(super, (Object*) ListItem_new("CPU", 1));
|
||||
if (type == &DynamicMeter_class)
|
||||
AvailableMetersPanel_addDynamicMeters(super, pl, i);
|
||||
else
|
||||
AvailableMetersPanel_addPlatformMeter(super, type, i);
|
||||
}
|
||||
AvailableMetersPanel_addCPUMeters(super, &CPUMeter_class, pl);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
@ -7,24 +7,28 @@ Released under the GNU GPLv2, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "Header.h"
|
||||
#include "MetersPanel.h"
|
||||
#include "Panel.h"
|
||||
#include "ProcessList.h"
|
||||
#include "ScreenManager.h"
|
||||
#include "Settings.h"
|
||||
|
||||
|
||||
typedef struct AvailableMetersPanel_ {
|
||||
Panel super;
|
||||
ScreenManager* scr;
|
||||
|
||||
Settings* settings;
|
||||
Header* header;
|
||||
Panel* leftPanel;
|
||||
Panel* rightPanel;
|
||||
size_t columns;
|
||||
MetersPanel** meterPanels;
|
||||
} AvailableMetersPanel;
|
||||
|
||||
extern const PanelClass AvailableMetersPanel_class;
|
||||
|
||||
AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* header, Panel* leftMeters, Panel* rightMeters, ScreenManager* scr, ProcessList* pl);
|
||||
AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* header, size_t columns, MetersPanel **meterPanels, ScreenManager* scr, const ProcessList* pl);
|
||||
|
||||
#endif
|
||||
|
@ -21,7 +21,7 @@ static const int BatteryMeter_attributes[] = {
|
||||
BATTERY
|
||||
};
|
||||
|
||||
static void BatteryMeter_updateValues(Meter* this, char* buffer, size_t len) {
|
||||
static void BatteryMeter_updateValues(Meter* this) {
|
||||
ACPresence isOnAC;
|
||||
double percent;
|
||||
|
||||
@ -29,7 +29,7 @@ static void BatteryMeter_updateValues(Meter* this, char* buffer, size_t len) {
|
||||
|
||||
if (isnan(percent)) {
|
||||
this->values[0] = NAN;
|
||||
xSnprintf(buffer, len, "N/A");
|
||||
xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "N/A");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -49,7 +49,7 @@ static void BatteryMeter_updateValues(Meter* this, char* buffer, size_t len) {
|
||||
break;
|
||||
}
|
||||
|
||||
xSnprintf(buffer, len, "%.1f%%%s", percent, text);
|
||||
xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%.1f%%%s", percent, text);
|
||||
}
|
||||
|
||||
const MeterClass BatteryMeter_class = {
|
||||
|
@ -11,6 +11,7 @@ This meter written by Ian P. Hands (iphands@gmail.com, ihands@redhat.com).
|
||||
|
||||
#include "Meter.h"
|
||||
|
||||
|
||||
typedef enum ACPresence_ {
|
||||
AC_ABSENT,
|
||||
AC_PRESENT,
|
||||
|
@ -29,9 +29,9 @@ are always included, please send those in!
|
||||
Feature Requests
|
||||
----------------
|
||||
|
||||
Please label Github issues that are feature requests with the [`feature
|
||||
request`](https://github.com/htop-dev/htop/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3A%22feature+request%22+)
|
||||
label.
|
||||
Please label Github issues that are feature requests with one of the `feature request`
|
||||
labels. If you can't do this yourself, don't worry. The friendly folks from the
|
||||
core team will distribute and fixup Github labels as part of the regular reviews.
|
||||
|
||||
Style Guide
|
||||
-----------
|
||||
|
58
COPYING
58
COPYING
@ -1,12 +1,12 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
675 Mass Ave, Cambridge, MA 02139, USA
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
@ -15,7 +15,7 @@ software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
@ -55,8 +55,8 @@ patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
@ -110,7 +110,7 @@ above, provided that you also meet all of these conditions:
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
@ -168,7 +168,7 @@ access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
@ -225,7 +225,7 @@ impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
@ -255,7 +255,7 @@ make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
@ -277,9 +277,9 @@ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
Appendix: How to Apply These Terms to Your New Programs
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
@ -291,7 +291,7 @@ convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) 19yy <name of author>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -303,16 +303,16 @@ the "copyright" line and a pointer to where the full notice is found.
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) 19yy name of author
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
@ -335,21 +335,5 @@ necessary. Here is a sample; alter the names:
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Library General
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
|
||||
|
||||
Appendix 2: Special exception concerning PLPA
|
||||
|
||||
In the following exception, "PLPA" means (i) code released by the
|
||||
Portable Linux Processor Affinity Project, or (ii) derivative works of
|
||||
such code, in both cases provided that the code is covered entirely by
|
||||
free software licensing terms.
|
||||
|
||||
As a special exception to the GNU GPL, the licensors of htop give you
|
||||
permission to combine GNU GPL-licensed code in htop (and derivative
|
||||
works of such code) with PLPA. You may copy and distribute such a
|
||||
combined work following the terms of the GNU GPL for htop and the
|
||||
applicable licenses of the version of PLPA used in your combined work,
|
||||
provided that you include the source code of such version of PLPA when
|
||||
and as the GNU GPL requires distribution of source code.
|
||||
|
143
CPUMeter.c
143
CPUMeter.c
@ -10,7 +10,6 @@ in the source distribution for its full text.
|
||||
#include "CPUMeter.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
@ -35,37 +34,48 @@ static const int CPUMeter_attributes[] = {
|
||||
};
|
||||
|
||||
typedef struct CPUMeterData_ {
|
||||
int cpus;
|
||||
unsigned int cpus;
|
||||
Meter** meters;
|
||||
} CPUMeterData;
|
||||
|
||||
static void CPUMeter_init(Meter* this) {
|
||||
int cpu = this->param;
|
||||
if (this->pl->cpuCount > 1) {
|
||||
unsigned int cpu = this->param;
|
||||
if (cpu == 0) {
|
||||
Meter_setCaption(this, "Avg");
|
||||
} else if (this->pl->activeCPUs > 1) {
|
||||
char caption[10];
|
||||
xSnprintf(caption, sizeof(caption), "%3d", Settings_cpuId(this->pl->settings, cpu - 1));
|
||||
xSnprintf(caption, sizeof(caption), "%3u", Settings_cpuId(this->pl->settings, cpu - 1));
|
||||
Meter_setCaption(this, caption);
|
||||
}
|
||||
if (this->param == 0)
|
||||
Meter_setCaption(this, "Avg");
|
||||
}
|
||||
|
||||
static void CPUMeter_updateValues(Meter* this, char* buffer, size_t size) {
|
||||
int cpu = this->param;
|
||||
if (cpu > this->pl->cpuCount) {
|
||||
xSnprintf(buffer, size, "absent");
|
||||
for (uint8_t i = 0; i < this->curItems; i++)
|
||||
this->values[i] = 0;
|
||||
// Custom uiName runtime logic to include the param (processor)
|
||||
static void CPUMeter_getUiName(const Meter* this, char* buffer, size_t length) {
|
||||
if (this->param > 0)
|
||||
xSnprintf(buffer, sizeof(length), "%s %u", Meter_uiName(this), this->param);
|
||||
else
|
||||
xSnprintf(buffer, sizeof(length), "%s", Meter_uiName(this));
|
||||
}
|
||||
|
||||
static void CPUMeter_updateValues(Meter* this) {
|
||||
memset(this->values, 0, sizeof(double) * CPU_METER_ITEMCOUNT);
|
||||
|
||||
unsigned int cpu = this->param;
|
||||
if (cpu > this->pl->existingCPUs) {
|
||||
xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "absent");
|
||||
return;
|
||||
}
|
||||
|
||||
double percent = Platform_setCPUValues(this, cpu);
|
||||
if (isnan(percent)) {
|
||||
xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "offline");
|
||||
return;
|
||||
}
|
||||
memset(this->values, 0, sizeof(double) * CPU_METER_ITEMCOUNT);
|
||||
|
||||
char cpuUsageBuffer[8] = { 0 };
|
||||
char cpuFrequencyBuffer[16] = { 0 };
|
||||
char cpuTemperatureBuffer[16] = { 0 };
|
||||
|
||||
double percent = Platform_setCPUValues(this, cpu);
|
||||
|
||||
if (this->pl->settings->showCPUUsage) {
|
||||
xSnprintf(cpuUsageBuffer, sizeof(cpuUsageBuffer), "%.1f%%", percent);
|
||||
}
|
||||
@ -79,7 +89,7 @@ static void CPUMeter_updateValues(Meter* this, char* buffer, size_t size) {
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_SENSORS_SENSORS_H
|
||||
#ifdef BUILD_WITH_CPU_TEMP
|
||||
if (this->pl->settings->showCPUTemperature) {
|
||||
double cpuTemperature = this->values[CPU_METER_TEMPERATURE];
|
||||
if (isnan(cpuTemperature))
|
||||
@ -91,7 +101,7 @@ static void CPUMeter_updateValues(Meter* this, char* buffer, size_t size) {
|
||||
}
|
||||
#endif
|
||||
|
||||
xSnprintf(buffer, size, "%s%s%s%s%s",
|
||||
xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%s%s%s%s%s",
|
||||
cpuUsageBuffer,
|
||||
(cpuUsageBuffer[0] && (cpuFrequencyBuffer[0] || cpuTemperatureBuffer[0])) ? " " : "",
|
||||
cpuFrequencyBuffer,
|
||||
@ -101,75 +111,82 @@ static void CPUMeter_updateValues(Meter* this, char* buffer, size_t size) {
|
||||
|
||||
static void CPUMeter_display(const Object* cast, RichString* out) {
|
||||
char buffer[50];
|
||||
int len;
|
||||
const Meter* this = (const Meter*)cast;
|
||||
RichString_prune(out);
|
||||
if (this->param > this->pl->cpuCount) {
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], "absent");
|
||||
|
||||
if (this->param > this->pl->existingCPUs) {
|
||||
RichString_appendAscii(out, CRT_colors[METER_SHADOW], " absent");
|
||||
return;
|
||||
}
|
||||
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_NORMAL]);
|
||||
|
||||
if (this->curItems == 0) {
|
||||
RichString_appendAscii(out, CRT_colors[METER_SHADOW], " offline");
|
||||
return;
|
||||
}
|
||||
|
||||
len = xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_NORMAL]);
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], ":");
|
||||
RichString_appendAscii(out, CRT_colors[CPU_NORMAL], buffer);
|
||||
RichString_appendnAscii(out, CRT_colors[CPU_NORMAL], buffer, len);
|
||||
if (this->pl->settings->detailedCPUTime) {
|
||||
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_KERNEL]);
|
||||
len = xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_KERNEL]);
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], "sy:");
|
||||
RichString_appendAscii(out, CRT_colors[CPU_SYSTEM], buffer);
|
||||
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_NICE]);
|
||||
RichString_appendnAscii(out, CRT_colors[CPU_SYSTEM], buffer, len);
|
||||
len = xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_NICE]);
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], "ni:");
|
||||
RichString_appendAscii(out, CRT_colors[CPU_NICE_TEXT], buffer);
|
||||
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_IRQ]);
|
||||
RichString_appendnAscii(out, CRT_colors[CPU_NICE_TEXT], buffer, len);
|
||||
len = xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_IRQ]);
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], "hi:");
|
||||
RichString_appendAscii(out, CRT_colors[CPU_IRQ], buffer);
|
||||
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_SOFTIRQ]);
|
||||
RichString_appendnAscii(out, CRT_colors[CPU_IRQ], buffer, len);
|
||||
len = xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_SOFTIRQ]);
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], "si:");
|
||||
RichString_appendAscii(out, CRT_colors[CPU_SOFTIRQ], buffer);
|
||||
RichString_appendnAscii(out, CRT_colors[CPU_SOFTIRQ], buffer, len);
|
||||
if (!isnan(this->values[CPU_METER_STEAL])) {
|
||||
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_STEAL]);
|
||||
len = xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_STEAL]);
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], "st:");
|
||||
RichString_appendAscii(out, CRT_colors[CPU_STEAL], buffer);
|
||||
RichString_appendnAscii(out, CRT_colors[CPU_STEAL], buffer, len);
|
||||
}
|
||||
if (!isnan(this->values[CPU_METER_GUEST])) {
|
||||
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_GUEST]);
|
||||
len = xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_GUEST]);
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], "gu:");
|
||||
RichString_appendAscii(out, CRT_colors[CPU_GUEST], buffer);
|
||||
RichString_appendnAscii(out, CRT_colors[CPU_GUEST], buffer, len);
|
||||
}
|
||||
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_IOWAIT]);
|
||||
len = xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_IOWAIT]);
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], "wa:");
|
||||
RichString_appendAscii(out, CRT_colors[CPU_IOWAIT], buffer);
|
||||
RichString_appendnAscii(out, CRT_colors[CPU_IOWAIT], buffer, len);
|
||||
} else {
|
||||
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_KERNEL]);
|
||||
len = xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_KERNEL]);
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], "sys:");
|
||||
RichString_appendAscii(out, CRT_colors[CPU_SYSTEM], buffer);
|
||||
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_NICE]);
|
||||
RichString_appendnAscii(out, CRT_colors[CPU_SYSTEM], buffer, len);
|
||||
len = xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_NICE]);
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], "low:");
|
||||
RichString_appendAscii(out, CRT_colors[CPU_NICE_TEXT], buffer);
|
||||
RichString_appendnAscii(out, CRT_colors[CPU_NICE_TEXT], buffer, len);
|
||||
if (!isnan(this->values[CPU_METER_IRQ])) {
|
||||
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_IRQ]);
|
||||
len = xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_IRQ]);
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], "vir:");
|
||||
RichString_appendAscii(out, CRT_colors[CPU_GUEST], buffer);
|
||||
RichString_appendnAscii(out, CRT_colors[CPU_GUEST], buffer, len);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_SENSORS_SENSORS_H
|
||||
#ifdef BUILD_WITH_CPU_TEMP
|
||||
if (this->pl->settings->showCPUTemperature) {
|
||||
char cpuTemperatureBuffer[10];
|
||||
double cpuTemperature = this->values[CPU_METER_TEMPERATURE];
|
||||
if (isnan(cpuTemperature)) {
|
||||
xSnprintf(cpuTemperatureBuffer, sizeof(cpuTemperatureBuffer), "N/A");
|
||||
len = xSnprintf(cpuTemperatureBuffer, sizeof(cpuTemperatureBuffer), "N/A");
|
||||
} else if (this->pl->settings->degreeFahrenheit) {
|
||||
xSnprintf(cpuTemperatureBuffer, sizeof(cpuTemperatureBuffer), "%5.1f%sF", cpuTemperature * 9 / 5 + 32, CRT_degreeSign);
|
||||
len = xSnprintf(cpuTemperatureBuffer, sizeof(cpuTemperatureBuffer), "%5.1f%sF", cpuTemperature * 9 / 5 + 32, CRT_degreeSign);
|
||||
} else {
|
||||
xSnprintf(cpuTemperatureBuffer, sizeof(cpuTemperatureBuffer), "%5.1f%sC", cpuTemperature, CRT_degreeSign);
|
||||
len = xSnprintf(cpuTemperatureBuffer, sizeof(cpuTemperatureBuffer), "%5.1f%sC", cpuTemperature, CRT_degreeSign);
|
||||
}
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], "temp:");
|
||||
RichString_appendWide(out, CRT_colors[METER_VALUE], cpuTemperatureBuffer);
|
||||
RichString_appendnWide(out, CRT_colors[METER_VALUE], cpuTemperatureBuffer, len);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void AllCPUsMeter_getRange(Meter* this, int* start, int* count) {
|
||||
CPUMeterData* data = this->meterData;
|
||||
int cpus = data->cpus;
|
||||
static void AllCPUsMeter_getRange(const Meter* this, int* start, int* count) {
|
||||
const CPUMeterData* data = this->meterData;
|
||||
unsigned int cpus = data->cpus;
|
||||
switch(Meter_name(this)[0]) {
|
||||
default:
|
||||
case 'A': // All
|
||||
@ -187,8 +204,17 @@ static void AllCPUsMeter_getRange(Meter* this, int* start, int* count) {
|
||||
}
|
||||
}
|
||||
|
||||
static void AllCPUsMeter_updateValues(Meter* this) {
|
||||
CPUMeterData* data = this->meterData;
|
||||
Meter** meters = data->meters;
|
||||
int start, count;
|
||||
AllCPUsMeter_getRange(this, &start, &count);
|
||||
for (int i = 0; i < count; i++)
|
||||
Meter_updateValues(meters[i]);
|
||||
}
|
||||
|
||||
static void CPUMeterCommonInit(Meter* this, int ncol) {
|
||||
int cpus = this->pl->cpuCount;
|
||||
unsigned int cpus = this->pl->existingCPUs;
|
||||
CPUMeterData* data = this->meterData;
|
||||
if (!data) {
|
||||
data = this->meterData = xMalloc(sizeof(CPUMeterData));
|
||||
@ -316,6 +342,7 @@ const MeterClass CPUMeter_class = {
|
||||
.display = CPUMeter_display
|
||||
},
|
||||
.updateValues = CPUMeter_updateValues,
|
||||
.getUiName = CPUMeter_getUiName,
|
||||
.defaultMode = BAR_METERMODE,
|
||||
.maxItems = CPU_METER_ITEMCOUNT,
|
||||
.total = 100.0,
|
||||
@ -332,6 +359,7 @@ const MeterClass AllCPUsMeter_class = {
|
||||
.delete = Meter_delete,
|
||||
.display = CPUMeter_display
|
||||
},
|
||||
.updateValues = AllCPUsMeter_updateValues,
|
||||
.defaultMode = CUSTOM_METERMODE,
|
||||
.total = 100.0,
|
||||
.attributes = CPUMeter_attributes,
|
||||
@ -351,6 +379,7 @@ const MeterClass AllCPUs2Meter_class = {
|
||||
.delete = Meter_delete,
|
||||
.display = CPUMeter_display
|
||||
},
|
||||
.updateValues = AllCPUsMeter_updateValues,
|
||||
.defaultMode = CUSTOM_METERMODE,
|
||||
.total = 100.0,
|
||||
.attributes = CPUMeter_attributes,
|
||||
@ -370,6 +399,7 @@ const MeterClass LeftCPUsMeter_class = {
|
||||
.delete = Meter_delete,
|
||||
.display = CPUMeter_display
|
||||
},
|
||||
.updateValues = AllCPUsMeter_updateValues,
|
||||
.defaultMode = CUSTOM_METERMODE,
|
||||
.total = 100.0,
|
||||
.attributes = CPUMeter_attributes,
|
||||
@ -389,6 +419,7 @@ const MeterClass RightCPUsMeter_class = {
|
||||
.delete = Meter_delete,
|
||||
.display = CPUMeter_display
|
||||
},
|
||||
.updateValues = AllCPUsMeter_updateValues,
|
||||
.defaultMode = CUSTOM_METERMODE,
|
||||
.total = 100.0,
|
||||
.attributes = CPUMeter_attributes,
|
||||
@ -408,6 +439,7 @@ const MeterClass LeftCPUs2Meter_class = {
|
||||
.delete = Meter_delete,
|
||||
.display = CPUMeter_display
|
||||
},
|
||||
.updateValues = AllCPUsMeter_updateValues,
|
||||
.defaultMode = CUSTOM_METERMODE,
|
||||
.total = 100.0,
|
||||
.attributes = CPUMeter_attributes,
|
||||
@ -427,6 +459,7 @@ const MeterClass RightCPUs2Meter_class = {
|
||||
.delete = Meter_delete,
|
||||
.display = CPUMeter_display
|
||||
},
|
||||
.updateValues = AllCPUsMeter_updateValues,
|
||||
.defaultMode = CUSTOM_METERMODE,
|
||||
.total = 100.0,
|
||||
.attributes = CPUMeter_attributes,
|
||||
@ -446,6 +479,7 @@ const MeterClass AllCPUs4Meter_class = {
|
||||
.delete = Meter_delete,
|
||||
.display = CPUMeter_display
|
||||
},
|
||||
.updateValues = AllCPUsMeter_updateValues,
|
||||
.defaultMode = CUSTOM_METERMODE,
|
||||
.total = 100.0,
|
||||
.attributes = CPUMeter_attributes,
|
||||
@ -465,6 +499,7 @@ const MeterClass LeftCPUs4Meter_class = {
|
||||
.delete = Meter_delete,
|
||||
.display = CPUMeter_display
|
||||
},
|
||||
.updateValues = AllCPUsMeter_updateValues,
|
||||
.defaultMode = CUSTOM_METERMODE,
|
||||
.total = 100.0,
|
||||
.attributes = CPUMeter_attributes,
|
||||
@ -484,6 +519,7 @@ const MeterClass RightCPUs4Meter_class = {
|
||||
.delete = Meter_delete,
|
||||
.display = CPUMeter_display
|
||||
},
|
||||
.updateValues = AllCPUsMeter_updateValues,
|
||||
.defaultMode = CUSTOM_METERMODE,
|
||||
.total = 100.0,
|
||||
.attributes = CPUMeter_attributes,
|
||||
@ -503,6 +539,7 @@ const MeterClass AllCPUs8Meter_class = {
|
||||
.delete = Meter_delete,
|
||||
.display = CPUMeter_display
|
||||
},
|
||||
.updateValues = AllCPUsMeter_updateValues,
|
||||
.defaultMode = CUSTOM_METERMODE,
|
||||
.total = 100.0,
|
||||
.attributes = CPUMeter_attributes,
|
||||
@ -522,6 +559,7 @@ const MeterClass LeftCPUs8Meter_class = {
|
||||
.delete = Meter_delete,
|
||||
.display = CPUMeter_display
|
||||
},
|
||||
.updateValues = AllCPUsMeter_updateValues,
|
||||
.defaultMode = CUSTOM_METERMODE,
|
||||
.total = 100.0,
|
||||
.attributes = CPUMeter_attributes,
|
||||
@ -541,6 +579,7 @@ const MeterClass RightCPUs8Meter_class = {
|
||||
.delete = Meter_delete,
|
||||
.display = CPUMeter_display
|
||||
},
|
||||
.updateValues = AllCPUsMeter_updateValues,
|
||||
.defaultMode = CUSTOM_METERMODE,
|
||||
.total = 100.0,
|
||||
.attributes = CPUMeter_attributes,
|
||||
|
@ -9,6 +9,7 @@ in the source distribution for its full text.
|
||||
|
||||
#include "Meter.h"
|
||||
|
||||
|
||||
typedef enum {
|
||||
CPU_METER_NICE = 0,
|
||||
CPU_METER_NORMAL = 1,
|
||||
|
315
CRT.c
315
CRT.c
@ -10,6 +10,7 @@ in the source distribution for its full text.
|
||||
#include "CRT.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <langinfo.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
@ -24,6 +25,10 @@ in the source distribution for its full text.
|
||||
#include <execinfo.h>
|
||||
#endif
|
||||
|
||||
#if !defined(NDEBUG) && defined(HAVE_MEMFD_CREATE)
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
|
||||
#define ColorIndex(i,j) ((7-(i))*8+(j))
|
||||
|
||||
@ -76,6 +81,7 @@ bool CRT_utf8 = false;
|
||||
|
||||
const char* const* CRT_treeStr = CRT_treeStrAscii;
|
||||
|
||||
static const Settings* CRT_crashSettings;
|
||||
static const int* CRT_delay;
|
||||
|
||||
const char* CRT_degreeSign;
|
||||
@ -114,6 +120,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
|
||||
[UPTIME] = A_BOLD | ColorPair(Cyan, Black),
|
||||
[BATTERY] = A_BOLD | ColorPair(Cyan, Black),
|
||||
[LARGE_NUMBER] = A_BOLD | ColorPair(Red, Black),
|
||||
[METER_SHADOW] = A_BOLD | ColorPairGrayBlack,
|
||||
[METER_TEXT] = ColorPair(Cyan, Black),
|
||||
[METER_VALUE] = A_BOLD | ColorPair(Cyan, Black),
|
||||
[METER_VALUE_ERROR] = A_BOLD | ColorPair(Red, Black),
|
||||
@ -144,17 +151,24 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
|
||||
[BAR_BORDER] = A_BOLD,
|
||||
[BAR_SHADOW] = A_BOLD | ColorPairGrayBlack,
|
||||
[SWAP] = ColorPair(Red, Black),
|
||||
[SWAP_CACHE] = ColorPair(Yellow, Black),
|
||||
[GRAPH_1] = A_BOLD | ColorPair(Cyan, Black),
|
||||
[GRAPH_2] = ColorPair(Cyan, Black),
|
||||
[MEMORY_USED] = ColorPair(Green, Black),
|
||||
[MEMORY_BUFFERS] = ColorPair(Blue, Black),
|
||||
[MEMORY_BUFFERS_TEXT] = A_BOLD | ColorPair(Blue, Black),
|
||||
[MEMORY_CACHE] = ColorPair(Yellow, Black),
|
||||
[MEMORY_SHARED] = ColorPair(Magenta, Black),
|
||||
[HUGEPAGE_1] = ColorPair(Green, Black),
|
||||
[HUGEPAGE_2] = ColorPair(Yellow, Black),
|
||||
[HUGEPAGE_3] = ColorPair(Red, Black),
|
||||
[HUGEPAGE_4] = ColorPair(Blue, Black),
|
||||
[LOAD_AVERAGE_FIFTEEN] = ColorPair(Cyan, Black),
|
||||
[LOAD_AVERAGE_FIVE] = A_BOLD | ColorPair(Cyan, Black),
|
||||
[LOAD_AVERAGE_ONE] = A_BOLD | ColorPair(White, Black),
|
||||
[LOAD] = A_BOLD,
|
||||
[HELP_BOLD] = A_BOLD | ColorPair(Cyan, Black),
|
||||
[HELP_SHADOW] = A_BOLD | ColorPairGrayBlack,
|
||||
[CLOCK] = A_BOLD,
|
||||
[DATE] = A_BOLD,
|
||||
[DATETIME] = A_BOLD,
|
||||
@ -182,6 +196,15 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
|
||||
[ZFS_COMPRESSED] = ColorPair(Blue, Black),
|
||||
[ZFS_RATIO] = ColorPair(Magenta, Black),
|
||||
[ZRAM] = ColorPair(Yellow, Black),
|
||||
[DYNAMIC_GRAY] = ColorPairGrayBlack,
|
||||
[DYNAMIC_DARKGRAY] = A_BOLD | ColorPairGrayBlack,
|
||||
[DYNAMIC_RED] = ColorPair(Red, Black),
|
||||
[DYNAMIC_GREEN] = ColorPair(Green, Black),
|
||||
[DYNAMIC_BLUE] = ColorPair(Blue, Black),
|
||||
[DYNAMIC_CYAN] = ColorPair(Cyan, Black),
|
||||
[DYNAMIC_MAGENTA] = ColorPair(Magenta, Black),
|
||||
[DYNAMIC_YELLOW] = ColorPair(Yellow, Black),
|
||||
[DYNAMIC_WHITE] = ColorPair(White, Black),
|
||||
},
|
||||
[COLORSCHEME_MONOCHROME] = {
|
||||
[RESET_COLOR] = A_NORMAL,
|
||||
@ -199,6 +222,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
|
||||
[UPTIME] = A_BOLD,
|
||||
[BATTERY] = A_BOLD,
|
||||
[LARGE_NUMBER] = A_BOLD,
|
||||
[METER_SHADOW] = A_DIM,
|
||||
[METER_TEXT] = A_NORMAL,
|
||||
[METER_VALUE] = A_BOLD,
|
||||
[METER_VALUE_ERROR] = A_BOLD,
|
||||
@ -229,17 +253,24 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
|
||||
[BAR_BORDER] = A_BOLD,
|
||||
[BAR_SHADOW] = A_DIM,
|
||||
[SWAP] = A_BOLD,
|
||||
[SWAP_CACHE] = A_NORMAL,
|
||||
[GRAPH_1] = A_BOLD,
|
||||
[GRAPH_2] = A_NORMAL,
|
||||
[MEMORY_USED] = A_BOLD,
|
||||
[MEMORY_BUFFERS] = A_NORMAL,
|
||||
[MEMORY_BUFFERS_TEXT] = A_NORMAL,
|
||||
[MEMORY_CACHE] = A_NORMAL,
|
||||
[MEMORY_SHARED] = A_NORMAL,
|
||||
[HUGEPAGE_1] = A_BOLD,
|
||||
[HUGEPAGE_2] = A_NORMAL,
|
||||
[HUGEPAGE_3] = A_REVERSE | A_BOLD,
|
||||
[HUGEPAGE_4] = A_REVERSE,
|
||||
[LOAD_AVERAGE_FIFTEEN] = A_DIM,
|
||||
[LOAD_AVERAGE_FIVE] = A_NORMAL,
|
||||
[LOAD_AVERAGE_ONE] = A_BOLD,
|
||||
[LOAD] = A_BOLD,
|
||||
[HELP_BOLD] = A_BOLD,
|
||||
[HELP_SHADOW] = A_DIM,
|
||||
[CLOCK] = A_BOLD,
|
||||
[DATE] = A_BOLD,
|
||||
[DATETIME] = A_BOLD,
|
||||
@ -267,6 +298,15 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
|
||||
[ZFS_COMPRESSED] = A_BOLD,
|
||||
[ZFS_RATIO] = A_BOLD,
|
||||
[ZRAM] = A_NORMAL,
|
||||
[DYNAMIC_GRAY] = A_DIM,
|
||||
[DYNAMIC_DARKGRAY] = A_DIM,
|
||||
[DYNAMIC_RED] = A_BOLD,
|
||||
[DYNAMIC_GREEN] = A_NORMAL,
|
||||
[DYNAMIC_BLUE] = A_NORMAL,
|
||||
[DYNAMIC_CYAN] = A_BOLD,
|
||||
[DYNAMIC_MAGENTA] = A_NORMAL,
|
||||
[DYNAMIC_YELLOW] = A_NORMAL,
|
||||
[DYNAMIC_WHITE] = A_BOLD,
|
||||
},
|
||||
[COLORSCHEME_BLACKONWHITE] = {
|
||||
[RESET_COLOR] = ColorPair(Black, White),
|
||||
@ -284,6 +324,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
|
||||
[UPTIME] = ColorPair(Yellow, White),
|
||||
[BATTERY] = ColorPair(Yellow, White),
|
||||
[LARGE_NUMBER] = ColorPair(Red, White),
|
||||
[METER_SHADOW] = ColorPair(Blue, White),
|
||||
[METER_TEXT] = ColorPair(Blue, White),
|
||||
[METER_VALUE] = ColorPair(Black, White),
|
||||
[METER_VALUE_ERROR] = A_BOLD | ColorPair(Red, White),
|
||||
@ -314,17 +355,24 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
|
||||
[BAR_BORDER] = ColorPair(Blue, White),
|
||||
[BAR_SHADOW] = ColorPair(Black, White),
|
||||
[SWAP] = ColorPair(Red, White),
|
||||
[SWAP_CACHE] = ColorPair(Yellow, White),
|
||||
[GRAPH_1] = A_BOLD | ColorPair(Blue, White),
|
||||
[GRAPH_2] = ColorPair(Blue, White),
|
||||
[MEMORY_USED] = ColorPair(Green, White),
|
||||
[MEMORY_BUFFERS] = ColorPair(Cyan, White),
|
||||
[MEMORY_BUFFERS_TEXT] = ColorPair(Cyan, White),
|
||||
[MEMORY_CACHE] = ColorPair(Yellow, White),
|
||||
[MEMORY_SHARED] = ColorPair(Magenta, White),
|
||||
[HUGEPAGE_1] = ColorPair(Green, White),
|
||||
[HUGEPAGE_2] = ColorPair(Yellow, White),
|
||||
[HUGEPAGE_3] = ColorPair(Red, White),
|
||||
[HUGEPAGE_4] = ColorPair(Blue, White),
|
||||
[LOAD_AVERAGE_FIFTEEN] = ColorPair(Black, White),
|
||||
[LOAD_AVERAGE_FIVE] = ColorPair(Black, White),
|
||||
[LOAD_AVERAGE_ONE] = ColorPair(Black, White),
|
||||
[LOAD] = ColorPair(Black, White),
|
||||
[HELP_BOLD] = ColorPair(Blue, White),
|
||||
[HELP_SHADOW] = A_BOLD | ColorPair(Black, White),
|
||||
[CLOCK] = ColorPair(Black, White),
|
||||
[DATE] = ColorPair(Black, White),
|
||||
[DATETIME] = ColorPair(Black, White),
|
||||
@ -351,7 +399,16 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
|
||||
[ZFS_OTHER] = ColorPair(Magenta, White),
|
||||
[ZFS_COMPRESSED] = ColorPair(Cyan, White),
|
||||
[ZFS_RATIO] = ColorPair(Magenta, White),
|
||||
[ZRAM] = ColorPair(Yellow, White)
|
||||
[ZRAM] = ColorPair(Yellow, White),
|
||||
[DYNAMIC_GRAY] = ColorPair(Black, White),
|
||||
[DYNAMIC_DARKGRAY] = A_BOLD | ColorPair(Black, White),
|
||||
[DYNAMIC_RED] = ColorPair(Red, White),
|
||||
[DYNAMIC_GREEN] = ColorPair(Green, White),
|
||||
[DYNAMIC_BLUE] = ColorPair(Blue, White),
|
||||
[DYNAMIC_CYAN] = ColorPair(Yellow, White),
|
||||
[DYNAMIC_MAGENTA] = ColorPair(Magenta, White),
|
||||
[DYNAMIC_YELLOW] = ColorPair(Yellow, White),
|
||||
[DYNAMIC_WHITE] = A_BOLD | ColorPair(Black, White),
|
||||
},
|
||||
[COLORSCHEME_LIGHTTERMINAL] = {
|
||||
[RESET_COLOR] = ColorPair(Black, Black),
|
||||
@ -369,6 +426,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
|
||||
[UPTIME] = ColorPair(Yellow, Black),
|
||||
[BATTERY] = ColorPair(Yellow, Black),
|
||||
[LARGE_NUMBER] = ColorPair(Red, Black),
|
||||
[METER_SHADOW] = A_BOLD | ColorPairGrayBlack,
|
||||
[METER_TEXT] = ColorPair(Blue, Black),
|
||||
[METER_VALUE] = ColorPair(Black, Black),
|
||||
[METER_VALUE_ERROR] = A_BOLD | ColorPair(Red, Black),
|
||||
@ -399,17 +457,24 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
|
||||
[BAR_BORDER] = ColorPair(Blue, Black),
|
||||
[BAR_SHADOW] = ColorPairGrayBlack,
|
||||
[SWAP] = ColorPair(Red, Black),
|
||||
[SWAP_CACHE] = ColorPair(Yellow, Black),
|
||||
[GRAPH_1] = A_BOLD | ColorPair(Cyan, Black),
|
||||
[GRAPH_2] = ColorPair(Cyan, Black),
|
||||
[MEMORY_USED] = ColorPair(Green, Black),
|
||||
[MEMORY_BUFFERS] = ColorPair(Cyan, Black),
|
||||
[MEMORY_BUFFERS_TEXT] = ColorPair(Cyan, Black),
|
||||
[MEMORY_CACHE] = ColorPair(Yellow, Black),
|
||||
[MEMORY_SHARED] = ColorPair(Magenta, Black),
|
||||
[HUGEPAGE_1] = ColorPair(Green, Black),
|
||||
[HUGEPAGE_2] = ColorPair(Yellow, Black),
|
||||
[HUGEPAGE_3] = ColorPair(Red, Black),
|
||||
[HUGEPAGE_4] = ColorPair(Blue, Black),
|
||||
[LOAD_AVERAGE_FIFTEEN] = ColorPair(Black, Black),
|
||||
[LOAD_AVERAGE_FIVE] = ColorPair(Black, Black),
|
||||
[LOAD_AVERAGE_ONE] = ColorPair(Black, Black),
|
||||
[LOAD] = ColorPairWhiteDefault,
|
||||
[HELP_BOLD] = ColorPair(Blue, Black),
|
||||
[HELP_SHADOW] = A_BOLD | ColorPairGrayBlack,
|
||||
[CLOCK] = ColorPairWhiteDefault,
|
||||
[DATE] = ColorPairWhiteDefault,
|
||||
[DATETIME] = ColorPairWhiteDefault,
|
||||
@ -437,6 +502,15 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
|
||||
[ZFS_COMPRESSED] = ColorPair(Cyan, Black),
|
||||
[ZFS_RATIO] = A_BOLD | ColorPair(Magenta, Black),
|
||||
[ZRAM] = ColorPair(Yellow, Black),
|
||||
[DYNAMIC_GRAY] = ColorPairGrayBlack,
|
||||
[DYNAMIC_DARKGRAY] = A_BOLD | ColorPairGrayBlack,
|
||||
[DYNAMIC_RED] = ColorPair(Red, Black),
|
||||
[DYNAMIC_GREEN] = ColorPair(Green, Black),
|
||||
[DYNAMIC_BLUE] = ColorPair(Blue, Black),
|
||||
[DYNAMIC_CYAN] = ColorPair(Cyan, Black),
|
||||
[DYNAMIC_MAGENTA] = ColorPair(Magenta, Black),
|
||||
[DYNAMIC_YELLOW] = ColorPair(Yellow, Black),
|
||||
[DYNAMIC_WHITE] = ColorPairWhiteDefault,
|
||||
},
|
||||
[COLORSCHEME_MIDNIGHT] = {
|
||||
[RESET_COLOR] = ColorPair(White, Blue),
|
||||
@ -454,6 +528,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
|
||||
[UPTIME] = A_BOLD | ColorPair(Yellow, Blue),
|
||||
[BATTERY] = A_BOLD | ColorPair(Yellow, Blue),
|
||||
[LARGE_NUMBER] = A_BOLD | ColorPair(Red, Blue),
|
||||
[METER_SHADOW] = ColorPair(Cyan, Blue),
|
||||
[METER_TEXT] = ColorPair(Cyan, Blue),
|
||||
[METER_VALUE] = A_BOLD | ColorPair(Cyan, Blue),
|
||||
[METER_VALUE_ERROR] = A_BOLD | ColorPair(Red, Blue),
|
||||
@ -484,17 +559,24 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
|
||||
[BAR_BORDER] = A_BOLD | ColorPair(Yellow, Blue),
|
||||
[BAR_SHADOW] = ColorPair(Cyan, Blue),
|
||||
[SWAP] = ColorPair(Red, Blue),
|
||||
[SWAP_CACHE] = A_BOLD | ColorPair(Yellow, Blue),
|
||||
[GRAPH_1] = A_BOLD | ColorPair(Cyan, Blue),
|
||||
[GRAPH_2] = ColorPair(Cyan, Blue),
|
||||
[MEMORY_USED] = A_BOLD | ColorPair(Green, Blue),
|
||||
[MEMORY_BUFFERS] = A_BOLD | ColorPair(Cyan, Blue),
|
||||
[MEMORY_BUFFERS_TEXT] = A_BOLD | ColorPair(Cyan, Blue),
|
||||
[MEMORY_CACHE] = A_BOLD | ColorPair(Yellow, Blue),
|
||||
[MEMORY_SHARED] = A_BOLD | ColorPair(Magenta, Blue),
|
||||
[HUGEPAGE_1] = A_BOLD | ColorPair(Green, Blue),
|
||||
[HUGEPAGE_2] = A_BOLD | ColorPair(Yellow, Blue),
|
||||
[HUGEPAGE_3] = A_BOLD | ColorPair(Red, Blue),
|
||||
[HUGEPAGE_4] = A_BOLD | ColorPair(White, Blue),
|
||||
[LOAD_AVERAGE_FIFTEEN] = A_BOLD | ColorPair(Black, Blue),
|
||||
[LOAD_AVERAGE_FIVE] = A_NORMAL | ColorPair(White, Blue),
|
||||
[LOAD_AVERAGE_ONE] = A_BOLD | ColorPair(White, Blue),
|
||||
[LOAD] = A_BOLD | ColorPair(White, Blue),
|
||||
[HELP_BOLD] = A_BOLD | ColorPair(Cyan, Blue),
|
||||
[HELP_SHADOW] = A_BOLD | ColorPair(Black, Blue),
|
||||
[CLOCK] = ColorPair(White, Blue),
|
||||
[DATE] = ColorPair(White, Blue),
|
||||
[DATETIME] = ColorPair(White, Blue),
|
||||
@ -522,6 +604,15 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
|
||||
[ZFS_COMPRESSED] = A_BOLD | ColorPair(White, Blue),
|
||||
[ZFS_RATIO] = A_BOLD | ColorPair(Magenta, Blue),
|
||||
[ZRAM] = A_BOLD | ColorPair(Yellow, Blue),
|
||||
[DYNAMIC_GRAY] = ColorPairGrayBlack,
|
||||
[DYNAMIC_DARKGRAY] = A_BOLD | ColorPairGrayBlack,
|
||||
[DYNAMIC_RED] = ColorPair(Red, Blue),
|
||||
[DYNAMIC_GREEN] = ColorPair(Green, Blue),
|
||||
[DYNAMIC_BLUE] = ColorPair(Black, Blue),
|
||||
[DYNAMIC_CYAN] = ColorPair(Cyan, Blue),
|
||||
[DYNAMIC_MAGENTA] = ColorPair(Magenta, Blue),
|
||||
[DYNAMIC_YELLOW] = ColorPair(Yellow, Blue),
|
||||
[DYNAMIC_WHITE] = ColorPair(White, Blue),
|
||||
},
|
||||
[COLORSCHEME_BLACKNIGHT] = {
|
||||
[RESET_COLOR] = ColorPair(Cyan, Black),
|
||||
@ -539,6 +630,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
|
||||
[UPTIME] = ColorPair(Green, Black),
|
||||
[BATTERY] = ColorPair(Green, Black),
|
||||
[LARGE_NUMBER] = A_BOLD | ColorPair(Red, Black),
|
||||
[METER_SHADOW] = A_BOLD | ColorPairGrayBlack,
|
||||
[METER_TEXT] = ColorPair(Cyan, Black),
|
||||
[METER_VALUE] = ColorPair(Green, Black),
|
||||
[METER_VALUE_ERROR] = A_BOLD | ColorPair(Red, Black),
|
||||
@ -569,17 +661,24 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
|
||||
[BAR_BORDER] = A_BOLD | ColorPair(Green, Black),
|
||||
[BAR_SHADOW] = ColorPair(Cyan, Black),
|
||||
[SWAP] = ColorPair(Red, Black),
|
||||
[SWAP_CACHE] = ColorPair(Yellow, Black),
|
||||
[GRAPH_1] = A_BOLD | ColorPair(Green, Black),
|
||||
[GRAPH_2] = ColorPair(Green, Black),
|
||||
[MEMORY_USED] = ColorPair(Green, Black),
|
||||
[MEMORY_BUFFERS] = ColorPair(Blue, Black),
|
||||
[MEMORY_BUFFERS_TEXT] = A_BOLD | ColorPair(Blue, Black),
|
||||
[MEMORY_CACHE] = ColorPair(Yellow, Black),
|
||||
[MEMORY_SHARED] = ColorPair(Magenta, Black),
|
||||
[HUGEPAGE_1] = ColorPair(Green, Black),
|
||||
[HUGEPAGE_2] = ColorPair(Yellow, Black),
|
||||
[HUGEPAGE_3] = ColorPair(Red, Black),
|
||||
[HUGEPAGE_4] = ColorPair(Blue, Black),
|
||||
[LOAD_AVERAGE_FIFTEEN] = ColorPair(Green, Black),
|
||||
[LOAD_AVERAGE_FIVE] = ColorPair(Green, Black),
|
||||
[LOAD_AVERAGE_ONE] = A_BOLD | ColorPair(Green, Black),
|
||||
[LOAD] = A_BOLD,
|
||||
[HELP_BOLD] = A_BOLD | ColorPair(Cyan, Black),
|
||||
[HELP_SHADOW] = A_BOLD | ColorPairGrayBlack,
|
||||
[CLOCK] = ColorPair(Green, Black),
|
||||
[CHECK_BOX] = ColorPair(Green, Black),
|
||||
[CHECK_MARK] = A_BOLD | ColorPair(Green, Black),
|
||||
@ -605,6 +704,15 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
|
||||
[ZFS_COMPRESSED] = ColorPair(Blue, Black),
|
||||
[ZFS_RATIO] = ColorPair(Magenta, Black),
|
||||
[ZRAM] = ColorPair(Yellow, Black),
|
||||
[DYNAMIC_GRAY] = ColorPairGrayBlack,
|
||||
[DYNAMIC_DARKGRAY] = A_BOLD | ColorPairGrayBlack,
|
||||
[DYNAMIC_RED] = ColorPair(Red, Black),
|
||||
[DYNAMIC_GREEN] = ColorPair(Green, Black),
|
||||
[DYNAMIC_BLUE] = ColorPair(Blue, Black),
|
||||
[DYNAMIC_CYAN] = ColorPair(Cyan, Black),
|
||||
[DYNAMIC_MAGENTA] = ColorPair(Magenta, Black),
|
||||
[DYNAMIC_YELLOW] = ColorPair(Yellow, Black),
|
||||
[DYNAMIC_WHITE] = ColorPair(White, Black),
|
||||
},
|
||||
[COLORSCHEME_BROKENGRAY] = { 0 } // dynamically generated.
|
||||
};
|
||||
@ -618,53 +726,139 @@ int CRT_scrollWheelVAmount = 10;
|
||||
ColorScheme CRT_colorScheme = COLORSCHEME_DEFAULT;
|
||||
|
||||
ATTR_NORETURN
|
||||
static void CRT_handleSIGTERM(int sgn) {
|
||||
(void) sgn;
|
||||
static void CRT_handleSIGTERM(ATTR_UNUSED int sgn) {
|
||||
CRT_done();
|
||||
exit(0);
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
#ifdef HAVE_SETUID_ENABLED
|
||||
#ifndef NDEBUG
|
||||
|
||||
static int CRT_euid = -1;
|
||||
static int stderrRedirectNewFd = -1;
|
||||
static int stderrRedirectBackupFd = -1;
|
||||
|
||||
static int CRT_egid = -1;
|
||||
static int createStderrCacheFile(void) {
|
||||
#if defined(HAVE_MEMFD_CREATE)
|
||||
return memfd_create("htop.stderr-redirect", 0);
|
||||
#elif defined(O_TMPFILE)
|
||||
return open("/tmp", O_TMPFILE | O_CREAT | O_EXCL | O_RDWR, S_IRUSR | S_IWUSR);
|
||||
#else
|
||||
char tmpName[] = "htop.stderr-redirectXXXXXX";
|
||||
mode_t curUmask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
|
||||
int r = mkstemp(tmpName);
|
||||
umask(curUmask);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
void CRT_dropPrivileges() {
|
||||
CRT_egid = getegid();
|
||||
CRT_euid = geteuid();
|
||||
if (setegid(getgid()) == -1) {
|
||||
CRT_fatalError("Fatal error: failed dropping group privileges");
|
||||
}
|
||||
if (seteuid(getuid()) == -1) {
|
||||
CRT_fatalError("Fatal error: failed dropping user privileges");
|
||||
}
|
||||
(void) unlink(tmpName);
|
||||
|
||||
return r;
|
||||
#endif /* HAVE_MEMFD_CREATE */
|
||||
}
|
||||
|
||||
void CRT_restorePrivileges() {
|
||||
if (CRT_egid == -1 || CRT_euid == -1) {
|
||||
CRT_fatalError("Fatal error: internal inconsistency");
|
||||
}
|
||||
if (setegid(CRT_egid) == -1) {
|
||||
CRT_fatalError("Fatal error: failed restoring group privileges");
|
||||
}
|
||||
if (seteuid(CRT_euid) == -1) {
|
||||
CRT_fatalError("Fatal error: failed restoring user privileges");
|
||||
static void redirectStderr(void) {
|
||||
stderrRedirectNewFd = createStderrCacheFile();
|
||||
if (stderrRedirectNewFd < 0) {
|
||||
/* ignore failure */
|
||||
return;
|
||||
}
|
||||
|
||||
stderrRedirectBackupFd = dup(STDERR_FILENO);
|
||||
dup2(stderrRedirectNewFd, STDERR_FILENO);
|
||||
}
|
||||
|
||||
#endif /* HAVE_SETUID_ENABLED */
|
||||
static void dumpStderr(void) {
|
||||
if (stderrRedirectNewFd < 0)
|
||||
return;
|
||||
|
||||
fsync(STDERR_FILENO);
|
||||
dup2(stderrRedirectBackupFd, STDERR_FILENO);
|
||||
lseek(stderrRedirectNewFd, 0, SEEK_SET);
|
||||
|
||||
bool header = false;
|
||||
char buffer[8192];
|
||||
for (;;) {
|
||||
errno = 0;
|
||||
ssize_t res = read(stderrRedirectNewFd, buffer, sizeof(buffer));
|
||||
if (res < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (res == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (res > 0) {
|
||||
if (!header) {
|
||||
fprintf(stderr, ">>>>>>>>>> stderr output >>>>>>>>>>\n");
|
||||
header = true;
|
||||
}
|
||||
(void)! write(STDERR_FILENO, buffer, res);
|
||||
}
|
||||
}
|
||||
|
||||
if (header)
|
||||
fprintf(stderr, "\n<<<<<<<<<< stderr output <<<<<<<<<<\n");
|
||||
|
||||
close(stderrRedirectNewFd);
|
||||
stderrRedirectNewFd = -1;
|
||||
}
|
||||
|
||||
#else /* !NDEBUG */
|
||||
|
||||
static void redirectStderr(void) {
|
||||
}
|
||||
|
||||
static void dumpStderr(void) {
|
||||
}
|
||||
|
||||
#endif /* !NDEBUG */
|
||||
|
||||
static struct sigaction old_sig_handler[32];
|
||||
|
||||
// TODO: pass an instance of Settings instead.
|
||||
static void CRT_installSignalHandlers(void) {
|
||||
struct sigaction act;
|
||||
sigemptyset (&act.sa_mask);
|
||||
act.sa_flags = (int)SA_RESETHAND | SA_NODEFER;
|
||||
act.sa_handler = CRT_handleSIGSEGV;
|
||||
sigaction (SIGSEGV, &act, &old_sig_handler[SIGSEGV]);
|
||||
sigaction (SIGFPE, &act, &old_sig_handler[SIGFPE]);
|
||||
sigaction (SIGILL, &act, &old_sig_handler[SIGILL]);
|
||||
sigaction (SIGBUS, &act, &old_sig_handler[SIGBUS]);
|
||||
sigaction (SIGPIPE, &act, &old_sig_handler[SIGPIPE]);
|
||||
sigaction (SIGSYS, &act, &old_sig_handler[SIGSYS]);
|
||||
sigaction (SIGABRT, &act, &old_sig_handler[SIGABRT]);
|
||||
|
||||
signal(SIGINT, CRT_handleSIGTERM);
|
||||
signal(SIGTERM, CRT_handleSIGTERM);
|
||||
signal(SIGQUIT, CRT_handleSIGTERM);
|
||||
}
|
||||
|
||||
void CRT_resetSignalHandlers(void) {
|
||||
sigaction (SIGSEGV, &old_sig_handler[SIGSEGV], NULL);
|
||||
sigaction (SIGFPE, &old_sig_handler[SIGFPE], NULL);
|
||||
sigaction (SIGILL, &old_sig_handler[SIGILL], NULL);
|
||||
sigaction (SIGBUS, &old_sig_handler[SIGBUS], NULL);
|
||||
sigaction (SIGPIPE, &old_sig_handler[SIGPIPE], NULL);
|
||||
sigaction (SIGSYS, &old_sig_handler[SIGSYS], NULL);
|
||||
sigaction (SIGABRT, &old_sig_handler[SIGABRT], NULL);
|
||||
|
||||
signal(SIGINT, SIG_DFL);
|
||||
signal(SIGTERM, SIG_DFL);
|
||||
signal(SIGQUIT, SIG_DFL);
|
||||
}
|
||||
|
||||
void CRT_init(const Settings* settings, bool allowUnicode) {
|
||||
redirectStderr();
|
||||
|
||||
void CRT_init(const int* delay, int colorScheme, bool allowUnicode) {
|
||||
initscr();
|
||||
noecho();
|
||||
CRT_delay = delay;
|
||||
CRT_colors = CRT_colorSchemes[colorScheme];
|
||||
CRT_colorScheme = colorScheme;
|
||||
CRT_crashSettings = settings;
|
||||
CRT_delay = &(settings->delay);
|
||||
CRT_colors = CRT_colorSchemes[settings->colorScheme];
|
||||
CRT_colorScheme = settings->colorScheme;
|
||||
|
||||
for (int i = 0; i < LAST_COLORELEMENT; i++) {
|
||||
unsigned int color = CRT_colorSchemes[COLORSCHEME_DEFAULT][i];
|
||||
@ -675,7 +869,9 @@ void CRT_init(const int* delay, int colorScheme, bool allowUnicode) {
|
||||
nonl();
|
||||
intrflush(stdscr, false);
|
||||
keypad(stdscr, true);
|
||||
#ifdef HAVE_GETMOUSE
|
||||
mouseinterval(0);
|
||||
#endif
|
||||
curs_set(0);
|
||||
|
||||
if (has_colors()) {
|
||||
@ -698,6 +894,7 @@ void CRT_init(const int* delay, int colorScheme, bool allowUnicode) {
|
||||
define_key("\033OQ", KEY_F(2));
|
||||
define_key("\033OR", KEY_F(3));
|
||||
define_key("\033OS", KEY_F(4));
|
||||
define_key("\033O2R", KEY_F(15));
|
||||
define_key("\033[11~", KEY_F(1));
|
||||
define_key("\033[12~", KEY_F(2));
|
||||
define_key("\033[13~", KEY_F(3));
|
||||
@ -711,20 +908,7 @@ void CRT_init(const int* delay, int colorScheme, bool allowUnicode) {
|
||||
}
|
||||
}
|
||||
|
||||
struct sigaction act;
|
||||
sigemptyset (&act.sa_mask);
|
||||
act.sa_flags = (int)SA_RESETHAND | SA_NODEFER;
|
||||
act.sa_handler = CRT_handleSIGSEGV;
|
||||
sigaction (SIGSEGV, &act, &old_sig_handler[SIGSEGV]);
|
||||
sigaction (SIGFPE, &act, &old_sig_handler[SIGFPE]);
|
||||
sigaction (SIGILL, &act, &old_sig_handler[SIGILL]);
|
||||
sigaction (SIGBUS, &act, &old_sig_handler[SIGBUS]);
|
||||
sigaction (SIGPIPE, &act, &old_sig_handler[SIGPIPE]);
|
||||
sigaction (SIGSYS, &act, &old_sig_handler[SIGSYS]);
|
||||
sigaction (SIGABRT, &act, &old_sig_handler[SIGABRT]);
|
||||
|
||||
signal(SIGTERM, CRT_handleSIGTERM);
|
||||
signal(SIGQUIT, CRT_handleSIGTERM);
|
||||
CRT_installSignalHandlers();
|
||||
|
||||
use_default_colors();
|
||||
if (!has_colors())
|
||||
@ -747,10 +931,12 @@ void CRT_init(const int* delay, int colorScheme, bool allowUnicode) {
|
||||
#endif
|
||||
CRT_treeStrAscii;
|
||||
|
||||
#ifdef HAVE_GETMOUSE
|
||||
#if NCURSES_MOUSE_VERSION > 1
|
||||
mousemask(BUTTON1_RELEASED | BUTTON4_PRESSED | BUTTON5_PRESSED, NULL);
|
||||
#else
|
||||
mousemask(BUTTON1_RELEASED, NULL);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
CRT_degreeSign = initDegreeSign();
|
||||
@ -759,6 +945,8 @@ void CRT_init(const int* delay, int colorScheme, bool allowUnicode) {
|
||||
void CRT_done() {
|
||||
curs_set(1);
|
||||
endwin();
|
||||
|
||||
dumpStderr();
|
||||
}
|
||||
|
||||
void CRT_fatalError(const char* note) {
|
||||
@ -818,15 +1006,14 @@ void CRT_handleSIGSEGV(int signal) {
|
||||
"============================\n"
|
||||
"Please check at https://htop.dev/issues whether this issue has already been reported.\n"
|
||||
"If no similar issue has been reported before, please create a new issue with the following information:\n"
|
||||
"\n"
|
||||
"- Your htop version (htop --version)\n"
|
||||
"- Your OS and kernel version (uname -a)\n"
|
||||
"- Your distribution and release (lsb_release -a)\n"
|
||||
"- Likely steps to reproduce (How did it happened?)\n"
|
||||
" - Your "PACKAGE" version: '"VERSION"'\n"
|
||||
" - Your OS and kernel version (uname -a)\n"
|
||||
" - Your distribution and release (lsb_release -a)\n"
|
||||
" - Likely steps to reproduce (How did it happen?)\n"
|
||||
);
|
||||
|
||||
#ifdef HAVE_EXECINFO_H
|
||||
fprintf(stderr, "- Backtrace of the issue (see below)\n");
|
||||
fprintf(stderr, " - Backtrace of the issue (see below)\n");
|
||||
#endif
|
||||
|
||||
fprintf(stderr,
|
||||
@ -845,46 +1032,46 @@ void CRT_handleSIGSEGV(int signal) {
|
||||
signal, signal_str
|
||||
);
|
||||
|
||||
fprintf(stderr,
|
||||
"Setting information:\n"
|
||||
"--------------------\n");
|
||||
Settings_write(CRT_crashSettings, true);
|
||||
fprintf(stderr, "\n\n");
|
||||
|
||||
#ifdef HAVE_EXECINFO_H
|
||||
fprintf(stderr,
|
||||
"Backtrace information:\n"
|
||||
"----------------------\n"
|
||||
"The following function calls were active when the issue was detected:\n"
|
||||
"---\n"
|
||||
);
|
||||
|
||||
void *backtraceArray[256];
|
||||
void* backtraceArray[256];
|
||||
|
||||
size_t size = backtrace(backtraceArray, ARRAYSIZE(backtraceArray));
|
||||
backtrace_symbols_fd(backtraceArray, size, 2);
|
||||
backtrace_symbols_fd(backtraceArray, size, STDERR_FILENO);
|
||||
fprintf(stderr,
|
||||
"---\n"
|
||||
"\n"
|
||||
"To make the above information more practical to work with,\n"
|
||||
"you should provide a disassembly of your binary.\n"
|
||||
"To make the above information more practical to work with, "
|
||||
"please also provide a disassembly of your "PACKAGE" binary. "
|
||||
"This can usually be done by running the following command:\n"
|
||||
"\n"
|
||||
);
|
||||
|
||||
#ifdef HTOP_DARWIN
|
||||
fprintf(stderr, " otool -tvV `which htop` > ~/htop.otool\n");
|
||||
fprintf(stderr, " otool -tvV `which "PACKAGE"` > ~/htop.otool\n");
|
||||
#else
|
||||
fprintf(stderr, " objdump -d -S -w `which htop` > ~/htop.objdump\n");
|
||||
fprintf(stderr, " objdump -d -S -w `which "PACKAGE"` > ~/htop.objdump\n");
|
||||
#endif
|
||||
|
||||
fprintf(stderr,
|
||||
"\n"
|
||||
"Please include the generated file in your report.\n"
|
||||
"\n"
|
||||
);
|
||||
#endif
|
||||
|
||||
fprintf(stderr,
|
||||
"Running this program with debug symbols or inside a debugger may provide further insights.\n"
|
||||
"\n"
|
||||
"Thank you for helping to improve htop!\n"
|
||||
"\n"
|
||||
"htop " VERSION " aborting.\n"
|
||||
"Thank you for helping to improve "PACKAGE"!\n"
|
||||
"\n"
|
||||
);
|
||||
|
||||
|
36
CRT.h
36
CRT.h
@ -13,6 +13,7 @@ in the source distribution for its full text.
|
||||
|
||||
#include "Macros.h"
|
||||
#include "ProvideCurses.h"
|
||||
#include "Settings.h"
|
||||
|
||||
|
||||
typedef enum TreeStr_ {
|
||||
@ -52,6 +53,7 @@ typedef enum ColorElements_ {
|
||||
PANEL_SELECTION_FOLLOW,
|
||||
PANEL_SELECTION_UNFOCUS,
|
||||
LARGE_NUMBER,
|
||||
METER_SHADOW,
|
||||
METER_TEXT,
|
||||
METER_VALUE,
|
||||
METER_VALUE_ERROR,
|
||||
@ -65,6 +67,7 @@ typedef enum ColorElements_ {
|
||||
BATTERY,
|
||||
TASKS_RUNNING,
|
||||
SWAP,
|
||||
SWAP_CACHE,
|
||||
PROCESS,
|
||||
PROCESS_SHADOW,
|
||||
PROCESS_TAG,
|
||||
@ -90,6 +93,11 @@ typedef enum ColorElements_ {
|
||||
MEMORY_BUFFERS,
|
||||
MEMORY_BUFFERS_TEXT,
|
||||
MEMORY_CACHE,
|
||||
MEMORY_SHARED,
|
||||
HUGEPAGE_1,
|
||||
HUGEPAGE_2,
|
||||
HUGEPAGE_3,
|
||||
HUGEPAGE_4,
|
||||
LOAD,
|
||||
LOAD_AVERAGE_FIFTEEN,
|
||||
LOAD_AVERAGE_FIVE,
|
||||
@ -101,6 +109,7 @@ typedef enum ColorElements_ {
|
||||
DATE,
|
||||
DATETIME,
|
||||
HELP_BOLD,
|
||||
HELP_SHADOW,
|
||||
HOSTNAME,
|
||||
CPU_NICE,
|
||||
CPU_NICE_TEXT,
|
||||
@ -122,6 +131,15 @@ typedef enum ColorElements_ {
|
||||
ZFS_COMPRESSED,
|
||||
ZFS_RATIO,
|
||||
ZRAM,
|
||||
DYNAMIC_GRAY,
|
||||
DYNAMIC_DARKGRAY,
|
||||
DYNAMIC_RED,
|
||||
DYNAMIC_GREEN,
|
||||
DYNAMIC_BLUE,
|
||||
DYNAMIC_CYAN,
|
||||
DYNAMIC_MAGENTA,
|
||||
DYNAMIC_YELLOW,
|
||||
DYNAMIC_WHITE,
|
||||
LAST_COLORELEMENT
|
||||
} ColorElements;
|
||||
|
||||
@ -154,24 +172,12 @@ extern int CRT_scrollWheelVAmount;
|
||||
|
||||
extern ColorScheme CRT_colorScheme;
|
||||
|
||||
#ifdef HAVE_SETUID_ENABLED
|
||||
|
||||
void CRT_dropPrivileges(void);
|
||||
|
||||
void CRT_restorePrivileges(void);
|
||||
|
||||
#else /* HAVE_SETUID_ENABLED */
|
||||
|
||||
/* Turn setuid operations into NOPs */
|
||||
static inline void CRT_dropPrivileges(void) { }
|
||||
static inline void CRT_restorePrivileges(void) { }
|
||||
|
||||
#endif /* HAVE_SETUID_ENABLED */
|
||||
|
||||
void CRT_init(const int* delay, int colorScheme, bool allowUnicode);
|
||||
void CRT_init(const Settings* settings, bool allowUnicode);
|
||||
|
||||
void CRT_done(void);
|
||||
|
||||
void CRT_resetSignalHandlers(void);
|
||||
|
||||
int CRT_readKey(void);
|
||||
|
||||
void CRT_disableDelay(void);
|
||||
|
@ -17,11 +17,16 @@ in the source distribution for its full text.
|
||||
#include "ColumnsPanel.h"
|
||||
#include "DisplayOptionsPanel.h"
|
||||
#include "FunctionBar.h"
|
||||
#include "Header.h"
|
||||
#include "HeaderLayout.h"
|
||||
#include "HeaderOptionsPanel.h"
|
||||
#include "ListItem.h"
|
||||
#include "Macros.h"
|
||||
#include "MetersPanel.h"
|
||||
#include "Object.h"
|
||||
#include "ProvideCurses.h"
|
||||
#include "Vector.h"
|
||||
#include "XUtils.h"
|
||||
|
||||
|
||||
static const char* const CategoriesFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL};
|
||||
@ -33,14 +38,24 @@ static void CategoriesPanel_delete(Object* object) {
|
||||
free(this);
|
||||
}
|
||||
|
||||
void CategoriesPanel_makeMetersPage(CategoriesPanel* this) {
|
||||
MetersPanel* leftMeters = MetersPanel_new(this->settings, "Left column", this->header->columns[0], this->scr);
|
||||
MetersPanel* rightMeters = MetersPanel_new(this->settings, "Right column", this->header->columns[1], this->scr);
|
||||
leftMeters->rightNeighbor = rightMeters;
|
||||
rightMeters->leftNeighbor = leftMeters;
|
||||
Panel* availableMeters = (Panel*) AvailableMetersPanel_new(this->settings, this->header, (Panel*) leftMeters, (Panel*) rightMeters, this->scr, this->pl);
|
||||
ScreenManager_add(this->scr, (Panel*) leftMeters, 20);
|
||||
ScreenManager_add(this->scr, (Panel*) rightMeters, 20);
|
||||
static void CategoriesPanel_makeMetersPage(CategoriesPanel* this) {
|
||||
size_t columns = HeaderLayout_getColumns(this->scr->header->headerLayout);
|
||||
MetersPanel** meterPanels = xMallocArray(columns, sizeof(MetersPanel));
|
||||
|
||||
for (size_t i = 0; i < columns; i++) {
|
||||
char titleBuffer[32];
|
||||
xSnprintf(titleBuffer, sizeof(titleBuffer), "Column %zu", i + 1);
|
||||
meterPanels[i] = MetersPanel_new(this->settings, titleBuffer, this->header->columns[i], this->scr);
|
||||
|
||||
if (i != 0) {
|
||||
meterPanels[i]->leftNeighbor = meterPanels[i - 1];
|
||||
meterPanels[i - 1]->rightNeighbor = meterPanels[i];
|
||||
}
|
||||
|
||||
ScreenManager_add(this->scr, (Panel*) meterPanels[i], 20);
|
||||
}
|
||||
|
||||
Panel* availableMeters = (Panel*) AvailableMetersPanel_new(this->settings, this->header, columns, meterPanels, this->scr, this->pl);
|
||||
ScreenManager_add(this->scr, availableMeters, -1);
|
||||
}
|
||||
|
||||
@ -50,17 +65,36 @@ static void CategoriesPanel_makeDisplayOptionsPage(CategoriesPanel* this) {
|
||||
}
|
||||
|
||||
static void CategoriesPanel_makeColorsPage(CategoriesPanel* this) {
|
||||
Panel* colors = (Panel*) ColorsPanel_new(this->settings, this->scr);
|
||||
Panel* colors = (Panel*) ColorsPanel_new(this->settings);
|
||||
ScreenManager_add(this->scr, colors, -1);
|
||||
}
|
||||
|
||||
static void CategoriesPanel_makeColumnsPage(CategoriesPanel* this) {
|
||||
Panel* columns = (Panel*) ColumnsPanel_new(this->settings);
|
||||
Panel* availableColumns = (Panel*) AvailableColumnsPanel_new(columns);
|
||||
Panel* availableColumns = (Panel*) AvailableColumnsPanel_new(columns, this->settings->dynamicColumns);
|
||||
ScreenManager_add(this->scr, columns, 20);
|
||||
ScreenManager_add(this->scr, availableColumns, -1);
|
||||
}
|
||||
|
||||
static void CategoriesPanel_makeHeaderOptionsPage(CategoriesPanel* this) {
|
||||
Panel* colors = (Panel*) HeaderOptionsPanel_new(this->settings, this->scr);
|
||||
ScreenManager_add(this->scr, colors, -1);
|
||||
}
|
||||
|
||||
typedef void (* CategoriesPanel_makePageFunc)(CategoriesPanel* ref);
|
||||
typedef struct CategoriesPanelPage_ {
|
||||
const char* name;
|
||||
CategoriesPanel_makePageFunc ctor;
|
||||
} CategoriesPanelPage;
|
||||
|
||||
static const CategoriesPanelPage categoriesPanelPages[] = {
|
||||
{ .name = "Display options", .ctor = CategoriesPanel_makeDisplayOptionsPage },
|
||||
{ .name = "Header layout", .ctor = CategoriesPanel_makeHeaderOptionsPage },
|
||||
{ .name = "Meters", .ctor = CategoriesPanel_makeMetersPage },
|
||||
{ .name = "Columns", .ctor = CategoriesPanel_makeColumnsPage },
|
||||
{ .name = "Colors", .ctor = CategoriesPanel_makeColorsPage },
|
||||
};
|
||||
|
||||
static HandlerResult CategoriesPanel_eventHandler(Panel* super, int ch) {
|
||||
CategoriesPanel* this = (CategoriesPanel*) super;
|
||||
|
||||
@ -98,19 +132,8 @@ static HandlerResult CategoriesPanel_eventHandler(Panel* super, int ch) {
|
||||
for (int i = 1; i < size; i++)
|
||||
ScreenManager_remove(this->scr, 1);
|
||||
|
||||
switch (selected) {
|
||||
case 0:
|
||||
CategoriesPanel_makeMetersPage(this);
|
||||
break;
|
||||
case 1:
|
||||
CategoriesPanel_makeDisplayOptionsPage(this);
|
||||
break;
|
||||
case 2:
|
||||
CategoriesPanel_makeColorsPage(this);
|
||||
break;
|
||||
case 3:
|
||||
CategoriesPanel_makeColumnsPage(this);
|
||||
break;
|
||||
if (selected >= 0 && (size_t)selected < ARRAYSIZE(categoriesPanelPages)) {
|
||||
categoriesPanelPages[selected].ctor(this);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@ -135,9 +158,10 @@ CategoriesPanel* CategoriesPanel_new(ScreenManager* scr, Settings* settings, Hea
|
||||
this->header = header;
|
||||
this->pl = pl;
|
||||
Panel_setHeader(super, "Setup");
|
||||
Panel_add(super, (Object*) ListItem_new("Meters", 0));
|
||||
Panel_add(super, (Object*) ListItem_new("Display options", 0));
|
||||
Panel_add(super, (Object*) ListItem_new("Colors", 0));
|
||||
Panel_add(super, (Object*) ListItem_new("Columns", 0));
|
||||
for (size_t i = 0; i < ARRAYSIZE(categoriesPanelPages); i++)
|
||||
Panel_add(super, (Object*) ListItem_new(categoriesPanelPages[i].name, 0));
|
||||
|
||||
ScreenManager_add(scr, super, 16);
|
||||
categoriesPanelPages[0].ctor(this);
|
||||
return this;
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ in the source distribution for its full text.
|
||||
#include "ScreenManager.h"
|
||||
#include "Settings.h"
|
||||
|
||||
|
||||
typedef struct CategoriesPanel_ {
|
||||
Panel super;
|
||||
ScreenManager* scr;
|
||||
@ -22,8 +23,6 @@ typedef struct CategoriesPanel_ {
|
||||
ProcessList* pl;
|
||||
} CategoriesPanel;
|
||||
|
||||
void CategoriesPanel_makeMetersPage(CategoriesPanel* this);
|
||||
|
||||
extern const PanelClass CategoriesPanel_class;
|
||||
|
||||
CategoriesPanel* CategoriesPanel_new(ScreenManager* scr, Settings* settings, Header* header, ProcessList* pl);
|
||||
|
152
ChangeLog
152
ChangeLog
@ -1,3 +1,153 @@
|
||||
What's new in version 3.1.0
|
||||
|
||||
|
||||
* Updated COPYING file to remove the PLPA exemption (appendix 2)
|
||||
With this change the license is now GPLv2 without any additional wording.
|
||||
* Improved default sort ordering
|
||||
Note for users: This may lead to an inverted sort order on startup of
|
||||
htop 3.1.0 compared to previous versions.
|
||||
This is due to what is stored in your htoprc file. Solution: Press I
|
||||
(to invert sort order).
|
||||
This changed setting will be saved by htop on exit as long as it can
|
||||
write to your htoprc file.
|
||||
* The compile-time option to cater specifically for running htop as
|
||||
setuid has been removed
|
||||
* Add read-only option
|
||||
This allows htop to be run in an non-intrusive fashion where it acts only
|
||||
as a process viewer disabling all functions to manipulate system state.
|
||||
Note: This is not a security feature!
|
||||
* Move the code for handling the command line formatting related tasks
|
||||
to be shared across all platforms
|
||||
This means important features like stale binary/library highlighting
|
||||
can now be available on all supported platforms.
|
||||
* Make the EXE and COMM columns available on all platforms
|
||||
All supported platforms have the name of the executable (EXE) and a
|
||||
self-chosen thread/command name (COMM) available one way or the other.
|
||||
Moving this column to be handled as a platform-independently available
|
||||
information simplifies the markup of the command line.
|
||||
* Introduce configuration file versioning and config_reader_min_version
|
||||
Starting with this version the configuration file contains an version
|
||||
identifying the minimum version of the configuration parser needed to
|
||||
fully understand the configuration file format.
|
||||
Old configuration file formats are automatically upgraded on startup.
|
||||
* Make the configuration parser friendlier to users (thanks to Bart Bakker)
|
||||
With this change only settings that cannot be parsed properly are
|
||||
reset to their defaults.
|
||||
* Improve default display for systems with many CPUs
|
||||
* Add the process ELAPSED time column
|
||||
* Improve the process STATE column sorting
|
||||
* Reworked handling resize and redrawing of the UI
|
||||
* Fixed an issue where the LED meter mode could overflow allotted space
|
||||
* Allow text mode Meters to span empty neighbors to the right
|
||||
* Rescale graph meters when value of total changes
|
||||
(thanks to Michael Schönitzer)
|
||||
* Update generic process field display
|
||||
Usually "uninteresting" values in columns like 1 thread, nice value
|
||||
of 0, CPU and memory of 0%, idle/sleeping state, etc. are shown with
|
||||
reduced intensity (dark grey)
|
||||
* Option and key ("*") to collapse / expand all branches under PID 1
|
||||
(and PID 2 if kernel threads are shown) (thanks to Krishna Chaitanya)
|
||||
* Keep following a process when inverting the sort order, displaying
|
||||
the help screen or hiding/unhiding userland threads
|
||||
If a thread is currently selected the selection is updated to point
|
||||
to the thread's parent process. (thanks to Gonzalo, et.al.)
|
||||
* Reorder process scanning to be performed before updating the display
|
||||
of the meters in the header
|
||||
* Always check the user for a process for any changes
|
||||
This affects multiple platforms that previously didn't correctly handle
|
||||
the user field for a process to change at runtime (e.g. due to seteuid
|
||||
or similar syscalls).
|
||||
* Disable mouse option when support is unavailable
|
||||
* Support curses libraries without ncurses mouse support
|
||||
(thanks to Santhosh Raju)
|
||||
* Support offline and hot-swapping of CPUs on all platforms
|
||||
* Fix the CPU Meter for machines with more than 256 CPUs
|
||||
* Supplemented the "show updated/deleted executables" feature (red basename)
|
||||
to indicate when linked libraries were updated (yellow basename)
|
||||
* Apply the stale binary highlighting for the EXE column in addition to
|
||||
the command line field
|
||||
* Add new combined Memory and Swap meter
|
||||
* Implement bar and graph mode for NetworkIO Meter
|
||||
(thanks to Michael F. Schönitzer)
|
||||
* Rework TTY column to be more consistent across platforms
|
||||
* Make the CWD column generally available on all platforms
|
||||
(thanks to Santhosh Raju et. al.)
|
||||
* Add Performance Co-Pilot (PCP) platform support
|
||||
This is added via a separate pcp-htop(1) binary which provides remote host
|
||||
analysis, new Meters for any PCP metric and new Columns for any PCP process
|
||||
metric - see the pcp-htop(5) man page for further details.
|
||||
(thanks to Sohaib Mohamed)
|
||||
* Add Linux columns and key bindings for process autogroup identifier
|
||||
and nice value
|
||||
* Change available and used memory reporting on Linux to be based on
|
||||
MemAvailable (Kernel 3.14+) (thanks to Chris Cheney and Tomas Wido)
|
||||
* Add a new SysArchMeter showing kernel and platform information
|
||||
(thanks to ahgamut)
|
||||
* Linux memory usage explicitly treats tmpfs memory usage as shared memory
|
||||
This is to make memory used by tmpfs visible as this cannot be freed
|
||||
unlike normal filesystem cache data.
|
||||
* Exclude zram devices when calculating DiskIO on Linux
|
||||
* Use PATH lookup for systemctl in systemd meter (thanks to Scott Olson)
|
||||
* Add native platform support for NetBSD
|
||||
This allows htop to run on NetBSD without the need for active Linux
|
||||
emulation of the procfs filesystem.
|
||||
(thanks to Santhosh Raju and Nia Alarie)
|
||||
* Add NetworkIO, DiskIO, CPU frequency, and battery meter support on NetBSD
|
||||
(thanks to Nia Alarie)
|
||||
* Fix NetBSD display of in-use and cached memory (thanks to Nia Alarie)
|
||||
* Rework NetBSD CPU and memory accounting (thanks to Santhosh Raju)
|
||||
* Fix NetBSD accounting of user and kernel threads (thanks to Santhosh Raju)
|
||||
* Initial work to allow building with default libcurses on NetBSD
|
||||
(thanks to Santhosh Raju)
|
||||
* FreeBSD updates - implement process majflt and processor column values
|
||||
* Add FreeBSD support for CPU frequency and temperature
|
||||
* Fixes and cleanups for ZFS Meters and metrics
|
||||
* Correctly color the ZFS ARC ratio (thanks to Ross Williams)
|
||||
* Bugfixes related to CPU time display/calculations for darwin on M1 systems
|
||||
(thanks to Alexander Momchilov)
|
||||
* Harmonize the handling of multiple batteries across different platforms
|
||||
The system is now considered to run on AC if at least one power supply
|
||||
marked as AC is found in the system.
|
||||
Battery capacity is summed up over all batteries found.
|
||||
This also changes the old behavior that batteries reported by the
|
||||
system after the first AC adapter where sometimes ignored.
|
||||
* Correctly handle multiple batteries on Darwin
|
||||
Resolves a possible memory leak on systems with multiple batteries
|
||||
* Handle Linux Shmem being part of Cached in the MemoryMeter
|
||||
* Add SwapCached to the Linux swap meter (thanks to David Zarzycki)
|
||||
* Convert process time to days if applicable (thanks to David Zarzycki)
|
||||
* Always show the number of threads in the TaskMeter, even when threads
|
||||
are not shown in the process list
|
||||
* Fix Linux --drop-capabilities option handling
|
||||
* Correctly detect failure to initialize Linux boottime
|
||||
* Overhaul the Linux memory fields to partition them like free(1) now does
|
||||
* Improve the Linux process I/O column values
|
||||
* Rework the libsensors parsing on Linux
|
||||
* Update the MemoryMeter to display shared memory
|
||||
* Update OpenBSD platform - implement additional columns, scan LWP,
|
||||
proper markup for STATE, show CPU frequency
|
||||
* Fix the tree view on OpenBSD when hiding kernel threads
|
||||
* Remove old InfoScreen lines before re-scanning (thanks to Øystein Hiåsen)
|
||||
* Document historic naming of Light-Weight Processes column aka threads
|
||||
* Improve user interaction when the last process entry is selected
|
||||
* Draw the panel header on the TraceScreen (thanks to Youngjae Lee)
|
||||
* Add mouse wheel scroll and fix mouse selection on the InfoScreen
|
||||
(thanks to Youngjae Lee)
|
||||
* Add a HugepageMeter and subtract hugepages from normal memory
|
||||
* Display wide characters in LED meters and restore non-wide ncurses support
|
||||
* Add command line option to drop Linux capabilities
|
||||
* Support scheduler affinity on platforms beyond Linux
|
||||
* Report on any failure to write the configuration file
|
||||
* Cache stderr to be able to print assert messages
|
||||
These messages are shown in case htop terminates unexpectedly.
|
||||
* Print current settings on crash
|
||||
* Reset signal handlers on program exit
|
||||
* Add configure script option to create a static htop binary
|
||||
* Resolved longer-standing compilation issues on Solaris/Illumos
|
||||
* Check for availability of set_escdelay in configure
|
||||
(thanks to Stefan Polluks)
|
||||
* Build system updates for autotools 2.70
|
||||
|
||||
What's new in version 3.0.5
|
||||
|
||||
* BUGFIX / SECURITY: InfoScreen: fix uncontrolled format string
|
||||
@ -11,7 +161,7 @@ What's new in version 3.0.5
|
||||
* Drop usage of formatted error messages from <err.h>
|
||||
* Show arrow indicating order of sorted process column
|
||||
* Lots of plumbing around the internal Hashtable, hardening and code cleanups
|
||||
* LibSensors: add support for Ryzen CPUs (vor 5 Tagen)
|
||||
* LibSensors: add support for Ryzen CPUs
|
||||
(thanks to Matej Dian)
|
||||
* BUGFIX: Fix CPU percentage on M1 silicon Macs
|
||||
(thanks to Luke Groeninger)
|
||||
|
11
ClockMeter.c
11
ClockMeter.c
@ -10,21 +10,24 @@ in the source distribution for its full text.
|
||||
#include "ClockMeter.h"
|
||||
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "CRT.h"
|
||||
#include "Object.h"
|
||||
#include "ProcessList.h"
|
||||
|
||||
|
||||
static const int ClockMeter_attributes[] = {
|
||||
CLOCK
|
||||
};
|
||||
|
||||
static void ClockMeter_updateValues(Meter* this, char* buffer, size_t size) {
|
||||
time_t t = time(NULL);
|
||||
static void ClockMeter_updateValues(Meter* this) {
|
||||
const ProcessList* pl = this->pl;
|
||||
|
||||
struct tm result;
|
||||
struct tm* lt = localtime_r(&t, &result);
|
||||
const struct tm* lt = localtime_r(&pl->realtime.tv_sec, &result);
|
||||
this->values[0] = lt->tm_hour * 60 + lt->tm_min;
|
||||
strftime(buffer, size, "%H:%M:%S", lt);
|
||||
strftime(this->txtBuffer, sizeof(this->txtBuffer), "%H:%M:%S", lt);
|
||||
}
|
||||
|
||||
const MeterClass ClockMeter_class = {
|
||||
|
@ -9,6 +9,7 @@ in the source distribution for its full text.
|
||||
|
||||
#include "Meter.h"
|
||||
|
||||
|
||||
extern const MeterClass ClockMeter_class;
|
||||
|
||||
#endif
|
||||
|
@ -7,17 +7,16 @@ in the source distribution for its full text.
|
||||
|
||||
#include "ColorsPanel.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "CRT.h"
|
||||
#include "FunctionBar.h"
|
||||
#include "Header.h"
|
||||
#include "Macros.h"
|
||||
#include "Object.h"
|
||||
#include "OptionItem.h"
|
||||
#include "ProvideCurses.h"
|
||||
#include "RichString.h"
|
||||
#include "Vector.h"
|
||||
|
||||
|
||||
// TO ADD A NEW SCHEME:
|
||||
@ -51,7 +50,7 @@ static HandlerResult ColorsPanel_eventHandler(Panel* super, int ch) {
|
||||
ColorsPanel* this = (ColorsPanel*) super;
|
||||
|
||||
HandlerResult result = IGNORED;
|
||||
int mark = Panel_getSelectedIndex(super);
|
||||
int mark;
|
||||
|
||||
switch(ch) {
|
||||
case 0x0a:
|
||||
@ -60,6 +59,7 @@ static HandlerResult ColorsPanel_eventHandler(Panel* super, int ch) {
|
||||
case KEY_MOUSE:
|
||||
case KEY_RECLICK:
|
||||
case ' ':
|
||||
mark = Panel_getSelectedIndex(super);
|
||||
assert(mark >= 0);
|
||||
assert(mark < LAST_COLORSCHEME);
|
||||
for (int i = 0; ColorSchemeNames[i] != NULL; i++)
|
||||
@ -86,14 +86,13 @@ const PanelClass ColorsPanel_class = {
|
||||
.eventHandler = ColorsPanel_eventHandler
|
||||
};
|
||||
|
||||
ColorsPanel* ColorsPanel_new(Settings* settings, ScreenManager* scr) {
|
||||
ColorsPanel* ColorsPanel_new(Settings* settings) {
|
||||
ColorsPanel* this = AllocThis(ColorsPanel);
|
||||
Panel* super = (Panel*) this;
|
||||
FunctionBar* fuBar = FunctionBar_new(ColorsFunctions, NULL, NULL);
|
||||
Panel_init(super, 1, 1, 1, 1, Class(CheckItem), true, fuBar);
|
||||
|
||||
this->settings = settings;
|
||||
this->scr = scr;
|
||||
|
||||
assert(ARRAYSIZE(ColorSchemeNames) == LAST_COLORSCHEME + 1);
|
||||
|
||||
|
@ -8,18 +8,17 @@ in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
#include "Panel.h"
|
||||
#include "ScreenManager.h"
|
||||
#include "Settings.h"
|
||||
|
||||
|
||||
typedef struct ColorsPanel_ {
|
||||
Panel super;
|
||||
|
||||
Settings* settings;
|
||||
ScreenManager* scr;
|
||||
} ColorsPanel;
|
||||
|
||||
extern const PanelClass ColorsPanel_class;
|
||||
|
||||
ColorsPanel* ColorsPanel_new(Settings* settings, ScreenManager* scr);
|
||||
ColorsPanel* ColorsPanel_new(Settings* settings);
|
||||
|
||||
#endif
|
||||
|
@ -7,11 +7,14 @@ in the source distribution for its full text.
|
||||
|
||||
#include "ColumnsPanel.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "CRT.h"
|
||||
#include "DynamicColumn.h"
|
||||
#include "FunctionBar.h"
|
||||
#include "Hashtable.h"
|
||||
#include "ListItem.h"
|
||||
#include "Object.h"
|
||||
#include "Process.h"
|
||||
@ -115,6 +118,32 @@ const PanelClass ColumnsPanel_class = {
|
||||
.eventHandler = ColumnsPanel_eventHandler
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
Panel* super;
|
||||
unsigned int id;
|
||||
unsigned int offset;
|
||||
} DynamicIterator;
|
||||
|
||||
static void ColumnsPanel_add(Panel* super, unsigned int key, Hashtable* columns) {
|
||||
const char* name;
|
||||
if (key < LAST_PROCESSFIELD) {
|
||||
name = Process_fields[key].name;
|
||||
} else {
|
||||
const DynamicColumn* column = Hashtable_get(columns, key);
|
||||
assert(column);
|
||||
if (!column) {
|
||||
name = NULL;
|
||||
} else {
|
||||
name = column->caption ? column->caption : column->heading;
|
||||
if (!name)
|
||||
name = column->name; /* name is a mandatory field */
|
||||
}
|
||||
}
|
||||
if (name == NULL)
|
||||
name = "- ";
|
||||
Panel_add(super, (Object*) ListItem_new(name, key));
|
||||
}
|
||||
|
||||
ColumnsPanel* ColumnsPanel_new(Settings* settings) {
|
||||
ColumnsPanel* this = AllocThis(ColumnsPanel);
|
||||
Panel* super = (Panel*) this;
|
||||
@ -125,12 +154,11 @@ ColumnsPanel* ColumnsPanel_new(Settings* settings) {
|
||||
this->moving = false;
|
||||
Panel_setHeader(super, "Active Columns");
|
||||
|
||||
ProcessField* fields = this->settings->fields;
|
||||
for (; *fields; fields++) {
|
||||
if (Process_fields[*fields].name) {
|
||||
Panel_add(super, (Object*) ListItem_new(Process_fields[*fields].name, *fields));
|
||||
}
|
||||
}
|
||||
Hashtable* dynamicColumns = settings->dynamicColumns;
|
||||
const ProcessField* fields = settings->fields;
|
||||
for (; *fields; fields++)
|
||||
ColumnsPanel_add(super, *fields, dynamicColumns);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -143,7 +171,8 @@ void ColumnsPanel_update(Panel* super) {
|
||||
for (int i = 0; i < size; i++) {
|
||||
int key = ((ListItem*) Panel_get(super, i))->key;
|
||||
this->settings->fields[i] = key;
|
||||
this->settings->flags |= Process_fields[key].flags;
|
||||
if (key < LAST_PROCESSFIELD)
|
||||
this->settings->flags |= Process_fields[key].flags;
|
||||
}
|
||||
this->settings->fields[size] = 0;
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ in the source distribution for its full text.
|
||||
#include "Panel.h"
|
||||
#include "Settings.h"
|
||||
|
||||
|
||||
typedef struct ColumnsPanel_ {
|
||||
Panel super;
|
||||
|
||||
|
402
CommandLine.c
Normal file
402
CommandLine.c
Normal file
@ -0,0 +1,402 @@
|
||||
/*
|
||||
htop - CommandLine.c
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
(C) 2020-2021 htop dev team
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
#include "config.h" // IWYU pragma: keep
|
||||
|
||||
#include "CommandLine.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <getopt.h>
|
||||
#include <locale.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "Action.h"
|
||||
#include "CRT.h"
|
||||
#include "DynamicColumn.h"
|
||||
#include "DynamicMeter.h"
|
||||
#include "Hashtable.h"
|
||||
#include "Header.h"
|
||||
#include "IncSet.h"
|
||||
#include "MainPanel.h"
|
||||
#include "MetersPanel.h"
|
||||
#include "Panel.h"
|
||||
#include "Platform.h"
|
||||
#include "Process.h"
|
||||
#include "ProcessList.h"
|
||||
#include "ProvideCurses.h"
|
||||
#include "ScreenManager.h"
|
||||
#include "Settings.h"
|
||||
#include "UsersTable.h"
|
||||
#include "XUtils.h"
|
||||
|
||||
|
||||
static void printVersionFlag(const char* name) {
|
||||
printf("%s " VERSION "\n", name);
|
||||
}
|
||||
|
||||
static void printHelpFlag(const char* name) {
|
||||
printf("%s " VERSION "\n"
|
||||
COPYRIGHT "\n"
|
||||
"Released under the GNU GPLv2.\n\n"
|
||||
"-C --no-color Use a monochrome color scheme\n"
|
||||
"-d --delay=DELAY Set the delay between updates, in tenths of seconds\n"
|
||||
"-F --filter=FILTER Show only the commands matching the given filter\n"
|
||||
"-h --help Print this help screen\n"
|
||||
"-H --highlight-changes[=DELAY] Highlight new and old processes\n"
|
||||
"-M --no-mouse Disable the mouse\n"
|
||||
"-p --pid=PID[,PID,PID...] Show only the given PIDs\n"
|
||||
" --readonly Disable all system and process changing features\n"
|
||||
"-s --sort-key=COLUMN Sort by COLUMN in list view (try --sort-key=help for a list)\n"
|
||||
"-t --tree Show the tree view (can be combined with -s)\n"
|
||||
"-u --user[=USERNAME] Show only processes for a given user (or $USER)\n"
|
||||
"-U --no-unicode Do not use unicode but plain ASCII\n"
|
||||
"-V --version Print version info\n", name);
|
||||
Platform_longOptionsUsage(name);
|
||||
printf("\n"
|
||||
"Long options may be passed with a single dash.\n\n"
|
||||
"Press F1 inside %s for online help.\n"
|
||||
"See 'man %s' for more information.\n", name, name);
|
||||
}
|
||||
|
||||
// ----------------------------------------
|
||||
|
||||
typedef struct CommandLineSettings_ {
|
||||
Hashtable* pidMatchList;
|
||||
char* commFilter;
|
||||
uid_t userId;
|
||||
int sortKey;
|
||||
int delay;
|
||||
bool useColors;
|
||||
bool enableMouse;
|
||||
bool treeView;
|
||||
bool allowUnicode;
|
||||
bool highlightChanges;
|
||||
int highlightDelaySecs;
|
||||
bool readonly;
|
||||
} CommandLineSettings;
|
||||
|
||||
static CommandLineSettings parseArguments(const char* program, int argc, char** argv) {
|
||||
|
||||
CommandLineSettings flags = {
|
||||
.pidMatchList = NULL,
|
||||
.commFilter = NULL,
|
||||
.userId = (uid_t)-1, // -1 is guaranteed to be an invalid uid_t (see setreuid(2))
|
||||
.sortKey = 0,
|
||||
.delay = -1,
|
||||
.useColors = true,
|
||||
.enableMouse = true,
|
||||
.treeView = false,
|
||||
.allowUnicode = true,
|
||||
.highlightChanges = false,
|
||||
.highlightDelaySecs = -1,
|
||||
.readonly = false,
|
||||
};
|
||||
|
||||
const struct option long_opts[] =
|
||||
{
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{"version", no_argument, 0, 'V'},
|
||||
{"delay", required_argument, 0, 'd'},
|
||||
{"sort-key", required_argument, 0, 's'},
|
||||
{"user", optional_argument, 0, 'u'},
|
||||
{"no-color", no_argument, 0, 'C'},
|
||||
{"no-colour", no_argument, 0, 'C'},
|
||||
{"no-mouse", no_argument, 0, 'M'},
|
||||
{"no-unicode", no_argument, 0, 'U'},
|
||||
{"tree", no_argument, 0, 't'},
|
||||
{"pid", required_argument, 0, 'p'},
|
||||
{"filter", required_argument, 0, 'F'},
|
||||
{"highlight-changes", optional_argument, 0, 'H'},
|
||||
{"readonly", no_argument, 0, 128},
|
||||
PLATFORM_LONG_OPTIONS
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
int opt, opti = 0;
|
||||
/* Parse arguments */
|
||||
while ((opt = getopt_long(argc, argv, "hVMCs:td:u::Up:F:H::", long_opts, &opti))) {
|
||||
if (opt == EOF)
|
||||
break;
|
||||
switch (opt) {
|
||||
case 'h':
|
||||
printHelpFlag(program);
|
||||
exit(0);
|
||||
case 'V':
|
||||
printVersionFlag(program);
|
||||
exit(0);
|
||||
case 's':
|
||||
assert(optarg); /* please clang analyzer, cause optarg can be NULL in the 'u' case */
|
||||
if (String_eq(optarg, "help")) {
|
||||
for (int j = 1; j < LAST_PROCESSFIELD; j++) {
|
||||
const char* name = Process_fields[j].name;
|
||||
const char* description = Process_fields[j].description;
|
||||
if (name) printf("%19s %s\n", name, description);
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
flags.sortKey = 0;
|
||||
for (int j = 1; j < LAST_PROCESSFIELD; j++) {
|
||||
if (Process_fields[j].name == NULL)
|
||||
continue;
|
||||
if (String_eq(optarg, Process_fields[j].name)) {
|
||||
flags.sortKey = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (flags.sortKey == 0) {
|
||||
fprintf(stderr, "Error: invalid column \"%s\".\n", optarg);
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case 'd':
|
||||
if (sscanf(optarg, "%16d", &(flags.delay)) == 1) {
|
||||
if (flags.delay < 1) flags.delay = 1;
|
||||
if (flags.delay > 100) flags.delay = 100;
|
||||
} else {
|
||||
fprintf(stderr, "Error: invalid delay value \"%s\".\n", optarg);
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case 'u':
|
||||
{
|
||||
const char *username = optarg;
|
||||
if (!username && optind < argc && argv[optind] != NULL &&
|
||||
(argv[optind][0] != '\0' && argv[optind][0] != '-')) {
|
||||
username = argv[optind++];
|
||||
}
|
||||
|
||||
if (!username) {
|
||||
flags.userId = geteuid();
|
||||
} else if (!Action_setUserOnly(username, &(flags.userId))) {
|
||||
fprintf(stderr, "Error: invalid user \"%s\".\n", username);
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'C':
|
||||
flags.useColors = false;
|
||||
break;
|
||||
case 'M':
|
||||
#ifdef HAVE_GETMOUSE
|
||||
flags.enableMouse = false;
|
||||
#endif
|
||||
break;
|
||||
case 'U':
|
||||
flags.allowUnicode = false;
|
||||
break;
|
||||
case 't':
|
||||
flags.treeView = true;
|
||||
break;
|
||||
case 'p': {
|
||||
assert(optarg); /* please clang analyzer, cause optarg can be NULL in the 'u' case */
|
||||
char* argCopy = xStrdup(optarg);
|
||||
char* saveptr;
|
||||
const char* pid = strtok_r(argCopy, ",", &saveptr);
|
||||
|
||||
if (!flags.pidMatchList) {
|
||||
flags.pidMatchList = Hashtable_new(8, false);
|
||||
}
|
||||
|
||||
while(pid) {
|
||||
unsigned int num_pid = atoi(pid);
|
||||
// deepcode ignore CastIntegerToAddress: we just want a non-NUll pointer here
|
||||
Hashtable_put(flags.pidMatchList, num_pid, (void *) 1);
|
||||
pid = strtok_r(NULL, ",", &saveptr);
|
||||
}
|
||||
free(argCopy);
|
||||
|
||||
break;
|
||||
}
|
||||
case 'F': {
|
||||
assert(optarg);
|
||||
free_and_xStrdup(&flags.commFilter, optarg);
|
||||
break;
|
||||
}
|
||||
case 'H': {
|
||||
const char *delay = optarg;
|
||||
if (!delay && optind < argc && argv[optind] != NULL &&
|
||||
(argv[optind][0] != '\0' && argv[optind][0] != '-')) {
|
||||
delay = argv[optind++];
|
||||
}
|
||||
if (delay) {
|
||||
if (sscanf(delay, "%16d", &(flags.highlightDelaySecs)) == 1) {
|
||||
if (flags.highlightDelaySecs < 1)
|
||||
flags.highlightDelaySecs = 1;
|
||||
} else {
|
||||
fprintf(stderr, "Error: invalid highlight delay value \"%s\".\n", delay);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
flags.highlightChanges = true;
|
||||
break;
|
||||
}
|
||||
case 128:
|
||||
flags.readonly = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (Platform_getLongOption(opt, argc, argv) == false)
|
||||
exit(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
static void CommandLine_delay(ProcessList* pl, unsigned long millisec) {
|
||||
struct timespec req = {
|
||||
.tv_sec = 0,
|
||||
.tv_nsec = millisec * 1000000L
|
||||
};
|
||||
while (nanosleep(&req, &req) == -1)
|
||||
continue;
|
||||
Platform_gettime_realtime(&pl->realtime, &pl->realtimeMs);
|
||||
}
|
||||
|
||||
static void setCommFilter(State* state, char** commFilter) {
|
||||
ProcessList* pl = state->pl;
|
||||
IncSet* inc = state->mainPanel->inc;
|
||||
|
||||
IncSet_setFilter(inc, *commFilter);
|
||||
pl->incFilter = IncSet_filter(inc);
|
||||
|
||||
free(*commFilter);
|
||||
*commFilter = NULL;
|
||||
}
|
||||
|
||||
int CommandLine_run(const char* name, int argc, char** argv) {
|
||||
|
||||
/* initialize locale */
|
||||
const char* lc_ctype;
|
||||
if ((lc_ctype = getenv("LC_CTYPE")) || (lc_ctype = getenv("LC_ALL")))
|
||||
setlocale(LC_CTYPE, lc_ctype);
|
||||
else
|
||||
setlocale(LC_CTYPE, "");
|
||||
|
||||
CommandLineSettings flags = parseArguments(name, argc, argv);
|
||||
|
||||
if (flags.readonly)
|
||||
Settings_enableReadonly();
|
||||
|
||||
Platform_init();
|
||||
|
||||
Process_setupColumnWidths();
|
||||
|
||||
UsersTable* ut = UsersTable_new();
|
||||
Hashtable* dc = DynamicColumns_new();
|
||||
Hashtable* dm = DynamicMeters_new();
|
||||
if (!dc)
|
||||
dc = Hashtable_new(0, true);
|
||||
|
||||
ProcessList* pl = ProcessList_new(ut, dm, dc, flags.pidMatchList, flags.userId);
|
||||
|
||||
Settings* settings = Settings_new(pl->activeCPUs, dc);
|
||||
pl->settings = settings;
|
||||
|
||||
Header* header = Header_new(pl, settings, 2);
|
||||
|
||||
Header_populateFromSettings(header);
|
||||
|
||||
if (flags.delay != -1)
|
||||
settings->delay = flags.delay;
|
||||
if (!flags.useColors)
|
||||
settings->colorScheme = COLORSCHEME_MONOCHROME;
|
||||
#ifdef HAVE_GETMOUSE
|
||||
if (!flags.enableMouse)
|
||||
settings->enableMouse = false;
|
||||
#endif
|
||||
if (flags.treeView)
|
||||
settings->treeView = true;
|
||||
if (flags.highlightChanges)
|
||||
settings->highlightChanges = true;
|
||||
if (flags.highlightDelaySecs != -1)
|
||||
settings->highlightDelaySecs = flags.highlightDelaySecs;
|
||||
if (flags.sortKey > 0) {
|
||||
// -t -s <key> means "tree sorted by key"
|
||||
// -s <key> means "list sorted by key" (previous existing behavior)
|
||||
if (!flags.treeView) {
|
||||
settings->treeView = false;
|
||||
}
|
||||
Settings_setSortKey(settings, flags.sortKey);
|
||||
}
|
||||
|
||||
CRT_init(settings, flags.allowUnicode);
|
||||
|
||||
MainPanel* panel = MainPanel_new();
|
||||
ProcessList_setPanel(pl, (Panel*) panel);
|
||||
|
||||
MainPanel_updateTreeFunctions(panel, settings->treeView);
|
||||
|
||||
State state = {
|
||||
.settings = settings,
|
||||
.ut = ut,
|
||||
.pl = pl,
|
||||
.mainPanel = panel,
|
||||
.header = header,
|
||||
.pauseProcessUpdate = false,
|
||||
.hideProcessSelection = false,
|
||||
};
|
||||
|
||||
MainPanel_setState(panel, &state);
|
||||
if (flags.commFilter)
|
||||
setCommFilter(&state, &(flags.commFilter));
|
||||
|
||||
ScreenManager* scr = ScreenManager_new(header, settings, &state, true);
|
||||
ScreenManager_add(scr, (Panel*) panel, -1);
|
||||
|
||||
ProcessList_scan(pl, false);
|
||||
CommandLine_delay(pl, 75);
|
||||
ProcessList_scan(pl, false);
|
||||
|
||||
if (settings->allBranchesCollapsed)
|
||||
ProcessList_collapseAllBranches(pl);
|
||||
|
||||
ScreenManager_run(scr, NULL, NULL);
|
||||
|
||||
attron(CRT_colors[RESET_COLOR]);
|
||||
mvhline(LINES - 1, 0, ' ', COLS);
|
||||
attroff(CRT_colors[RESET_COLOR]);
|
||||
refresh();
|
||||
|
||||
Platform_done();
|
||||
|
||||
CRT_done();
|
||||
|
||||
if (settings->changed) {
|
||||
int r = Settings_write(settings, false);
|
||||
if (r < 0)
|
||||
fprintf(stderr, "Can not save configuration to %s: %s\n", settings->filename, strerror(-r));
|
||||
}
|
||||
|
||||
Header_delete(header);
|
||||
ProcessList_delete(pl);
|
||||
|
||||
ScreenManager_delete(scr);
|
||||
MetersPanel_cleanup();
|
||||
|
||||
UsersTable_delete(ut);
|
||||
|
||||
if (flags.pidMatchList)
|
||||
Hashtable_delete(flags.pidMatchList);
|
||||
|
||||
CRT_resetSignalHandlers();
|
||||
|
||||
/* Delete these last, since they can get accessed in the crash handler */
|
||||
Settings_delete(settings);
|
||||
if (dc)
|
||||
Hashtable_delete(dc);
|
||||
if (dm)
|
||||
Hashtable_delete(dm);
|
||||
|
||||
return 0;
|
||||
}
|
14
CommandLine.h
Normal file
14
CommandLine.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef HEADER_CommandLine
|
||||
#define HEADER_CommandLine
|
||||
/*
|
||||
htop - CommandLine.h
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
(C) 2020-2021 htop dev team
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
|
||||
int CommandLine_run(const char* name, int argc, char** argv);
|
||||
|
||||
#endif
|
@ -2,13 +2,13 @@
|
||||
|
||||
#include "CommandScreen.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "Macros.h"
|
||||
#include "Panel.h"
|
||||
#include "ProvideCurses.h"
|
||||
#include "XUtils.h"
|
||||
|
||||
|
||||
static void CommandScreen_scan(InfoScreen* this) {
|
||||
|
36
Compat.c
36
Compat.c
@ -11,18 +11,12 @@ in the source distribution for its full text.
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h> // IWYU pragma: keep
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h> // IWYU pragma: keep
|
||||
|
||||
#include "XUtils.h" // IWYU pragma: keep
|
||||
|
||||
#ifdef HAVE_HOST_GET_CLOCK_SERVICE
|
||||
#include <mach/clock.h>
|
||||
#include <mach/mach.h>
|
||||
#endif
|
||||
|
||||
|
||||
int Compat_faccessat(int dirfd,
|
||||
const char* pathname,
|
||||
@ -43,7 +37,7 @@ int Compat_faccessat(int dirfd,
|
||||
#endif
|
||||
|
||||
// Error out on unsupported configurations
|
||||
if (dirfd != AT_FDCWD || mode != F_OK) {
|
||||
if (dirfd != (int)AT_FDCWD || mode != F_OK) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
@ -123,31 +117,3 @@ ssize_t Compat_readlinkat(int dirfd,
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
int Compat_clock_monotonic_gettime(struct timespec *tp) {
|
||||
|
||||
#if defined(HAVE_CLOCK_GETTIME)
|
||||
|
||||
return clock_gettime(CLOCK_MONOTONIC, tp);
|
||||
|
||||
#elif defined(HAVE_HOST_GET_CLOCK_SERVICE)
|
||||
|
||||
clock_serv_t cclock;
|
||||
mach_timespec_t mts;
|
||||
|
||||
host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock);
|
||||
clock_get_time(cclock, &mts);
|
||||
mach_port_deallocate(mach_task_self(), cclock);
|
||||
|
||||
tp->tv_sec = mts.tv_sec;
|
||||
tp->tv_nsec = mts.tv_nsec;
|
||||
|
||||
return 0;
|
||||
|
||||
#else
|
||||
|
||||
#error No Compat_clock_monotonic_gettime() implementation!
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
2
Compat.h
2
Compat.h
@ -56,6 +56,4 @@ ssize_t Compat_readlinkat(int dirfd,
|
||||
char* buf,
|
||||
size_t bufsize);
|
||||
|
||||
int Compat_clock_monotonic_gettime(struct timespec *tp);
|
||||
|
||||
#endif /* HEADER_Compat */
|
||||
|
14
DateMeter.c
14
DateMeter.c
@ -10,28 +10,30 @@ in the source distribution for its full text.
|
||||
#include "DateMeter.h"
|
||||
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "CRT.h"
|
||||
#include "Object.h"
|
||||
#include "ProcessList.h"
|
||||
|
||||
|
||||
static const int DateMeter_attributes[] = {
|
||||
DATE
|
||||
};
|
||||
|
||||
static void DateMeter_updateValues(Meter* this, char* buffer, size_t size) {
|
||||
time_t t = time(NULL);
|
||||
static void DateMeter_updateValues(Meter* this) {
|
||||
const ProcessList* pl = this->pl;
|
||||
|
||||
struct tm result;
|
||||
struct tm* lt = localtime_r(&t, &result);
|
||||
const struct tm* lt = localtime_r(&pl->realtime.tv_sec, &result);
|
||||
this->values[0] = lt->tm_yday;
|
||||
int year = lt->tm_year + 1900;
|
||||
if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)) {
|
||||
this->total = 366;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
this->total = 365;
|
||||
}
|
||||
strftime(buffer, size, "%F", lt);
|
||||
strftime(this->txtBuffer, sizeof(this->txtBuffer), "%F", lt);
|
||||
}
|
||||
|
||||
const MeterClass DateMeter_class = {
|
||||
|
@ -9,6 +9,7 @@ in the source distribution for its full text.
|
||||
|
||||
#include "Meter.h"
|
||||
|
||||
|
||||
extern const MeterClass DateMeter_class;
|
||||
|
||||
#endif
|
||||
|
@ -10,28 +10,30 @@ in the source distribution for its full text.
|
||||
#include "DateTimeMeter.h"
|
||||
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "CRT.h"
|
||||
#include "Object.h"
|
||||
#include "ProcessList.h"
|
||||
|
||||
|
||||
static const int DateTimeMeter_attributes[] = {
|
||||
DATETIME
|
||||
};
|
||||
|
||||
static void DateTimeMeter_updateValues(Meter* this, char* buffer, size_t size) {
|
||||
time_t t = time(NULL);
|
||||
static void DateTimeMeter_updateValues(Meter* this) {
|
||||
const ProcessList* pl = this->pl;
|
||||
|
||||
struct tm result;
|
||||
struct tm* lt = localtime_r(&t, &result);
|
||||
const struct tm* lt = localtime_r(&pl->realtime.tv_sec, &result);
|
||||
int year = lt->tm_year + 1900;
|
||||
if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)) {
|
||||
this->total = 366;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
this->total = 365;
|
||||
}
|
||||
this->values[0] = lt->tm_yday;
|
||||
strftime(buffer, size, "%F %H:%M:%S", lt);
|
||||
strftime(this->txtBuffer, sizeof(this->txtBuffer), "%F %H:%M:%S", lt);
|
||||
}
|
||||
|
||||
const MeterClass DateTimeMeter_class = {
|
||||
|
@ -9,6 +9,7 @@ in the source distribution for its full text.
|
||||
|
||||
#include "Meter.h"
|
||||
|
||||
|
||||
extern const MeterClass DateTimeMeter_class;
|
||||
|
||||
#endif
|
||||
|
@ -9,12 +9,12 @@ in the source distribution for its full text.
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "CRT.h"
|
||||
#include "Macros.h"
|
||||
#include "Object.h"
|
||||
#include "Platform.h"
|
||||
#include "ProcessList.h"
|
||||
#include "RichString.h"
|
||||
#include "XUtils.h"
|
||||
|
||||
@ -26,51 +26,55 @@ static const int DiskIOMeter_attributes[] = {
|
||||
};
|
||||
|
||||
static bool hasData = false;
|
||||
static unsigned long int cached_read_diff = 0;
|
||||
static unsigned long int cached_write_diff = 0;
|
||||
static double cached_utilisation_diff = 0.0;
|
||||
static uint32_t cached_read_diff;
|
||||
static uint32_t cached_write_diff;
|
||||
static double cached_utilisation_diff;
|
||||
|
||||
static void DiskIOMeter_updateValues(Meter* this, char* buffer, size_t len) {
|
||||
static unsigned long long int cached_last_update = 0;
|
||||
static void DiskIOMeter_updateValues(Meter* this) {
|
||||
const ProcessList* pl = this->pl;
|
||||
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
unsigned long long int timeInMilliSeconds = (unsigned long long int)tv.tv_sec * 1000 + (unsigned long long int)tv.tv_usec / 1000;
|
||||
unsigned long long int passedTimeInMs = timeInMilliSeconds - cached_last_update;
|
||||
static uint64_t cached_last_update;
|
||||
uint64_t passedTimeInMs = pl->realtimeMs - cached_last_update;
|
||||
|
||||
/* update only every 500ms */
|
||||
if (passedTimeInMs > 500) {
|
||||
static unsigned long int cached_read_total = 0;
|
||||
static unsigned long int cached_write_total = 0;
|
||||
static unsigned long int cached_msTimeSpend_total = 0;
|
||||
static uint64_t cached_read_total;
|
||||
static uint64_t cached_write_total;
|
||||
static uint64_t cached_msTimeSpend_total;
|
||||
uint64_t diff;
|
||||
|
||||
cached_last_update = timeInMilliSeconds;
|
||||
cached_last_update = pl->realtimeMs;
|
||||
|
||||
DiskIOData data;
|
||||
|
||||
hasData = Platform_getDiskIO(&data);
|
||||
if (!hasData) {
|
||||
this->values[0] = 0;
|
||||
xSnprintf(buffer, len, "no data");
|
||||
xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "no data");
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.totalBytesRead > cached_read_total) {
|
||||
cached_read_diff = (data.totalBytesRead - cached_read_total) / 1024; /* Meter_humanUnit() expects unit in kilo */
|
||||
diff = data.totalBytesRead - cached_read_total;
|
||||
diff /= 1024; /* Meter_humanUnit() expects unit in kilo */
|
||||
cached_read_diff = (uint32_t)diff;
|
||||
} else {
|
||||
cached_read_diff = 0;
|
||||
}
|
||||
cached_read_total = data.totalBytesRead;
|
||||
|
||||
if (data.totalBytesWritten > cached_write_total) {
|
||||
cached_write_diff = (data.totalBytesWritten - cached_write_total) / 1024; /* Meter_humanUnit() expects unit in kilo */
|
||||
diff = data.totalBytesWritten - cached_write_total;
|
||||
diff /= 1024; /* Meter_humanUnit() expects unit in kilo */
|
||||
cached_write_diff = (uint32_t)diff;
|
||||
} else {
|
||||
cached_write_diff = 0;
|
||||
}
|
||||
cached_write_total = data.totalBytesWritten;
|
||||
|
||||
if (data.totalMsTimeSpend > cached_msTimeSpend_total) {
|
||||
cached_utilisation_diff = 100 * (double)(data.totalMsTimeSpend - cached_msTimeSpend_total) / passedTimeInMs;
|
||||
diff = data.totalMsTimeSpend - cached_msTimeSpend_total;
|
||||
cached_utilisation_diff = 100.0 * (double)diff / passedTimeInMs;
|
||||
} else {
|
||||
cached_utilisation_diff = 0.0;
|
||||
}
|
||||
@ -83,20 +87,21 @@ static void DiskIOMeter_updateValues(Meter* this, char* buffer, size_t len) {
|
||||
char bufferRead[12], bufferWrite[12];
|
||||
Meter_humanUnit(bufferRead, cached_read_diff, sizeof(bufferRead));
|
||||
Meter_humanUnit(bufferWrite, cached_write_diff, sizeof(bufferWrite));
|
||||
snprintf(buffer, len, "%sB %sB %.1f%%", bufferRead, bufferWrite, cached_utilisation_diff);
|
||||
snprintf(this->txtBuffer, sizeof(this->txtBuffer), "%sB %sB %.1f%%", bufferRead, bufferWrite, cached_utilisation_diff);
|
||||
}
|
||||
|
||||
static void DIskIOMeter_display(ATTR_UNUSED const Object* cast, RichString* out) {
|
||||
static void DiskIOMeter_display(ATTR_UNUSED const Object* cast, RichString* out) {
|
||||
if (!hasData) {
|
||||
RichString_writeAscii(out, CRT_colors[METER_VALUE_ERROR], "no data");
|
||||
return;
|
||||
}
|
||||
|
||||
char buffer[16];
|
||||
int len;
|
||||
|
||||
int color = cached_utilisation_diff > 40.0 ? METER_VALUE_NOTICE : METER_VALUE;
|
||||
xSnprintf(buffer, sizeof(buffer), "%.1f%%", cached_utilisation_diff);
|
||||
RichString_writeAscii(out, CRT_colors[color], buffer);
|
||||
len = xSnprintf(buffer, sizeof(buffer), "%.1f%%", cached_utilisation_diff);
|
||||
RichString_appendnAscii(out, CRT_colors[color], buffer, len);
|
||||
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], " read: ");
|
||||
Meter_humanUnit(buffer, cached_read_diff, sizeof(buffer));
|
||||
@ -111,7 +116,7 @@ const MeterClass DiskIOMeter_class = {
|
||||
.super = {
|
||||
.extends = Class(Meter),
|
||||
.delete = Meter_delete,
|
||||
.display = DIskIOMeter_display
|
||||
.display = DiskIOMeter_display
|
||||
},
|
||||
.updateValues = DiskIOMeter_updateValues,
|
||||
.defaultMode = TEXT_METERMODE,
|
||||
|
@ -9,10 +9,11 @@ in the source distribution for its full text.
|
||||
|
||||
#include "Meter.h"
|
||||
|
||||
|
||||
typedef struct DiskIOData_ {
|
||||
unsigned long int totalBytesRead;
|
||||
unsigned long int totalBytesWritten;
|
||||
unsigned long int totalMsTimeSpend;
|
||||
uint64_t totalBytesRead;
|
||||
uint64_t totalBytesWritten;
|
||||
uint64_t totalMsTimeSpend;
|
||||
} DiskIOData;
|
||||
|
||||
extern const MeterClass DiskIOMeter_class;
|
||||
|
@ -72,8 +72,9 @@ static HandlerResult DisplayOptionsPanel_eventHandler(Panel* super, int ch) {
|
||||
Header* header = this->scr->header;
|
||||
Header_calculateHeight(header);
|
||||
Header_reinit(header);
|
||||
Header_updateData(header);
|
||||
Header_draw(header);
|
||||
ScreenManager_resize(this->scr, this->scr->x1, header->height, this->scr->x2, this->scr->y2);
|
||||
ScreenManager_resize(this->scr);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -98,6 +99,7 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager*
|
||||
Panel_setHeader(super, "Display options");
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("Tree view", &(settings->treeView)));
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("- Tree view is always sorted by PID (htop 2 behavior)", &(settings->treeViewAlwaysByPID)));
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("- Tree view is collapsed by default", &(settings->allBranchesCollapsed)));
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("Shadow other users' processes", &(settings->shadowOtherUsers)));
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("Hide kernel threads", &(settings->hideKernelThreads)));
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("Hide userland process threads", &(settings->hideUserlandThreads)));
|
||||
@ -105,6 +107,7 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager*
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("Show custom thread names", &(settings->showThreadNames)));
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("Show program path", &(settings->showProgramPath)));
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("Highlight program \"basename\"", &(settings->highlightBaseName)));
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("Highlight out-dated/removed programs", &(settings->highlightDeletedExe)));
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("Merge exe, comm and cmdline in Command", &(settings->showMergedCommand)));
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("- Try to find comm in cmdline (when Command is merged)", &(settings->findCommInCmdline)));
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("- Try to strip exe from cmdline (when Command is merged)", &(settings->stripExeFromCmdline)));
|
||||
@ -116,14 +119,24 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager*
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("Add guest time in CPU meter percentage", &(settings->accountGuestInCPUMeter)));
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("Also show CPU percentage numerically", &(settings->showCPUUsage)));
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("Also show CPU frequency", &(settings->showCPUFrequency)));
|
||||
#ifdef HAVE_SENSORS_SENSORS_H
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("Also show CPU temperature (requires libsensors)", &(settings->showCPUTemperature)));
|
||||
#ifdef BUILD_WITH_CPU_TEMP
|
||||
Panel_add(super, (Object*) CheckItem_newByRef(
|
||||
#if defined(HTOP_LINUX)
|
||||
"Also show CPU temperature (requires libsensors)",
|
||||
#elif defined(HTOP_FREEBSD)
|
||||
"Also show CPU temperature",
|
||||
#else
|
||||
#error Unknown temperature implementation!
|
||||
#endif
|
||||
&(settings->showCPUTemperature)));
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("- Show temperature in degree Fahrenheit instead of Celsius", &(settings->degreeFahrenheit)));
|
||||
#endif
|
||||
#ifdef HAVE_GETMOUSE
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("Enable the mouse", &(settings->enableMouse)));
|
||||
#endif
|
||||
Panel_add(super, (Object*) NumberItem_newByRef("Update interval (in seconds)", &(settings->delay), -1, 1, 255));
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("Highlight new and old processes", &(settings->highlightChanges)));
|
||||
Panel_add(super, (Object*) NumberItem_newByRef("- Highlight time (in seconds)", &(settings->highlightDelaySecs), 0, 1, 24*60*60));
|
||||
Panel_add(super, (Object*) NumberItem_newByRef("- Highlight time (in seconds)", &(settings->highlightDelaySecs), 0, 1, 24 * 60 * 60));
|
||||
Panel_add(super, (Object*) NumberItem_newByRef("Hide main function bar (0 - off, 1 - on ESC until next input, 2 - permanently)", &(settings->hideFunctionBar), 0, 0, 2));
|
||||
#ifdef HAVE_LIBHWLOC
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("Show topology when selecting affinity by default", &(settings->topologyAffinity)));
|
||||
|
@ -11,6 +11,7 @@ in the source distribution for its full text.
|
||||
#include "ScreenManager.h"
|
||||
#include "Settings.h"
|
||||
|
||||
|
||||
typedef struct DisplayOptionsPanel_ {
|
||||
Panel super;
|
||||
|
||||
|
59
DynamicColumn.c
Normal file
59
DynamicColumn.c
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
htop - DynamicColumn.c
|
||||
(C) 2021 Sohaib Mohammed
|
||||
(C) 2021 htop dev team
|
||||
(C) 2021 Red Hat, Inc. All Rights Reserved.
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
#include "config.h" // IWYU pragma: keep
|
||||
|
||||
#include "DynamicColumn.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "Platform.h"
|
||||
#include "RichString.h"
|
||||
#include "XUtils.h"
|
||||
|
||||
|
||||
Hashtable* DynamicColumns_new(void) {
|
||||
return Platform_dynamicColumns();
|
||||
}
|
||||
|
||||
const char* DynamicColumn_init(unsigned int key) {
|
||||
return Platform_dynamicColumnInit(key);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
const char* name;
|
||||
const DynamicColumn* data;
|
||||
unsigned int key;
|
||||
} DynamicIterator;
|
||||
|
||||
static void DynamicColumn_compare(ht_key_t key, void* value, void* data) {
|
||||
const DynamicColumn* column = (const DynamicColumn*)value;
|
||||
DynamicIterator* iter = (DynamicIterator*)data;
|
||||
if (String_eq(iter->name, column->name)) {
|
||||
iter->data = column;
|
||||
iter->key = key;
|
||||
}
|
||||
}
|
||||
|
||||
const DynamicColumn* DynamicColumn_search(Hashtable* dynamics, const char* name, unsigned int* key) {
|
||||
DynamicIterator iter = { .key = 0, .data = NULL, .name = name };
|
||||
if (dynamics)
|
||||
Hashtable_foreach(dynamics, DynamicColumn_compare, &iter);
|
||||
if (key)
|
||||
*key = iter.key;
|
||||
return iter.data;
|
||||
}
|
||||
|
||||
const DynamicColumn* DynamicColumn_lookup(Hashtable* dynamics, unsigned int key) {
|
||||
return (const DynamicColumn*) Hashtable_get(dynamics, key);
|
||||
}
|
||||
|
||||
bool DynamicColumn_writeField(const Process* proc, RichString* str, unsigned int key) {
|
||||
return Platform_dynamicColumnWriteField(proc, str, key);
|
||||
}
|
32
DynamicColumn.h
Normal file
32
DynamicColumn.h
Normal file
@ -0,0 +1,32 @@
|
||||
#ifndef HEADER_DynamicColumn
|
||||
#define HEADER_DynamicColumn
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "Hashtable.h"
|
||||
#include "Process.h"
|
||||
#include "RichString.h"
|
||||
|
||||
|
||||
#define DYNAMIC_MAX_COLUMN_WIDTH 28
|
||||
#define DYNAMIC_DEFAULT_COLUMN_WIDTH -5
|
||||
|
||||
typedef struct DynamicColumn_ {
|
||||
char name[32]; /* unique, internal-only name */
|
||||
char* heading; /* displayed in main screen */
|
||||
char* caption; /* displayed in setup menu (short name) */
|
||||
char* description; /* displayed in setup menu (detail) */
|
||||
int width; /* display width +/- for value alignment */
|
||||
} DynamicColumn;
|
||||
|
||||
Hashtable* DynamicColumns_new(void);
|
||||
|
||||
const char* DynamicColumn_init(unsigned int key);
|
||||
|
||||
const DynamicColumn* DynamicColumn_lookup(Hashtable* dynamics, unsigned int key);
|
||||
|
||||
const DynamicColumn* DynamicColumn_search(Hashtable* dynamics, const char* name, unsigned int* key);
|
||||
|
||||
bool DynamicColumn_writeField(const Process* proc, RichString* str, unsigned int key);
|
||||
|
||||
#endif
|
124
DynamicMeter.c
Normal file
124
DynamicMeter.c
Normal file
@ -0,0 +1,124 @@
|
||||
/*
|
||||
htop - DynamicMeter.c
|
||||
(C) 2021 htop dev team
|
||||
(C) 2021 Red Hat, Inc. All Rights Reserved.
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
#include "config.h" // IWYU pragma: keep
|
||||
|
||||
#include "DynamicMeter.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "CRT.h"
|
||||
#include "Object.h"
|
||||
#include "Platform.h"
|
||||
#include "ProcessList.h"
|
||||
#include "RichString.h"
|
||||
#include "XUtils.h"
|
||||
|
||||
|
||||
static const int DynamicMeter_attributes[] = {
|
||||
DYNAMIC_GRAY,
|
||||
DYNAMIC_DARKGRAY,
|
||||
DYNAMIC_RED,
|
||||
DYNAMIC_GREEN,
|
||||
DYNAMIC_BLUE,
|
||||
DYNAMIC_CYAN,
|
||||
DYNAMIC_MAGENTA,
|
||||
DYNAMIC_YELLOW,
|
||||
DYNAMIC_WHITE
|
||||
};
|
||||
|
||||
Hashtable* DynamicMeters_new(void) {
|
||||
return Platform_dynamicMeters();
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
unsigned int key;
|
||||
const char* name;
|
||||
bool found;
|
||||
} DynamicIterator;
|
||||
|
||||
static void DynamicMeter_compare(ht_key_t key, void* value, void* data) {
|
||||
const DynamicMeter* meter = (const DynamicMeter*)value;
|
||||
DynamicIterator* iter = (DynamicIterator*)data;
|
||||
if (String_eq(iter->name, meter->name)) {
|
||||
iter->found = true;
|
||||
iter->key = key;
|
||||
}
|
||||
}
|
||||
|
||||
bool DynamicMeter_search(Hashtable* dynamics, const char* name, unsigned int* key) {
|
||||
DynamicIterator iter = { .key = 0, .name = name, .found = false };
|
||||
if (dynamics)
|
||||
Hashtable_foreach(dynamics, DynamicMeter_compare, &iter);
|
||||
if (key)
|
||||
*key = iter.key;
|
||||
return iter.found;
|
||||
}
|
||||
|
||||
const char* DynamicMeter_lookup(Hashtable* dynamics, unsigned int key) {
|
||||
const DynamicMeter* meter = Hashtable_get(dynamics, key);
|
||||
return meter ? meter->name : NULL;
|
||||
}
|
||||
|
||||
static void DynamicMeter_init(Meter* meter) {
|
||||
Platform_dynamicMeterInit(meter);
|
||||
}
|
||||
|
||||
static void DynamicMeter_updateValues(Meter* meter) {
|
||||
Platform_dynamicMeterUpdateValues(meter);
|
||||
}
|
||||
|
||||
static void DynamicMeter_display(const Object* cast, RichString* out) {
|
||||
const Meter* meter = (const Meter*)cast;
|
||||
Platform_dynamicMeterDisplay(meter, out);
|
||||
}
|
||||
|
||||
static const char* DynamicMeter_getCaption(const Meter* this) {
|
||||
const ProcessList* pl = this->pl;
|
||||
const DynamicMeter* meter = Hashtable_get(pl->dynamicMeters, this->param);
|
||||
if (meter)
|
||||
return meter->caption ? meter->caption : meter->name;
|
||||
return this->caption;
|
||||
}
|
||||
|
||||
static void DynamicMeter_getUiName(const Meter* this, char* name, size_t length) {
|
||||
const ProcessList* pl = this->pl;
|
||||
const DynamicMeter* meter = Hashtable_get(pl->dynamicMeters, this->param);
|
||||
if (meter) {
|
||||
const char* uiName = meter->caption;
|
||||
if (uiName) {
|
||||
int len = strlen(uiName);
|
||||
if (len > 2 && uiName[len - 2] == ':')
|
||||
len -= 2;
|
||||
xSnprintf(name, length, "%.*s", len, uiName);
|
||||
} else {
|
||||
xSnprintf(name, length, "%s", meter->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const MeterClass DynamicMeter_class = {
|
||||
.super = {
|
||||
.extends = Class(Meter),
|
||||
.delete = Meter_delete,
|
||||
.display = DynamicMeter_display
|
||||
},
|
||||
.init = DynamicMeter_init,
|
||||
.updateValues = DynamicMeter_updateValues,
|
||||
.getCaption = DynamicMeter_getCaption,
|
||||
.getUiName = DynamicMeter_getUiName,
|
||||
.defaultMode = TEXT_METERMODE,
|
||||
.maxItems = 0,
|
||||
.total = 100.0,
|
||||
.attributes = DynamicMeter_attributes,
|
||||
.name = "Dynamic",
|
||||
.uiName = "Dynamic",
|
||||
.caption = "",
|
||||
};
|
26
DynamicMeter.h
Normal file
26
DynamicMeter.h
Normal file
@ -0,0 +1,26 @@
|
||||
#ifndef HEADER_DynamicMeter
|
||||
#define HEADER_DynamicMeter
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "Hashtable.h"
|
||||
#include "Meter.h"
|
||||
|
||||
|
||||
typedef struct DynamicMeter_ {
|
||||
char name[32]; /* unique name, cannot contain spaces */
|
||||
char* caption;
|
||||
char* description;
|
||||
unsigned int type;
|
||||
double maximum;
|
||||
} DynamicMeter;
|
||||
|
||||
Hashtable* DynamicMeters_new(void);
|
||||
|
||||
const char* DynamicMeter_lookup(Hashtable* dynamics, unsigned int key);
|
||||
|
||||
bool DynamicMeter_search(Hashtable* dynamics, const char* name, unsigned int* key);
|
||||
|
||||
extern const MeterClass DynamicMeter_class;
|
||||
|
||||
#endif
|
@ -5,7 +5,6 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "CRT.h"
|
||||
#include "Macros.h"
|
||||
#include "Panel.h"
|
||||
#include "Platform.h"
|
||||
@ -34,11 +33,9 @@ static void EnvScreen_scan(InfoScreen* this) {
|
||||
|
||||
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)
|
||||
for (const char* p = env; *p; p = strrchr(p, 0) + 1)
|
||||
InfoScreen_addLine(this, p);
|
||||
free(env);
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "Object.h"
|
||||
#include "Process.h"
|
||||
|
||||
|
||||
typedef struct EnvScreen_ {
|
||||
InfoScreen super;
|
||||
} EnvScreen;
|
||||
|
@ -9,6 +9,7 @@ in the source distribution for its full text.
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
|
||||
typedef struct FunctionBar_ {
|
||||
int size;
|
||||
char** functions;
|
||||
|
@ -11,7 +11,6 @@ in the source distribution for its full text.
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
@ -19,6 +18,10 @@ in the source distribution for its full text.
|
||||
#include "Macros.h"
|
||||
#include "XUtils.h"
|
||||
|
||||
#ifndef NDEBUG
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct HashtableItem_ {
|
||||
ht_key_t key;
|
||||
@ -95,7 +98,7 @@ static const uint64_t OEISprimes[] = {
|
||||
34359738337, 68719476731, 137438953447
|
||||
};
|
||||
|
||||
static uint64_t nextPrime(size_t n) {
|
||||
static size_t nextPrime(size_t n) {
|
||||
/* on 32-bit make sure we do not return primes not fitting in size_t */
|
||||
for (size_t i = 0; i < ARRAYSIZE(OEISprimes) && OEISprimes[i] < SIZE_MAX; i++) {
|
||||
if (n <= OEISprimes[i])
|
||||
|
246
Header.c
246
Header.c
@ -7,12 +7,17 @@ in the source distribution for its full text.
|
||||
|
||||
#include "Header.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "CRT.h"
|
||||
#include "CPUMeter.h"
|
||||
#include "DynamicMeter.h"
|
||||
#include "Macros.h"
|
||||
#include "Object.h"
|
||||
#include "Platform.h"
|
||||
@ -20,15 +25,17 @@ in the source distribution for its full text.
|
||||
#include "XUtils.h"
|
||||
|
||||
|
||||
Header* Header_new(struct ProcessList_* pl, Settings* settings, int nrColumns) {
|
||||
Header* Header_new(ProcessList* pl, Settings* settings, HeaderLayout hLayout) {
|
||||
Header* this = xCalloc(1, sizeof(Header));
|
||||
this->columns = xCalloc(nrColumns, sizeof(Vector*));
|
||||
this->columns = xMallocArray(HeaderLayout_getColumns(hLayout), sizeof(Vector*));
|
||||
this->settings = settings;
|
||||
this->pl = pl;
|
||||
this->nrColumns = nrColumns;
|
||||
this->headerLayout = hLayout;
|
||||
|
||||
Header_forEachColumn(this, i) {
|
||||
this->columns[i] = Vector_new(Class(Meter), true, DEFAULT_SIZE);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -36,42 +43,119 @@ void Header_delete(Header* this) {
|
||||
Header_forEachColumn(this, i) {
|
||||
Vector_delete(this->columns[i]);
|
||||
}
|
||||
|
||||
free(this->columns);
|
||||
free(this);
|
||||
}
|
||||
|
||||
void Header_populateFromSettings(Header* this) {
|
||||
Header_forEachColumn(this, col) {
|
||||
MeterColumnSettings* colSettings = &this->settings->columns[col];
|
||||
for (int i = 0; i < colSettings->len; i++) {
|
||||
Header_addMeterByName(this, colSettings->names[i], col);
|
||||
if (colSettings->modes[i] != 0) {
|
||||
Header_setMode(this, i, colSettings->modes[i], col);
|
||||
void Header_setLayout(Header* this, HeaderLayout hLayout) {
|
||||
size_t oldColumns = HeaderLayout_getColumns(this->headerLayout);
|
||||
size_t newColumns = HeaderLayout_getColumns(hLayout);
|
||||
|
||||
this->headerLayout = hLayout;
|
||||
|
||||
if (newColumns == oldColumns)
|
||||
return;
|
||||
|
||||
if (newColumns > oldColumns) {
|
||||
this->columns = xReallocArray(this->columns, newColumns, sizeof(Vector*));
|
||||
for (size_t i = oldColumns; i < newColumns; i++)
|
||||
this->columns[i] = Vector_new(Class(Meter), true, DEFAULT_SIZE);
|
||||
} else {
|
||||
// move meters from to-be-deleted columns into last one
|
||||
for (size_t i = newColumns; i < oldColumns; i++) {
|
||||
for (int j = this->columns[i]->items - 1; j >= 0; j--) {
|
||||
Vector_add(this->columns[newColumns - 1], Vector_take(this->columns[i], j));
|
||||
}
|
||||
Vector_delete(this->columns[i]);
|
||||
}
|
||||
this->columns = xReallocArray(this->columns, newColumns, sizeof(Vector*));
|
||||
}
|
||||
|
||||
Header_calculateHeight(this);
|
||||
}
|
||||
|
||||
static void Header_addMeterByName(Header* this, const char* name, MeterModeId mode, unsigned int column) {
|
||||
assert(column < HeaderLayout_getColumns(this->headerLayout));
|
||||
|
||||
Vector* meters = this->columns[column];
|
||||
|
||||
const char* paren = strchr(name, '(');
|
||||
unsigned int param = 0;
|
||||
size_t nameLen;
|
||||
if (paren) {
|
||||
int ok = sscanf(paren, "(%10u)", ¶m); // CPUMeter
|
||||
if (!ok) {
|
||||
char* end, dynamic[32] = {0};
|
||||
if (sscanf(paren, "(%30s)", dynamic)) { // DynamicMeter
|
||||
if ((end = strrchr(dynamic, ')')) == NULL)
|
||||
return; // htoprc parse failure
|
||||
*end = '\0';
|
||||
if (!DynamicMeter_search(this->pl->dynamicMeters, dynamic, ¶m))
|
||||
return; // name lookup failure
|
||||
} else {
|
||||
param = 0;
|
||||
}
|
||||
}
|
||||
nameLen = paren - name;
|
||||
} else {
|
||||
nameLen = strlen(name);
|
||||
}
|
||||
|
||||
for (const MeterClass* const* type = Platform_meterTypes; *type; type++) {
|
||||
if (0 == strncmp(name, (*type)->name, nameLen) && (*type)->name[nameLen] == '\0') {
|
||||
Meter* meter = Meter_new(this->pl, param, *type);
|
||||
if (mode != 0) {
|
||||
Meter_setMode(meter, mode);
|
||||
}
|
||||
Vector_add(meters, meter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Header_populateFromSettings(Header* this) {
|
||||
Header_setLayout(this, this->settings->hLayout);
|
||||
|
||||
Header_forEachColumn(this, col) {
|
||||
const MeterColumnSetting* colSettings = &this->settings->hColumns[col];
|
||||
Vector_prune(this->columns[col]);
|
||||
for (size_t i = 0; i < colSettings->len; i++) {
|
||||
Header_addMeterByName(this, colSettings->names[i], colSettings->modes[i], col);
|
||||
}
|
||||
}
|
||||
|
||||
Header_calculateHeight(this);
|
||||
}
|
||||
|
||||
void Header_writeBackToSettings(const Header* this) {
|
||||
Header_forEachColumn(this, col) {
|
||||
MeterColumnSettings* colSettings = &this->settings->columns[col];
|
||||
Settings_setHeaderLayout(this->settings, this->headerLayout);
|
||||
|
||||
String_freeArray(colSettings->names);
|
||||
Header_forEachColumn(this, col) {
|
||||
MeterColumnSetting* colSettings = &this->settings->hColumns[col];
|
||||
|
||||
if (colSettings->names) {
|
||||
for (size_t j = 0; j < colSettings->len; j++)
|
||||
free(colSettings->names[j]);
|
||||
free(colSettings->names);
|
||||
}
|
||||
free(colSettings->modes);
|
||||
|
||||
Vector* vec = this->columns[col];
|
||||
const Vector* vec = this->columns[col];
|
||||
int len = Vector_size(vec);
|
||||
|
||||
colSettings->names = xCalloc(len + 1, sizeof(char*));
|
||||
colSettings->modes = xCalloc(len, sizeof(int));
|
||||
colSettings->names = len ? xCalloc(len, sizeof(char*)) : NULL;
|
||||
colSettings->modes = len ? xCalloc(len, sizeof(int)) : NULL;
|
||||
colSettings->len = len;
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
Meter* meter = (Meter*) Vector_get(vec, i);
|
||||
const Meter* meter = (Meter*) Vector_get(vec, i);
|
||||
char* name;
|
||||
if (meter->param) {
|
||||
xAsprintf(&name, "%s(%d)", As_Meter(meter)->name, meter->param);
|
||||
if (meter->param && As_Meter(meter) == &DynamicMeter_class) {
|
||||
const char* dynamic = DynamicMeter_lookup(this->pl->dynamicMeters, meter->param);
|
||||
xAsprintf(&name, "%s(%s)", As_Meter(meter)->name, dynamic);
|
||||
} else if (meter->param && As_Meter(meter) == &CPUMeter_class) {
|
||||
xAsprintf(&name, "%s(%u)", As_Meter(meter)->name, meter->param);
|
||||
} else {
|
||||
xAsprintf(&name, "%s", As_Meter(meter)->name);
|
||||
}
|
||||
@ -81,44 +165,9 @@ void Header_writeBackToSettings(const Header* this) {
|
||||
}
|
||||
}
|
||||
|
||||
MeterModeId Header_addMeterByName(Header* this, char* name, int column) {
|
||||
Vector* meters = this->columns[column];
|
||||
Meter* Header_addMeterByClass(Header* this, const MeterClass* type, unsigned int param, unsigned int column) {
|
||||
assert(column < HeaderLayout_getColumns(this->headerLayout));
|
||||
|
||||
char* paren = strchr(name, '(');
|
||||
int param = 0;
|
||||
if (paren) {
|
||||
int ok = sscanf(paren, "(%10d)", ¶m);
|
||||
if (!ok)
|
||||
param = 0;
|
||||
*paren = '\0';
|
||||
}
|
||||
MeterModeId mode = TEXT_METERMODE;
|
||||
for (const MeterClass* const* type = Platform_meterTypes; *type; type++) {
|
||||
if (String_eq(name, (*type)->name)) {
|
||||
Meter* meter = Meter_new(this->pl, param, *type);
|
||||
Vector_add(meters, meter);
|
||||
mode = meter->mode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (paren)
|
||||
*paren = '(';
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
void Header_setMode(Header* this, int i, MeterModeId mode, int column) {
|
||||
Vector* meters = this->columns[column];
|
||||
|
||||
if (i >= Vector_size(meters))
|
||||
return;
|
||||
|
||||
Meter* meter = (Meter*) Vector_get(meters, i);
|
||||
Meter_setMode(meter, mode);
|
||||
}
|
||||
|
||||
Meter* Header_addMeterByClass(Header* this, const MeterClass* type, int param, int column) {
|
||||
Vector* meters = this->columns[column];
|
||||
|
||||
Meter* meter = Meter_new(this->pl, param, type);
|
||||
@ -126,18 +175,6 @@ Meter* Header_addMeterByClass(Header* this, const MeterClass* type, int param, i
|
||||
return meter;
|
||||
}
|
||||
|
||||
int Header_size(Header* this, int column) {
|
||||
Vector* meters = this->columns[column];
|
||||
return Vector_size(meters);
|
||||
}
|
||||
|
||||
MeterModeId Header_readMeterMode(Header* this, int i, int column) {
|
||||
Vector* meters = this->columns[column];
|
||||
|
||||
Meter* meter = (Meter*) Vector_get(meters, i);
|
||||
return meter->mode;
|
||||
}
|
||||
|
||||
void Header_reinit(Header* this) {
|
||||
Header_forEachColumn(this, col) {
|
||||
for (int i = 0; i < Vector_size(this->columns[col]); i++) {
|
||||
@ -150,35 +187,94 @@ void Header_reinit(Header* this) {
|
||||
}
|
||||
|
||||
void Header_draw(const Header* this) {
|
||||
int height = this->height;
|
||||
int pad = this->pad;
|
||||
const int height = this->height;
|
||||
const int pad = this->pad;
|
||||
attrset(CRT_colors[RESET_COLOR]);
|
||||
for (int y = 0; y < height; y++) {
|
||||
mvhline(y, 0, ' ', COLS);
|
||||
}
|
||||
int width = COLS / this->nrColumns - (pad * this->nrColumns - 1) - 1;
|
||||
const int width = COLS - pad;
|
||||
int x = pad;
|
||||
float roundingLoss = 0.0f;
|
||||
|
||||
Header_forEachColumn(this, col) {
|
||||
Vector* meters = this->columns[col];
|
||||
float colWidth = (float)width * HeaderLayout_layouts[this->headerLayout].widths[col] / 100.0f;
|
||||
|
||||
roundingLoss += colWidth - floorf(colWidth);
|
||||
if (roundingLoss >= 1.0f) {
|
||||
colWidth += 1.0f;
|
||||
roundingLoss -= 1.0f;
|
||||
}
|
||||
|
||||
for (int y = (pad / 2), i = 0; i < Vector_size(meters); i++) {
|
||||
Meter* meter = (Meter*) Vector_get(meters, i);
|
||||
meter->draw(meter, x, y, width);
|
||||
|
||||
float actualWidth = colWidth;
|
||||
if (meter->mode == TEXT_METERMODE) {
|
||||
for (int j = 1; j < meter->columnWidthCount; j++) {
|
||||
actualWidth += (float)width * HeaderLayout_layouts[this->headerLayout].widths[col + j] / 100.0f;
|
||||
}
|
||||
}
|
||||
|
||||
assert(meter->draw);
|
||||
meter->draw(meter, x, y, floorf(actualWidth));
|
||||
y += meter->h;
|
||||
}
|
||||
x += width + pad;
|
||||
|
||||
x += floorf(colWidth);
|
||||
}
|
||||
}
|
||||
|
||||
void Header_updateData(Header* this) {
|
||||
Header_forEachColumn(this, col) {
|
||||
Vector* meters = this->columns[col];
|
||||
int items = Vector_size(meters);
|
||||
for (int i = 0; i < items; i++) {
|
||||
Meter* meter = (Meter*) Vector_get(meters, i);
|
||||
Meter_updateValues(meter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate how many columns the current meter is allowed to span,
|
||||
* by counting how many columns to the right are empty or contain a BlankMeter.
|
||||
* Returns the number of columns to span, i.e. if the direct neighbor is occupied 1.
|
||||
*/
|
||||
static int calcColumnWidthCount(const Header* this, const Meter* curMeter, const int pad, const unsigned int curColumn, const int curHeight) {
|
||||
for (size_t i = curColumn + 1; i < HeaderLayout_getColumns(this->headerLayout); i++) {
|
||||
const Vector* meters = this->columns[i];
|
||||
|
||||
int height = pad;
|
||||
for (int j = 0; j < Vector_size(meters); j++) {
|
||||
const Meter* meter = (const Meter*) Vector_get(meters, j);
|
||||
|
||||
if (height >= curHeight + curMeter->h)
|
||||
break;
|
||||
|
||||
height += meter->h;
|
||||
if (height <= curHeight)
|
||||
continue;
|
||||
|
||||
if (!Object_isA((const Object*) meter, (const ObjectClass*) &BlankMeter_class))
|
||||
return i - curColumn;
|
||||
}
|
||||
}
|
||||
|
||||
return HeaderLayout_getColumns(this->headerLayout) - curColumn;
|
||||
}
|
||||
|
||||
int Header_calculateHeight(Header* this) {
|
||||
int pad = this->settings->headerMargin ? 2 : 0;
|
||||
const int pad = this->settings->headerMargin ? 2 : 0;
|
||||
int maxHeight = pad;
|
||||
|
||||
Header_forEachColumn(this, col) {
|
||||
Vector* meters = this->columns[col];
|
||||
const Vector* meters = this->columns[col];
|
||||
int height = pad;
|
||||
for (int i = 0; i < Vector_size(meters); i++) {
|
||||
Meter* meter = (Meter*) Vector_get(meters, i);
|
||||
meter->columnWidthCount = calcColumnWidthCount(this, meter, pad, col, height);
|
||||
height += meter->h;
|
||||
}
|
||||
maxHeight = MAXIMUM(maxHeight, height);
|
||||
|
22
Header.h
22
Header.h
@ -7,44 +7,42 @@ Released under the GNU GPLv2, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
#include "HeaderLayout.h"
|
||||
#include "Meter.h"
|
||||
#include "ProcessList.h"
|
||||
#include "Settings.h"
|
||||
#include "Vector.h"
|
||||
|
||||
|
||||
typedef struct Header_ {
|
||||
Vector** columns;
|
||||
Settings* settings;
|
||||
ProcessList* pl;
|
||||
int nrColumns;
|
||||
HeaderLayout headerLayout;
|
||||
int pad;
|
||||
int height;
|
||||
} Header;
|
||||
|
||||
#define Header_forEachColumn(this_, i_) for (int (i_)=0; (i_) < (this_)->nrColumns; ++(i_))
|
||||
#define Header_forEachColumn(this_, i_) for (size_t (i_)=0; (i_) < HeaderLayout_getColumns((this_)->headerLayout); ++(i_))
|
||||
|
||||
Header* Header_new(ProcessList* pl, Settings* settings, int nrColumns);
|
||||
Header* Header_new(ProcessList* pl, Settings* settings, HeaderLayout hLayout);
|
||||
|
||||
void Header_delete(Header* this);
|
||||
|
||||
void Header_setLayout(Header* this, HeaderLayout hLayout);
|
||||
|
||||
void Header_populateFromSettings(Header* this);
|
||||
|
||||
void Header_writeBackToSettings(const Header* this);
|
||||
|
||||
MeterModeId Header_addMeterByName(Header* this, char* name, int column);
|
||||
|
||||
void Header_setMode(Header* this, int i, MeterModeId mode, int column);
|
||||
|
||||
Meter* Header_addMeterByClass(Header* this, const MeterClass* type, int param, int column);
|
||||
|
||||
int Header_size(Header* this, int column);
|
||||
|
||||
MeterModeId Header_readMeterMode(Header* this, int i, int column);
|
||||
Meter* Header_addMeterByClass(Header* this, const MeterClass* type, unsigned int param, unsigned int column);
|
||||
|
||||
void Header_reinit(Header* this);
|
||||
|
||||
void Header_draw(const Header* this);
|
||||
|
||||
void Header_updateData(Header* this);
|
||||
|
||||
int Header_calculateHeight(Header* this);
|
||||
|
||||
#endif
|
||||
|
77
HeaderLayout.h
Normal file
77
HeaderLayout.h
Normal file
@ -0,0 +1,77 @@
|
||||
#ifndef HEADER_HeaderLayout
|
||||
#define HEADER_HeaderLayout
|
||||
/*
|
||||
htop - HeaderLayout.h
|
||||
(C) 2021 htop dev team
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
#include "config.h" // IWYU pragma: keep
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "Macros.h"
|
||||
#include "XUtils.h"
|
||||
|
||||
|
||||
typedef enum HeaderLayout_ {
|
||||
HF_TWO_50_50,
|
||||
HF_TWO_33_67,
|
||||
HF_TWO_67_33,
|
||||
HF_THREE_33_34_33,
|
||||
HF_THREE_25_25_50,
|
||||
HF_THREE_25_50_25,
|
||||
HF_THREE_50_25_25,
|
||||
HF_THREE_40_20_40,
|
||||
HF_FOUR_25_25_25_25,
|
||||
LAST_HEADER_LAYOUT
|
||||
} HeaderLayout;
|
||||
|
||||
static const struct {
|
||||
uint8_t columns;
|
||||
const uint8_t widths[4];
|
||||
const char* name;
|
||||
const char* description;
|
||||
} HeaderLayout_layouts[LAST_HEADER_LAYOUT] = {
|
||||
[HF_TWO_50_50] = { 2, { 50, 50, 0, 0 }, "two_50_50", "2 columns - 50/50 (default)", },
|
||||
[HF_TWO_33_67] = { 2, { 33, 67, 0, 0 }, "two_33_67", "2 columns - 33/67", },
|
||||
[HF_TWO_67_33] = { 2, { 67, 33, 0, 0 }, "two_67_33", "2 columns - 67/33", },
|
||||
[HF_THREE_33_34_33] = { 3, { 33, 34, 33, 0 }, "three_33_34_33", "3 columns - 33/34/33", },
|
||||
[HF_THREE_25_25_50] = { 3, { 25, 25, 50, 0 }, "three_25_25_50", "3 columns - 25/25/50", },
|
||||
[HF_THREE_25_50_25] = { 3, { 25, 50, 25, 0 }, "three_25_50_25", "3 columns - 25/50/25", },
|
||||
[HF_THREE_50_25_25] = { 3, { 50, 25, 25, 0 }, "three_50_25_25", "3 columns - 50/25/25", },
|
||||
[HF_THREE_40_20_40] = { 3, { 40, 20, 40, 0 }, "three_40_20_40", "3 columns - 40/20/40", },
|
||||
[HF_FOUR_25_25_25_25] = { 4, { 25, 25, 25, 25 }, "four_25_25_25_25", "4 columns - 25/25/25/25", },
|
||||
};
|
||||
|
||||
static inline size_t HeaderLayout_getColumns(HeaderLayout hLayout) {
|
||||
/* assert the layout is initialized */
|
||||
assert(0 <= hLayout);
|
||||
assert(hLayout < LAST_HEADER_LAYOUT);
|
||||
assert(HeaderLayout_layouts[hLayout].name[0]);
|
||||
assert(HeaderLayout_layouts[hLayout].description[0]);
|
||||
return HeaderLayout_layouts[hLayout].columns;
|
||||
}
|
||||
|
||||
static inline const char* HeaderLayout_getName(HeaderLayout hLayout) {
|
||||
/* assert the layout is initialized */
|
||||
assert(0 <= hLayout);
|
||||
assert(hLayout < LAST_HEADER_LAYOUT);
|
||||
assert(HeaderLayout_layouts[hLayout].name[0]);
|
||||
assert(HeaderLayout_layouts[hLayout].description[0]);
|
||||
return HeaderLayout_layouts[hLayout].name;
|
||||
}
|
||||
|
||||
static inline HeaderLayout HeaderLayout_fromName(const char* name) {
|
||||
for (size_t i = 0; i < LAST_HEADER_LAYOUT; i++) {
|
||||
if (String_eq(HeaderLayout_layouts[i].name, name))
|
||||
return (HeaderLayout) i;
|
||||
}
|
||||
|
||||
return LAST_HEADER_LAYOUT;
|
||||
}
|
||||
|
||||
#endif /* HEADER_HeaderLayout */
|
87
HeaderOptionsPanel.c
Normal file
87
HeaderOptionsPanel.c
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
htop - HeaderOptionsPanel.c
|
||||
(C) 2021 htop dev team
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
#include "HeaderOptionsPanel.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "CRT.h"
|
||||
#include "FunctionBar.h"
|
||||
#include "Header.h"
|
||||
#include "HeaderLayout.h"
|
||||
#include "Object.h"
|
||||
#include "OptionItem.h"
|
||||
#include "ProvideCurses.h"
|
||||
|
||||
|
||||
static const char* const HeaderOptionsFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL};
|
||||
|
||||
static void HeaderOptionsPanel_delete(Object* object) {
|
||||
Panel* super = (Panel*) object;
|
||||
HeaderOptionsPanel* this = (HeaderOptionsPanel*) object;
|
||||
Panel_done(super);
|
||||
free(this);
|
||||
}
|
||||
|
||||
static HandlerResult HeaderOptionsPanel_eventHandler(Panel* super, int ch) {
|
||||
HeaderOptionsPanel* this = (HeaderOptionsPanel*) super;
|
||||
|
||||
HandlerResult result = IGNORED;
|
||||
int mark;
|
||||
|
||||
switch(ch) {
|
||||
case 0x0a:
|
||||
case 0x0d:
|
||||
case KEY_ENTER:
|
||||
case KEY_MOUSE:
|
||||
case KEY_RECLICK:
|
||||
case ' ':
|
||||
mark = Panel_getSelectedIndex(super);
|
||||
assert(mark >= 0);
|
||||
assert(mark < LAST_HEADER_LAYOUT);
|
||||
|
||||
for (int i = 0; i < LAST_HEADER_LAYOUT; i++)
|
||||
CheckItem_set((CheckItem*)Panel_get(super, i), false);
|
||||
CheckItem_set((CheckItem*)Panel_get(super, mark), true);
|
||||
|
||||
Header_setLayout(this->scr->header, mark);
|
||||
this->settings->changed = true;
|
||||
|
||||
ScreenManager_resize(this->scr);
|
||||
|
||||
result = HANDLED;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const PanelClass HeaderOptionsPanel_class = {
|
||||
.super = {
|
||||
.extends = Class(Panel),
|
||||
.delete = HeaderOptionsPanel_delete
|
||||
},
|
||||
.eventHandler = HeaderOptionsPanel_eventHandler
|
||||
};
|
||||
|
||||
HeaderOptionsPanel* HeaderOptionsPanel_new(Settings* settings, ScreenManager* scr) {
|
||||
HeaderOptionsPanel* this = AllocThis(HeaderOptionsPanel);
|
||||
Panel* super = (Panel*) this;
|
||||
FunctionBar* fuBar = FunctionBar_new(HeaderOptionsFunctions, NULL, NULL);
|
||||
Panel_init(super, 1, 1, 1, 1, Class(CheckItem), true, fuBar);
|
||||
|
||||
this->scr = scr;
|
||||
this->settings = settings;
|
||||
|
||||
Panel_setHeader(super, "Header Layout");
|
||||
for (int i = 0; i < LAST_HEADER_LAYOUT; i++) {
|
||||
Panel_add(super, (Object*) CheckItem_newByVal(HeaderLayout_layouts[i].description, false));
|
||||
}
|
||||
CheckItem_set((CheckItem*)Panel_get(super, settings->hLayout), true);
|
||||
return this;
|
||||
}
|
26
HeaderOptionsPanel.h
Normal file
26
HeaderOptionsPanel.h
Normal file
@ -0,0 +1,26 @@
|
||||
#ifndef HEADER_HeaderOptionsPanel
|
||||
#define HEADER_HeaderOptionsPanel
|
||||
/*
|
||||
htop - ColorsPanel.h
|
||||
(C) 2021 htop dev team
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
#include "Panel.h"
|
||||
#include "ScreenManager.h"
|
||||
#include "Settings.h"
|
||||
|
||||
|
||||
typedef struct HeaderOptionsPanel_ {
|
||||
Panel super;
|
||||
|
||||
ScreenManager* scr;
|
||||
Settings* settings;
|
||||
} HeaderOptionsPanel;
|
||||
|
||||
extern const PanelClass HeaderOptionsPanel_class;
|
||||
|
||||
HeaderOptionsPanel* HeaderOptionsPanel_new(Settings* settings, ScreenManager* scr);
|
||||
|
||||
#endif /* HEADER_HeaderOptionsPanel */
|
@ -9,19 +9,17 @@ in the source distribution for its full text.
|
||||
|
||||
#include "HostnameMeter.h"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "CRT.h"
|
||||
#include "Object.h"
|
||||
#include "Platform.h"
|
||||
|
||||
|
||||
static const int HostnameMeter_attributes[] = {
|
||||
HOSTNAME
|
||||
};
|
||||
|
||||
static void HostnameMeter_updateValues(Meter* this, char* buffer, size_t size) {
|
||||
(void) this;
|
||||
gethostname(buffer, size - 1);
|
||||
static void HostnameMeter_updateValues(Meter* this) {
|
||||
Platform_getHostname(this->txtBuffer, sizeof(this->txtBuffer));
|
||||
}
|
||||
|
||||
const MeterClass HostnameMeter_class = {
|
||||
|
@ -9,6 +9,7 @@ in the source distribution for its full text.
|
||||
|
||||
#include "Meter.h"
|
||||
|
||||
|
||||
extern const MeterClass HostnameMeter_class;
|
||||
|
||||
#endif
|
||||
|
17
IncSet.c
17
IncSet.c
@ -29,6 +29,13 @@ void IncSet_reset(IncSet* this, IncType type) {
|
||||
IncMode_reset(&this->modes[type]);
|
||||
}
|
||||
|
||||
void IncSet_setFilter(IncSet* this, const char* filter) {
|
||||
IncMode* mode = &this->modes[INC_FILTER];
|
||||
size_t len = String_safeStrncpy(mode->buffer, filter, sizeof(mode->buffer));
|
||||
mode->index = len;
|
||||
this->filtering = true;
|
||||
}
|
||||
|
||||
static const char* const searchFunctions[] = {"Next ", "Prev ", "Cancel ", " Search: ", NULL};
|
||||
static const char* const searchKeys[] = {"F3", "S-F3", "Esc", " "};
|
||||
static const int searchEvents[] = {KEY_F(3), KEY_F(15), 27, ERR};
|
||||
@ -70,8 +77,8 @@ void IncSet_delete(IncSet* this) {
|
||||
free(this);
|
||||
}
|
||||
|
||||
static void updateWeakPanel(IncSet* this, Panel* panel, Vector* lines) {
|
||||
Object* selected = Panel_getSelected(panel);
|
||||
static void updateWeakPanel(const IncSet* this, Panel* panel, Vector* lines) {
|
||||
const Object* selected = Panel_getSelected(panel);
|
||||
Panel_prune(panel);
|
||||
if (this->filtering) {
|
||||
int n = 0;
|
||||
@ -98,7 +105,7 @@ static void updateWeakPanel(IncSet* this, Panel* panel, Vector* lines) {
|
||||
}
|
||||
}
|
||||
|
||||
static bool search(IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelValue) {
|
||||
static bool search(const IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelValue) {
|
||||
int size = Panel_size(panel);
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (String_contains_i(getPanelValue(panel, i), mode->buffer)) {
|
||||
@ -110,7 +117,7 @@ static bool search(IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelVa
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool IncMode_find(IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelValue, int step) {
|
||||
static bool IncMode_find(const IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelValue, int step) {
|
||||
int size = Panel_size(panel);
|
||||
int here = Panel_getSelectedIndex(panel);
|
||||
int i = here;
|
||||
@ -201,7 +208,7 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue
|
||||
}
|
||||
|
||||
const char* IncSet_getListItemValue(Panel* panel, int i) {
|
||||
ListItem* l = (ListItem*) Panel_get(panel, i);
|
||||
const ListItem* l = (const ListItem*) Panel_get(panel, i);
|
||||
return l ? l->value : "";
|
||||
}
|
||||
|
||||
|
3
IncSet.h
3
IncSet.h
@ -14,6 +14,7 @@ in the source distribution for its full text.
|
||||
#include "Panel.h"
|
||||
#include "Vector.h"
|
||||
|
||||
|
||||
#define INCMODE_MAX 40
|
||||
|
||||
typedef enum {
|
||||
@ -40,6 +41,8 @@ static inline const char* IncSet_filter(const IncSet* this) {
|
||||
return this->filtering ? this->modes[INC_FILTER].buffer : NULL;
|
||||
}
|
||||
|
||||
void IncSet_setFilter(IncSet* this, const char* filter);
|
||||
|
||||
typedef const char* (*IncMode_GetPanelValue)(Panel*, int);
|
||||
|
||||
void IncSet_reset(IncSet* this, IncType type);
|
||||
|
34
InfoScreen.c
34
InfoScreen.c
@ -4,7 +4,6 @@
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "CRT.h"
|
||||
@ -28,7 +27,7 @@ InfoScreen* InfoScreen_init(InfoScreen* this, const Process* process, FunctionBa
|
||||
}
|
||||
this->display = Panel_new(0, 1, COLS, height, Class(ListItem), false, bar);
|
||||
this->inc = IncSet_new(bar);
|
||||
this->lines = Vector_new(this->display->items->type, true, DEFAULT_SIZE);
|
||||
this->lines = Vector_new(Vector_type(this->display->items), true, DEFAULT_SIZE);
|
||||
Panel_setHeader(this->display, panelHeader);
|
||||
return this;
|
||||
}
|
||||
@ -95,7 +94,9 @@ void InfoScreen_run(InfoScreen* this) {
|
||||
if (this->inc->active) {
|
||||
(void) move(LINES - 1, CRT_cursorX);
|
||||
}
|
||||
#ifdef HAVE_SET_ESCDELAY
|
||||
set_escdelay(25);
|
||||
#endif
|
||||
int ch = getch();
|
||||
|
||||
if (ch == ERR) {
|
||||
@ -105,18 +106,29 @@ void InfoScreen_run(InfoScreen* this) {
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_GETMOUSE
|
||||
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;
|
||||
} else if (mevent.y == LINES - 1) {
|
||||
ch = IncSet_synthesizeEvent(this->inc, mevent.x);
|
||||
if (mevent.bstate & BUTTON1_RELEASED) {
|
||||
if (mevent.y >= panel->y && mevent.y < LINES - 1) {
|
||||
Panel_setSelected(panel, mevent.y - panel->y + panel->scrollV - 1);
|
||||
ch = 0;
|
||||
} else if (mevent.y == LINES - 1) {
|
||||
ch = IncSet_synthesizeEvent(this->inc, mevent.x);
|
||||
}
|
||||
}
|
||||
#if NCURSES_MOUSE_VERSION > 1
|
||||
else if (mevent.bstate & BUTTON4_PRESSED) {
|
||||
ch = KEY_WHEELUP;
|
||||
} else if (mevent.bstate & BUTTON5_PRESSED) {
|
||||
ch = KEY_WHEELDOWN;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (this->inc->active) {
|
||||
IncSet_handleKey(this->inc, ch, panel, IncSet_getListItemValue, this->lines);
|
||||
@ -136,8 +148,10 @@ void InfoScreen_run(InfoScreen* this) {
|
||||
break;
|
||||
case KEY_F(5):
|
||||
clear();
|
||||
if (As_InfoScreen(this)->scan)
|
||||
if (As_InfoScreen(this)->scan) {
|
||||
Vector_prune(this->lines);
|
||||
InfoScreen_scan(this);
|
||||
}
|
||||
|
||||
InfoScreen_draw(this);
|
||||
break;
|
||||
@ -152,8 +166,10 @@ void InfoScreen_run(InfoScreen* this) {
|
||||
break;
|
||||
case KEY_RESIZE:
|
||||
Panel_resize(panel, COLS, LINES - 2);
|
||||
if (As_InfoScreen(this)->scan)
|
||||
if (As_InfoScreen(this)->scan) {
|
||||
Vector_prune(this->lines);
|
||||
InfoScreen_scan(this);
|
||||
}
|
||||
|
||||
InfoScreen_draw(this);
|
||||
break;
|
||||
|
@ -31,11 +31,9 @@ static void ListItem_display(const Object* cast, RichString* out) {
|
||||
if (this->moving) {
|
||||
RichString_writeWide(out, CRT_colors[DEFAULT_COLOR],
|
||||
#ifdef HAVE_LIBNCURSESW
|
||||
CRT_utf8 ? "↕ " :
|
||||
CRT_utf8 ? "↕ " :
|
||||
#endif
|
||||
"+ ");
|
||||
} else {
|
||||
RichString_prune(out);
|
||||
"+ ");
|
||||
}
|
||||
RichString_appendWide(out, CRT_colors[DEFAULT_COLOR], this->value);
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ in the source distribution for its full text.
|
||||
|
||||
#include "Object.h"
|
||||
|
||||
|
||||
typedef struct ListItem_ {
|
||||
Object super;
|
||||
char* value;
|
||||
|
@ -10,6 +10,7 @@ in the source distribution for its full text.
|
||||
#include "CRT.h"
|
||||
#include "Object.h"
|
||||
#include "Platform.h"
|
||||
#include "ProcessList.h"
|
||||
#include "RichString.h"
|
||||
#include "XUtils.h"
|
||||
|
||||
@ -36,7 +37,7 @@ static const int High_attributes[] = {
|
||||
METER_VALUE_ERROR
|
||||
};
|
||||
|
||||
static void LoadAverageMeter_updateValues(Meter* this, char* buffer, size_t size) {
|
||||
static void LoadAverageMeter_updateValues(Meter* this) {
|
||||
Platform_getLoadAverage(&this->values[0], &this->values[1], &this->values[2]);
|
||||
|
||||
// only show bar for 1min value
|
||||
@ -46,29 +47,31 @@ static void LoadAverageMeter_updateValues(Meter* this, char* buffer, size_t size
|
||||
if (this->values[0] < 1.0) {
|
||||
this->curAttributes = OK_attributes;
|
||||
this->total = 1.0;
|
||||
} else if (this->values[0] < this->pl->cpuCount) {
|
||||
} else if (this->values[0] < this->pl->activeCPUs) {
|
||||
this->curAttributes = Medium_attributes;
|
||||
this->total = this->pl->cpuCount;
|
||||
this->total = this->pl->activeCPUs;
|
||||
} else {
|
||||
this->curAttributes = High_attributes;
|
||||
this->total = 2 * this->pl->cpuCount;
|
||||
this->total = 2 * this->pl->activeCPUs;
|
||||
}
|
||||
|
||||
xSnprintf(buffer, size, "%.2f/%.2f/%.2f", this->values[0], this->values[1], this->values[2]);
|
||||
xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%.2f/%.2f/%.2f", this->values[0], this->values[1], this->values[2]);
|
||||
}
|
||||
|
||||
static void LoadAverageMeter_display(const Object* cast, RichString* out) {
|
||||
const Meter* this = (const Meter*)cast;
|
||||
char buffer[20];
|
||||
xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[0]);
|
||||
RichString_writeAscii(out, CRT_colors[LOAD_AVERAGE_ONE], buffer);
|
||||
xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[1]);
|
||||
RichString_appendAscii(out, CRT_colors[LOAD_AVERAGE_FIVE], buffer);
|
||||
xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[2]);
|
||||
RichString_appendAscii(out, CRT_colors[LOAD_AVERAGE_FIFTEEN], buffer);
|
||||
int len;
|
||||
|
||||
len = xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[0]);
|
||||
RichString_appendnAscii(out, CRT_colors[LOAD_AVERAGE_ONE], buffer, len);
|
||||
len = xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[1]);
|
||||
RichString_appendnAscii(out, CRT_colors[LOAD_AVERAGE_FIVE], buffer, len);
|
||||
len = xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[2]);
|
||||
RichString_appendnAscii(out, CRT_colors[LOAD_AVERAGE_FIFTEEN], buffer, len);
|
||||
}
|
||||
|
||||
static void LoadMeter_updateValues(Meter* this, char* buffer, size_t size) {
|
||||
static void LoadMeter_updateValues(Meter* this) {
|
||||
double five, fifteen;
|
||||
Platform_getLoadAverage(&this->values[0], &five, &fifteen);
|
||||
|
||||
@ -76,22 +79,24 @@ static void LoadMeter_updateValues(Meter* this, char* buffer, size_t size) {
|
||||
if (this->values[0] < 1.0) {
|
||||
this->curAttributes = OK_attributes;
|
||||
this->total = 1.0;
|
||||
} else if (this->values[0] < this->pl->cpuCount) {
|
||||
} else if (this->values[0] < this->pl->activeCPUs) {
|
||||
this->curAttributes = Medium_attributes;
|
||||
this->total = this->pl->cpuCount;
|
||||
this->total = this->pl->activeCPUs;
|
||||
} else {
|
||||
this->curAttributes = High_attributes;
|
||||
this->total = 2 * this->pl->cpuCount;
|
||||
this->total = 2 * this->pl->activeCPUs;
|
||||
}
|
||||
|
||||
xSnprintf(buffer, size, "%.2f", this->values[0]);
|
||||
xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%.2f", this->values[0]);
|
||||
}
|
||||
|
||||
static void LoadMeter_display(const Object* cast, RichString* out) {
|
||||
const Meter* this = (const Meter*)cast;
|
||||
char buffer[20];
|
||||
xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[0]);
|
||||
RichString_writeAscii(out, CRT_colors[LOAD], buffer);
|
||||
int len;
|
||||
|
||||
len = xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[0]);
|
||||
RichString_appendnAscii(out, CRT_colors[LOAD], buffer, len);
|
||||
}
|
||||
|
||||
const MeterClass LoadAverageMeter_class = {
|
||||
|
@ -9,6 +9,7 @@ in the source distribution for its full text.
|
||||
|
||||
#include "Meter.h"
|
||||
|
||||
|
||||
extern const MeterClass LoadAverageMeter_class;
|
||||
|
||||
extern const MeterClass LoadMeter_class;
|
||||
|
37
Macros.h
37
Macros.h
@ -4,27 +4,31 @@
|
||||
#include <assert.h> // IWYU pragma: keep
|
||||
|
||||
#ifndef MINIMUM
|
||||
#define MINIMUM(a, b) ((a) < (b) ? (a) : (b))
|
||||
#define MINIMUM(a, b) ((a) < (b) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
#ifndef MAXIMUM
|
||||
#define MAXIMUM(a, b) ((a) > (b) ? (a) : (b))
|
||||
#define MAXIMUM(a, b) ((a) > (b) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
#ifndef CLAMP
|
||||
#define CLAMP(x, low, high) (assert((low) <= (high)), ((x) > (high)) ? (high) : MAXIMUM(x, low))
|
||||
#define CLAMP(x, low, high) (assert((low) <= (high)), ((x) > (high)) ? (high) : MAXIMUM(x, low))
|
||||
#endif
|
||||
|
||||
#ifndef ARRAYSIZE
|
||||
#define ARRAYSIZE(x) (sizeof(x) / sizeof((x)[0]))
|
||||
#define ARRAYSIZE(x) (sizeof(x) / sizeof((x)[0]))
|
||||
#endif
|
||||
|
||||
#ifndef SPACESHIP_NUMBER
|
||||
#define SPACESHIP_NUMBER(a, b) (((a) > (b)) - ((a) < (b)))
|
||||
#define SPACESHIP_NUMBER(a, b) (((a) > (b)) - ((a) < (b)))
|
||||
#endif
|
||||
|
||||
#ifndef SPACESHIP_NULLSTR
|
||||
#define SPACESHIP_NULLSTR(a, b) strcmp((a) ? (a) : "", (b) ? (b) : "")
|
||||
#define SPACESHIP_NULLSTR(a, b) strcmp((a) ? (a) : "", (b) ? (b) : "")
|
||||
#endif
|
||||
|
||||
#ifndef SPACESHIP_DEFAULTSTR
|
||||
#define SPACESHIP_DEFAULTSTR(a, b, s) strcmp((a) ? (a) : (s), (b) ? (b) : (s))
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__ // defined by GCC and Clang
|
||||
@ -33,6 +37,7 @@
|
||||
#define ATTR_NONNULL __attribute__((nonnull))
|
||||
#define ATTR_NORETURN __attribute__((noreturn))
|
||||
#define ATTR_UNUSED __attribute__((unused))
|
||||
#define ATTR_MALLOC __attribute__((malloc))
|
||||
|
||||
#else /* __GNUC__ */
|
||||
|
||||
@ -40,13 +45,26 @@
|
||||
#define ATTR_NONNULL
|
||||
#define ATTR_NORETURN
|
||||
#define ATTR_UNUSED
|
||||
#define ATTR_MALLOC
|
||||
|
||||
#endif /* __GNUC__ */
|
||||
|
||||
#ifdef HAVE_ATTR_ALLOC_SIZE
|
||||
|
||||
#define ATTR_ALLOC_SIZE1(a) __attribute__((alloc_size (a)))
|
||||
#define ATTR_ALLOC_SIZE2(a, b) __attribute__((alloc_size (a, b)))
|
||||
|
||||
#else
|
||||
|
||||
#define ATTR_ALLOC_SIZE1(a)
|
||||
#define ATTR_ALLOC_SIZE2(a, b)
|
||||
|
||||
#endif /* HAVE_ATTR_ALLOC_SIZE */
|
||||
|
||||
// ignore casts discarding const specifier, e.g.
|
||||
// const char [] -> char * / void *
|
||||
// const char *[2]' -> char *const *
|
||||
#ifdef __clang__
|
||||
#if defined(__clang__)
|
||||
#define IGNORE_WCASTQUAL_BEGIN _Pragma("clang diagnostic push") \
|
||||
_Pragma("clang diagnostic ignored \"-Wcast-qual\"")
|
||||
#define IGNORE_WCASTQUAL_END _Pragma("clang diagnostic pop")
|
||||
@ -59,4 +77,9 @@
|
||||
#define IGNORE_WCASTQUAL_END
|
||||
#endif
|
||||
|
||||
/* This subtraction is used by Linux / NetBSD / OpenBSD for calculation of CPU usage items. */
|
||||
static inline unsigned long long saturatingSub(unsigned long long a, unsigned long long b) {
|
||||
return a > b ? a - b : 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
14
MainPanel.c
14
MainPanel.c
@ -21,18 +21,19 @@ in the source distribution for its full text.
|
||||
#include "XUtils.h"
|
||||
|
||||
|
||||
static const char* const MainFunctions[] = {"Help ", "Setup ", "Search", "Filter", "Tree ", "SortBy", "Nice -", "Nice +", "Kill ", "Quit ", NULL};
|
||||
static const char* const MainFunctions[] = {"Help ", "Setup ", "Search", "Filter", "Tree ", "SortBy", "Nice -", "Nice +", "Kill ", "Quit ", NULL};
|
||||
static const char* const MainFunctions_ro[] = {"Help ", "Setup ", "Search", "Filter", "Tree ", "SortBy", " ", " ", " ", "Quit ", NULL};
|
||||
|
||||
void MainPanel_updateTreeFunctions(MainPanel* this, bool mode) {
|
||||
FunctionBar* bar = MainPanel_getFunctionBar(this);
|
||||
FunctionBar_setLabel(bar, KEY_F(5), mode ? "List " : "Tree ");
|
||||
}
|
||||
|
||||
void MainPanel_pidSearch(MainPanel* this, int ch) {
|
||||
static 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);
|
||||
const Process* p = (const Process*) Panel_get(super, i);
|
||||
if (p && p->pid == pid) {
|
||||
Panel_setSelected(super, i);
|
||||
break;
|
||||
@ -111,6 +112,9 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
|
||||
if (reaction & HTOP_REDRAW_BAR) {
|
||||
MainPanel_updateTreeFunctions(this, this->state->settings->treeView);
|
||||
}
|
||||
if (reaction & HTOP_RESIZE) {
|
||||
result |= RESIZE;
|
||||
}
|
||||
if (reaction & HTOP_UPDATE_PANELHDR) {
|
||||
result |= REDRAW;
|
||||
}
|
||||
@ -134,7 +138,7 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
|
||||
}
|
||||
|
||||
int MainPanel_selectedPid(MainPanel* this) {
|
||||
Process* p = (Process*) Panel_getSelected((Panel*)this);
|
||||
const Process* p = (const Process*) Panel_getSelected((Panel*)this);
|
||||
if (p) {
|
||||
return p->pid;
|
||||
}
|
||||
@ -195,7 +199,7 @@ const PanelClass MainPanel_class = {
|
||||
|
||||
MainPanel* MainPanel_new() {
|
||||
MainPanel* this = AllocThis(MainPanel);
|
||||
Panel_init((Panel*) this, 1, 1, 1, 1, Class(Process), false, FunctionBar_new(MainFunctions, NULL, NULL));
|
||||
Panel_init((Panel*) this, 1, 1, 1, 1, Class(Process), false, FunctionBar_new(Settings_isReadonly() ? MainFunctions_ro : MainFunctions, NULL, NULL));
|
||||
this->keys = xCalloc(KEY_MAX, sizeof(Htop_Action));
|
||||
this->inc = IncSet_new(MainPanel_getFunctionBar(this));
|
||||
|
||||
|
@ -34,8 +34,6 @@ typedef bool(*MainPanel_ForeachProcessFn)(Process*, Arg);
|
||||
|
||||
void MainPanel_updateTreeFunctions(MainPanel* this, bool mode);
|
||||
|
||||
void MainPanel_pidSearch(MainPanel* this, int ch);
|
||||
|
||||
int MainPanel_selectedPid(MainPanel* this);
|
||||
|
||||
bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, Arg arg, bool* wasAnyTagged);
|
||||
|
240
Makefile.am
240
Makefile.am
@ -1,10 +1,14 @@
|
||||
AUTOMAKE_OPTIONS = subdir-objects
|
||||
|
||||
bin_PROGRAMS = htop
|
||||
|
||||
dist_man_MANS = htop.1
|
||||
EXTRA_DIST = $(dist_man_MANS) htop.desktop htop.png htop.svg \
|
||||
install-sh autogen.sh missing
|
||||
EXTRA_DIST = \
|
||||
$(dist_man_MANS) \
|
||||
autogen.sh \
|
||||
htop.desktop \
|
||||
htop.png \
|
||||
htop.svg \
|
||||
build-aux/compile \
|
||||
build-aux/depcomp \
|
||||
build-aux/install-sh \
|
||||
build-aux/missing
|
||||
applicationsdir = $(datadir)/applications
|
||||
applications_DATA = htop.desktop
|
||||
pixmapdir = $(datadir)/pixmaps
|
||||
@ -12,7 +16,7 @@ pixmap_DATA = htop.png
|
||||
appicondir = $(datadir)/icons/hicolor/scalable/apps
|
||||
appicon_DATA = htop.svg
|
||||
|
||||
AM_CFLAGS += -pedantic -std=c99 -D_XOPEN_SOURCE_EXTENDED -DSYSCONFDIR=\"$(sysconfdir)\" -I"$(top_srcdir)/$(my_htop_platform)"
|
||||
AM_CFLAGS += -pedantic -std=c99 -D_XOPEN_SOURCE_EXTENDED -DSYSCONFDIR="\"$(sysconfdir)\"" -I"$(top_srcdir)/$(my_htop_platform)"
|
||||
AM_LDFLAGS =
|
||||
|
||||
myhtopsources = \
|
||||
@ -26,6 +30,7 @@ myhtopsources = \
|
||||
ClockMeter.c \
|
||||
ColorsPanel.c \
|
||||
ColumnsPanel.c \
|
||||
CommandLine.c \
|
||||
CommandScreen.c \
|
||||
Compat.c \
|
||||
CPUMeter.c \
|
||||
@ -34,18 +39,21 @@ myhtopsources = \
|
||||
DateTimeMeter.c \
|
||||
DiskIOMeter.c \
|
||||
DisplayOptionsPanel.c \
|
||||
DynamicColumn.c \
|
||||
DynamicMeter.c \
|
||||
EnvScreen.c \
|
||||
FunctionBar.c \
|
||||
Hashtable.c \
|
||||
Header.c \
|
||||
HeaderOptionsPanel.c \
|
||||
HostnameMeter.c \
|
||||
htop.c \
|
||||
IncSet.c \
|
||||
InfoScreen.c \
|
||||
ListItem.c \
|
||||
LoadAverageMeter.c \
|
||||
MainPanel.c \
|
||||
MemoryMeter.c \
|
||||
MemorySwapMeter.c \
|
||||
Meter.c \
|
||||
MetersPanel.c \
|
||||
NetworkIOMeter.c \
|
||||
@ -61,6 +69,7 @@ myhtopsources = \
|
||||
Settings.c \
|
||||
SignalsPanel.c \
|
||||
SwapMeter.c \
|
||||
SysArchMeter.c \
|
||||
TasksMeter.c \
|
||||
TraceScreen.c \
|
||||
UptimeMeter.c \
|
||||
@ -81,16 +90,21 @@ myhtopheaders = \
|
||||
ClockMeter.h \
|
||||
ColorsPanel.h \
|
||||
ColumnsPanel.h \
|
||||
CommandLine.h \
|
||||
CommandScreen.h \
|
||||
Compat.h \
|
||||
DateMeter.h \
|
||||
DateTimeMeter.h \
|
||||
DiskIOMeter.h \
|
||||
DisplayOptionsPanel.h \
|
||||
DynamicColumn.h \
|
||||
DynamicMeter.h \
|
||||
EnvScreen.h \
|
||||
FunctionBar.h \
|
||||
Hashtable.h \
|
||||
Header.h \
|
||||
HeaderLayout.h \
|
||||
HeaderOptionsPanel.h \
|
||||
HostnameMeter.h \
|
||||
IncSet.h \
|
||||
InfoScreen.h \
|
||||
@ -99,6 +113,7 @@ myhtopheaders = \
|
||||
Macros.h \
|
||||
MainPanel.h \
|
||||
MemoryMeter.h \
|
||||
MemorySwapMeter.h \
|
||||
Meter.h \
|
||||
MetersPanel.h \
|
||||
NetworkIOMeter.h \
|
||||
@ -115,6 +130,7 @@ myhtopheaders = \
|
||||
Settings.h \
|
||||
SignalsPanel.h \
|
||||
SwapMeter.h \
|
||||
SysArchMeter.h \
|
||||
TasksMeter.h \
|
||||
TraceScreen.h \
|
||||
UptimeMeter.h \
|
||||
@ -126,6 +142,10 @@ myhtopheaders = \
|
||||
# -----
|
||||
|
||||
linux_platform_headers = \
|
||||
generic/gettime.h \
|
||||
generic/hostname.h \
|
||||
generic/uname.h \
|
||||
linux/HugePageMeter.h \
|
||||
linux/IOPriority.h \
|
||||
linux/IOPriorityPanel.h \
|
||||
linux/LibSensors.h \
|
||||
@ -142,9 +162,11 @@ linux_platform_headers = \
|
||||
zfs/ZfsArcStats.h \
|
||||
zfs/ZfsCompressedArcMeter.h
|
||||
|
||||
if HTOP_LINUX
|
||||
AM_LDFLAGS += -rdynamic
|
||||
myhtopplatsources = \
|
||||
linux_platform_sources = \
|
||||
generic/gettime.c \
|
||||
generic/hostname.c \
|
||||
generic/uname.c \
|
||||
linux/HugePageMeter.c \
|
||||
linux/IOPriorityPanel.c \
|
||||
linux/LibSensors.c \
|
||||
linux/LinuxProcess.c \
|
||||
@ -155,10 +177,13 @@ myhtopplatsources = \
|
||||
linux/SystemdMeter.c \
|
||||
linux/ZramMeter.c \
|
||||
zfs/ZfsArcMeter.c \
|
||||
zfs/ZfsArcStats.c \
|
||||
zfs/ZfsCompressedArcMeter.c
|
||||
|
||||
if HTOP_LINUX
|
||||
AM_LDFLAGS += -rdynamic
|
||||
myhtopplatprogram = htop
|
||||
myhtopplatheaders = $(linux_platform_headers)
|
||||
myhtopplatsources = $(linux_platform_sources)
|
||||
endif
|
||||
|
||||
# FreeBSD
|
||||
@ -169,17 +194,29 @@ freebsd_platform_headers = \
|
||||
freebsd/FreeBSDProcess.h \
|
||||
freebsd/Platform.h \
|
||||
freebsd/ProcessField.h \
|
||||
generic/gettime.h \
|
||||
generic/hostname.h \
|
||||
generic/openzfs_sysctl.h \
|
||||
generic/uname.h \
|
||||
zfs/ZfsArcMeter.h \
|
||||
zfs/ZfsCompressedArcMeter.h \
|
||||
zfs/ZfsArcStats.h \
|
||||
zfs/openzfs_sysctl.h
|
||||
zfs/ZfsCompressedArcMeter.h
|
||||
|
||||
freebsd_platform_sources = \
|
||||
freebsd/Platform.c \
|
||||
freebsd/FreeBSDProcessList.c \
|
||||
freebsd/FreeBSDProcess.c \
|
||||
generic/gettime.c \
|
||||
generic/hostname.c \
|
||||
generic/openzfs_sysctl.c \
|
||||
generic/uname.c \
|
||||
zfs/ZfsArcMeter.c \
|
||||
zfs/ZfsCompressedArcMeter.c
|
||||
|
||||
if HTOP_FREEBSD
|
||||
myhtopplatsources = freebsd/Platform.c freebsd/FreeBSDProcessList.c \
|
||||
freebsd/FreeBSDProcess.c \
|
||||
zfs/ZfsArcMeter.c zfs/ZfsCompressedArcMeter.c zfs/ZfsArcStats.c zfs/openzfs_sysctl.c
|
||||
|
||||
myhtopplatprogram = htop
|
||||
myhtopplatheaders = $(freebsd_platform_headers)
|
||||
myhtopplatsources = $(freebsd_platform_sources)
|
||||
endif
|
||||
|
||||
# DragonFlyBSD
|
||||
@ -189,31 +226,75 @@ dragonflybsd_platform_headers = \
|
||||
dragonflybsd/DragonFlyBSDProcessList.h \
|
||||
dragonflybsd/DragonFlyBSDProcess.h \
|
||||
dragonflybsd/Platform.h \
|
||||
dragonflybsd/ProcessField.h
|
||||
dragonflybsd/ProcessField.h \
|
||||
generic/gettime.h \
|
||||
generic/hostname.h \
|
||||
generic/uname.h
|
||||
|
||||
dragonflybsd_platform_sources = \
|
||||
dragonflybsd/DragonFlyBSDProcessList.c \
|
||||
dragonflybsd/DragonFlyBSDProcess.c \
|
||||
dragonflybsd/Platform.c \
|
||||
generic/gettime.c \
|
||||
generic/hostname.c \
|
||||
generic/uname.c
|
||||
|
||||
if HTOP_DRAGONFLYBSD
|
||||
myhtopplatsources = \
|
||||
dragonflybsd/Platform.c \
|
||||
dragonflybsd/DragonFlyBSDProcessList.c \
|
||||
dragonflybsd/DragonFlyBSDProcess.c
|
||||
|
||||
myhtopplatprogram = htop
|
||||
myhtopplatheaders = $(dragonflybsd_platform_headers)
|
||||
myhtopplatsources = $(dragonflybsd_platform_sources)
|
||||
endif
|
||||
|
||||
# NetBSD
|
||||
# -------
|
||||
|
||||
netbsd_platform_headers = \
|
||||
generic/gettime.h \
|
||||
generic/hostname.h \
|
||||
generic/uname.h \
|
||||
netbsd/Platform.h \
|
||||
netbsd/ProcessField.h \
|
||||
netbsd/NetBSDProcess.h \
|
||||
netbsd/NetBSDProcessList.h
|
||||
|
||||
netbsd_platform_sources = \
|
||||
generic/gettime.c \
|
||||
generic/hostname.c \
|
||||
generic/uname.c \
|
||||
netbsd/Platform.c \
|
||||
netbsd/NetBSDProcess.c \
|
||||
netbsd/NetBSDProcessList.c
|
||||
|
||||
if HTOP_NETBSD
|
||||
myhtopplatprogram = htop
|
||||
myhtopplatheaders = $(netbsd_platform_headers)
|
||||
myhtopplatsources = $(netbsd_platform_sources)
|
||||
endif
|
||||
|
||||
# OpenBSD
|
||||
# -------
|
||||
|
||||
openbsd_platform_headers = \
|
||||
generic/gettime.h \
|
||||
generic/hostname.h \
|
||||
generic/uname.h \
|
||||
openbsd/OpenBSDProcessList.h \
|
||||
openbsd/OpenBSDProcess.h \
|
||||
openbsd/Platform.h \
|
||||
openbsd/ProcessField.h
|
||||
|
||||
if HTOP_OPENBSD
|
||||
myhtopplatsources = openbsd/Platform.c openbsd/OpenBSDProcessList.c \
|
||||
openbsd/OpenBSDProcess.c
|
||||
openbsd_platform_sources = \
|
||||
generic/gettime.c \
|
||||
generic/hostname.c \
|
||||
generic/uname.c \
|
||||
openbsd/OpenBSDProcessList.c \
|
||||
openbsd/OpenBSDProcess.c \
|
||||
openbsd/Platform.c
|
||||
|
||||
if HTOP_OPENBSD
|
||||
myhtopplatprogram = htop
|
||||
myhtopplatheaders = $(openbsd_platform_headers)
|
||||
myhtopplatsources = $(openbsd_platform_sources)
|
||||
endif
|
||||
|
||||
# Darwin
|
||||
@ -224,59 +305,126 @@ darwin_platform_headers = \
|
||||
darwin/DarwinProcessList.h \
|
||||
darwin/Platform.h \
|
||||
darwin/ProcessField.h \
|
||||
generic/gettime.h \
|
||||
generic/hostname.h \
|
||||
generic/openzfs_sysctl.h \
|
||||
generic/uname.h \
|
||||
zfs/ZfsArcMeter.h \
|
||||
zfs/ZfsCompressedArcMeter.h \
|
||||
zfs/ZfsArcStats.h \
|
||||
zfs/openzfs_sysctl.h
|
||||
zfs/ZfsCompressedArcMeter.h
|
||||
|
||||
darwin_platform_sources = \
|
||||
darwin/Platform.c \
|
||||
darwin/DarwinProcess.c \
|
||||
darwin/DarwinProcessList.c \
|
||||
generic/gettime.c \
|
||||
generic/hostname.c \
|
||||
generic/openzfs_sysctl.c \
|
||||
generic/uname.c \
|
||||
zfs/ZfsArcMeter.c \
|
||||
zfs/ZfsCompressedArcMeter.c
|
||||
|
||||
if HTOP_DARWIN
|
||||
AM_LDFLAGS += -framework IOKit -framework CoreFoundation
|
||||
myhtopplatsources = darwin/Platform.c darwin/DarwinProcess.c \
|
||||
darwin/DarwinProcessList.c \
|
||||
zfs/ZfsArcMeter.c zfs/ZfsCompressedArcMeter.c zfs/ZfsArcStats.c zfs/openzfs_sysctl.c
|
||||
|
||||
myhtopplatprogram = htop
|
||||
myhtopplatheaders = $(darwin_platform_headers)
|
||||
myhtopplatsources = $(darwin_platform_sources)
|
||||
endif
|
||||
|
||||
# Solaris
|
||||
# -------
|
||||
|
||||
solaris_platform_headers = \
|
||||
solaris/Platform.h \
|
||||
generic/gettime.h \
|
||||
generic/hostname.h \
|
||||
generic/uname.h \
|
||||
solaris/ProcessField.h \
|
||||
solaris/Platform.h \
|
||||
solaris/SolarisProcess.h \
|
||||
solaris/SolarisProcessList.h \
|
||||
zfs/ZfsArcMeter.h \
|
||||
zfs/ZfsCompressedArcMeter.h \
|
||||
zfs/ZfsArcStats.h
|
||||
zfs/ZfsArcStats.h \
|
||||
zfs/ZfsCompressedArcMeter.h
|
||||
|
||||
solaris_platform_sources = \
|
||||
generic/gettime.c \
|
||||
generic/hostname.c \
|
||||
generic/uname.c \
|
||||
solaris/Platform.c \
|
||||
solaris/SolarisProcess.c \
|
||||
solaris/SolarisProcessList.c \
|
||||
zfs/ZfsArcMeter.c \
|
||||
zfs/ZfsCompressedArcMeter.c
|
||||
|
||||
if HTOP_SOLARIS
|
||||
myhtopplatsources = solaris/Platform.c \
|
||||
solaris/SolarisProcess.c solaris/SolarisProcessList.c \
|
||||
zfs/ZfsArcMeter.c zfs/ZfsCompressedArcMeter.c zfs/ZfsArcStats.c
|
||||
|
||||
myhtopplatprogram = htop
|
||||
myhtopplatheaders = $(solaris_platform_headers)
|
||||
myhtopplatsources = $(solaris_platform_sources)
|
||||
endif
|
||||
|
||||
# Performance Co-Pilot (PCP)
|
||||
# --------------------------
|
||||
|
||||
pcp_platform_headers = \
|
||||
linux/PressureStallMeter.h \
|
||||
linux/ZramMeter.h \
|
||||
linux/ZramStats.h \
|
||||
pcp/PCPDynamicColumn.h \
|
||||
pcp/PCPDynamicMeter.h \
|
||||
pcp/PCPMetric.h \
|
||||
pcp/PCPProcess.h \
|
||||
pcp/PCPProcessList.h \
|
||||
pcp/Platform.h \
|
||||
pcp/ProcessField.h \
|
||||
zfs/ZfsArcMeter.h \
|
||||
zfs/ZfsArcStats.h \
|
||||
zfs/ZfsCompressedArcMeter.h
|
||||
|
||||
pcp_platform_sources = \
|
||||
linux/PressureStallMeter.c \
|
||||
linux/ZramMeter.c \
|
||||
pcp/PCPDynamicColumn.c \
|
||||
pcp/PCPDynamicMeter.c \
|
||||
pcp/PCPMetric.c \
|
||||
pcp/PCPProcess.c \
|
||||
pcp/PCPProcessList.c \
|
||||
pcp/Platform.c \
|
||||
zfs/ZfsArcMeter.c \
|
||||
zfs/ZfsCompressedArcMeter.c
|
||||
|
||||
if HTOP_PCP
|
||||
myhtopplatprogram = pcp-htop
|
||||
myhtopplatheaders = $(pcp_platform_headers)
|
||||
myhtopplatsources = $(pcp_platform_sources)
|
||||
pcp_htop_SOURCES = $(myhtopplatprogram).c $(myhtopheaders) $(myhtopplatheaders) $(myhtopsources) $(myhtopplatsources)
|
||||
endif
|
||||
|
||||
# Unsupported
|
||||
# -----------
|
||||
|
||||
unsupported_platform_headers = \
|
||||
generic/gettime.h \
|
||||
unsupported/Platform.h \
|
||||
unsupported/ProcessField.h \
|
||||
unsupported/UnsupportedProcess.h \
|
||||
unsupported/UnsupportedProcessList.h
|
||||
|
||||
if HTOP_UNSUPPORTED
|
||||
myhtopplatsources = unsupported/Platform.c \
|
||||
unsupported/UnsupportedProcess.c unsupported/UnsupportedProcessList.c
|
||||
unsupported_platform_sources = \
|
||||
generic/gettime.c \
|
||||
unsupported/Platform.c \
|
||||
unsupported/UnsupportedProcess.c \
|
||||
unsupported/UnsupportedProcessList.c
|
||||
|
||||
if HTOP_UNSUPPORTED
|
||||
myhtopplatprogram = htop
|
||||
myhtopplatsources = $(unsupported_platform_sources)
|
||||
myhtopplatheaders = $(unsupported_platform_headers)
|
||||
endif
|
||||
|
||||
# ----
|
||||
|
||||
htop_SOURCES = $(myhtopheaders) $(myhtopplatheaders) $(myhtopsources) $(myhtopplatsources)
|
||||
bin_PROGRAMS = $(myhtopplatprogram)
|
||||
htop_SOURCES = $(myhtopplatprogram).c $(myhtopheaders) $(myhtopplatheaders) $(myhtopsources) $(myhtopplatsources)
|
||||
nodist_htop_SOURCES = config.h
|
||||
|
||||
target:
|
||||
@ -286,10 +434,10 @@ profile:
|
||||
$(MAKE) all AM_CPPFLAGS="-pg -O2 -DNDEBUG"
|
||||
|
||||
debug:
|
||||
$(MAKE) all AM_CPPFLAGS="-ggdb -DDEBUG"
|
||||
$(MAKE) all AM_CPPFLAGS="-ggdb3 -Og" CFLAGS="`printf ' %s ' "$(CFLAGS)"|sed -E 's#[[:space:]]-O[^[:space:]]+[[:space:]]# #g'` -ggdb3 -Og"
|
||||
|
||||
coverage:
|
||||
$(MAKE) all AM_CPPFLAGS="-fprofile-arcs -ftest-coverage -DDEBUG" AM_LDFLAGS="-lgcov"
|
||||
$(MAKE) all AM_CPPFLAGS="-fprofile-arcs -ftest-coverage" AM_LDFLAGS="-lgcov"
|
||||
|
||||
cppcheck:
|
||||
cppcheck -q -v . --enable=all -DHAVE_OPENVZ
|
||||
|
@ -7,6 +7,9 @@ in the source distribution for its full text.
|
||||
|
||||
#include "MemoryMeter.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "CRT.h"
|
||||
#include "Object.h"
|
||||
#include "Platform.h"
|
||||
@ -16,14 +19,24 @@ in the source distribution for its full text.
|
||||
static const int MemoryMeter_attributes[] = {
|
||||
MEMORY_USED,
|
||||
MEMORY_BUFFERS,
|
||||
MEMORY_SHARED,
|
||||
MEMORY_CACHE
|
||||
};
|
||||
|
||||
static void MemoryMeter_updateValues(Meter* this, char* buffer, size_t size) {
|
||||
static void MemoryMeter_updateValues(Meter* this) {
|
||||
char* buffer = this->txtBuffer;
|
||||
size_t size = sizeof(this->txtBuffer);
|
||||
int written;
|
||||
|
||||
/* shared and available memory are not supported on all platforms */
|
||||
this->values[2] = NAN;
|
||||
this->values[4] = NAN;
|
||||
Platform_setMemoryValues(this);
|
||||
|
||||
written = Meter_humanUnit(buffer, this->values[0], size);
|
||||
/* Do not print available memory in bar mode */
|
||||
this->curItems = 4;
|
||||
|
||||
written = Meter_humanUnit(buffer, isnan(this->values[4]) ? this->values[0] : this->total - this->values[4], size);
|
||||
METER_BUFFER_CHECK(buffer, size, written);
|
||||
|
||||
METER_BUFFER_APPEND_CHR(buffer, size, '/');
|
||||
@ -34,18 +47,36 @@ static void MemoryMeter_updateValues(Meter* this, char* buffer, size_t size) {
|
||||
static void MemoryMeter_display(const Object* cast, RichString* out) {
|
||||
char buffer[50];
|
||||
const Meter* this = (const Meter*)cast;
|
||||
|
||||
RichString_writeAscii(out, CRT_colors[METER_TEXT], ":");
|
||||
Meter_humanUnit(buffer, this->total, sizeof(buffer));
|
||||
RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer);
|
||||
|
||||
Meter_humanUnit(buffer, this->values[0], sizeof(buffer));
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], " used:");
|
||||
RichString_appendAscii(out, CRT_colors[MEMORY_USED], buffer);
|
||||
|
||||
Meter_humanUnit(buffer, this->values[1], sizeof(buffer));
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], " buffers:");
|
||||
RichString_appendAscii(out, CRT_colors[MEMORY_BUFFERS_TEXT], buffer);
|
||||
Meter_humanUnit(buffer, this->values[2], sizeof(buffer));
|
||||
|
||||
/* shared memory is not supported on all platforms */
|
||||
if (!isnan(this->values[2])) {
|
||||
Meter_humanUnit(buffer, this->values[2], sizeof(buffer));
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], " shared:");
|
||||
RichString_appendAscii(out, CRT_colors[MEMORY_SHARED], buffer);
|
||||
}
|
||||
|
||||
Meter_humanUnit(buffer, this->values[3], sizeof(buffer));
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], " cache:");
|
||||
RichString_appendAscii(out, CRT_colors[MEMORY_CACHE], buffer);
|
||||
|
||||
/* available memory is not supported on all platforms */
|
||||
if (!isnan(this->values[4])) {
|
||||
Meter_humanUnit(buffer, this->values[4], sizeof(buffer));
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], " available:");
|
||||
RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer);
|
||||
}
|
||||
}
|
||||
|
||||
const MeterClass MemoryMeter_class = {
|
||||
@ -56,7 +87,7 @@ const MeterClass MemoryMeter_class = {
|
||||
},
|
||||
.updateValues = MemoryMeter_updateValues,
|
||||
.defaultMode = BAR_METERMODE,
|
||||
.maxItems = 3,
|
||||
.maxItems = 5,
|
||||
.total = 100.0,
|
||||
.attributes = MemoryMeter_attributes,
|
||||
.name = "Memory",
|
||||
|
@ -9,6 +9,7 @@ in the source distribution for its full text.
|
||||
|
||||
#include "Meter.h"
|
||||
|
||||
|
||||
extern const MeterClass MemoryMeter_class;
|
||||
|
||||
#endif
|
||||
|
102
MemorySwapMeter.c
Normal file
102
MemorySwapMeter.c
Normal file
@ -0,0 +1,102 @@
|
||||
/*
|
||||
htop - MemorySwapMeter.c
|
||||
(C) 2021 htop dev team
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
#include "MemorySwapMeter.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "Macros.h"
|
||||
#include "MemoryMeter.h"
|
||||
#include "Object.h"
|
||||
#include "SwapMeter.h"
|
||||
#include "XUtils.h"
|
||||
|
||||
|
||||
typedef struct MemorySwapMeterData_ {
|
||||
Meter* memoryMeter;
|
||||
Meter* swapMeter;
|
||||
} MemorySwapMeterData;
|
||||
|
||||
static void MemorySwapMeter_updateValues(Meter* this) {
|
||||
MemorySwapMeterData* data = this->meterData;
|
||||
|
||||
Meter_updateValues(data->memoryMeter);
|
||||
Meter_updateValues(data->swapMeter);
|
||||
}
|
||||
|
||||
static void MemorySwapMeter_draw(Meter* this, int x, int y, int w) {
|
||||
MemorySwapMeterData* data = this->meterData;
|
||||
|
||||
assert(data->memoryMeter->draw);
|
||||
data->memoryMeter->draw(data->memoryMeter, x, y, w / 2);
|
||||
assert(data->swapMeter->draw);
|
||||
data->swapMeter->draw(data->swapMeter, x + w / 2, y, w - w / 2);
|
||||
}
|
||||
|
||||
static void MemorySwapMeter_init(Meter* this) {
|
||||
MemorySwapMeterData* data = this->meterData;
|
||||
|
||||
if (!data) {
|
||||
data = this->meterData = xMalloc(sizeof(MemorySwapMeterData));
|
||||
data->memoryMeter = NULL;
|
||||
data->swapMeter = NULL;
|
||||
}
|
||||
|
||||
if (!data->memoryMeter)
|
||||
data->memoryMeter = Meter_new(this->pl, 0, (const MeterClass*) Class(MemoryMeter));
|
||||
if (!data->swapMeter)
|
||||
data->swapMeter = Meter_new(this->pl, 0, (const MeterClass*) Class(SwapMeter));
|
||||
|
||||
if (Meter_initFn(data->memoryMeter))
|
||||
Meter_init(data->memoryMeter);
|
||||
if (Meter_initFn(data->swapMeter))
|
||||
Meter_init(data->swapMeter);
|
||||
|
||||
if (this->mode == 0)
|
||||
this->mode = BAR_METERMODE;
|
||||
|
||||
this->h = MAXIMUM(Meter_modes[data->memoryMeter->mode]->h, Meter_modes[data->swapMeter->mode]->h);
|
||||
}
|
||||
|
||||
static void MemorySwapMeter_updateMode(Meter* this, int mode) {
|
||||
MemorySwapMeterData* data = this->meterData;
|
||||
|
||||
this->mode = mode;
|
||||
|
||||
Meter_setMode(data->memoryMeter, mode);
|
||||
Meter_setMode(data->swapMeter, mode);
|
||||
|
||||
this->h = MAXIMUM(Meter_modes[data->memoryMeter->mode]->h, Meter_modes[data->swapMeter->mode]->h);
|
||||
}
|
||||
|
||||
static void MemorySwapMeter_done(Meter* this) {
|
||||
MemorySwapMeterData* data = this->meterData;
|
||||
|
||||
Meter_delete((Object*)data->swapMeter);
|
||||
Meter_delete((Object*)data->memoryMeter);
|
||||
|
||||
free(data);
|
||||
}
|
||||
|
||||
const MeterClass MemorySwapMeter_class = {
|
||||
.super = {
|
||||
.extends = Class(Meter),
|
||||
.delete = Meter_delete,
|
||||
},
|
||||
.updateValues = MemorySwapMeter_updateValues,
|
||||
.defaultMode = CUSTOM_METERMODE,
|
||||
.name = "MemorySwap",
|
||||
.uiName = "Memory & Swap",
|
||||
.description = "Combined memory and swap usage",
|
||||
.caption = "M&S",
|
||||
.draw = MemorySwapMeter_draw,
|
||||
.init = MemorySwapMeter_init,
|
||||
.updateMode = MemorySwapMeter_updateMode,
|
||||
.done = MemorySwapMeter_done
|
||||
};
|
15
MemorySwapMeter.h
Normal file
15
MemorySwapMeter.h
Normal file
@ -0,0 +1,15 @@
|
||||
#ifndef HEADER_MemorySwapMeter
|
||||
#define HEADER_MemorySwapMeter
|
||||
/*
|
||||
htop - MemorySwapMeter.h
|
||||
(C) 2021 htop dev team
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
#include "Meter.h"
|
||||
|
||||
|
||||
extern const MeterClass MemorySwapMeter_class;
|
||||
|
||||
#endif
|
111
Meter.c
111
Meter.c
@ -32,7 +32,7 @@ const MeterClass Meter_class = {
|
||||
}
|
||||
};
|
||||
|
||||
Meter* Meter_new(const struct ProcessList_* pl, int param, const MeterClass* type) {
|
||||
Meter* Meter_new(const struct ProcessList_* pl, unsigned int param, const MeterClass* type) {
|
||||
Meter* this = xCalloc(1, sizeof(Meter));
|
||||
Object_setClass(this, type);
|
||||
this->h = 1;
|
||||
@ -93,15 +93,14 @@ void Meter_delete(Object* cast) {
|
||||
}
|
||||
|
||||
void Meter_setCaption(Meter* this, const char* caption) {
|
||||
free(this->caption);
|
||||
this->caption = xStrdup(caption);
|
||||
free_and_xStrdup(&this->caption, caption);
|
||||
}
|
||||
|
||||
static inline void Meter_displayBuffer(const Meter* this, const char* buffer, RichString* out) {
|
||||
static inline void Meter_displayBuffer(const Meter* this, RichString* out) {
|
||||
if (Object_displayFn(this)) {
|
||||
Object_display(this, out);
|
||||
} else {
|
||||
RichString_writeWide(out, CRT_colors[Meter_attributes(this)[0]], buffer);
|
||||
RichString_writeWide(out, CRT_colors[Meter_attributes(this)[0]], this->txtBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
@ -132,21 +131,20 @@ void Meter_setMode(Meter* this, int modeIndex) {
|
||||
this->mode = modeIndex;
|
||||
}
|
||||
|
||||
ListItem* Meter_toListItem(Meter* this, bool moving) {
|
||||
ListItem* Meter_toListItem(const Meter* this, bool moving) {
|
||||
char mode[20];
|
||||
if (this->mode) {
|
||||
xSnprintf(mode, sizeof(mode), " [%s]", Meter_modes[this->mode]->uiName);
|
||||
} else {
|
||||
mode[0] = '\0';
|
||||
}
|
||||
char number[10];
|
||||
if (this->param > 0) {
|
||||
xSnprintf(number, sizeof(number), " %d", this->param);
|
||||
} else {
|
||||
number[0] = '\0';
|
||||
}
|
||||
char name[32];
|
||||
if (Meter_getUiNameFn(this))
|
||||
Meter_getUiName(this, name, sizeof(name));
|
||||
else
|
||||
xSnprintf(name, sizeof(name), "%s", Meter_uiName(this));
|
||||
char buffer[50];
|
||||
xSnprintf(buffer, sizeof(buffer), "%s%s%s", Meter_uiName(this), number, mode);
|
||||
xSnprintf(buffer, sizeof(buffer), "%s%s", name, mode);
|
||||
ListItem* li = ListItem_new(buffer, 0);
|
||||
li->moving = moving;
|
||||
return li;
|
||||
@ -155,23 +153,21 @@ ListItem* Meter_toListItem(Meter* this, bool moving) {
|
||||
/* ---------- TextMeterMode ---------- */
|
||||
|
||||
static void TextMeterMode_draw(Meter* this, int x, int y, int w) {
|
||||
char buffer[METER_BUFFER_LEN];
|
||||
Meter_updateValues(this, buffer, sizeof(buffer));
|
||||
|
||||
const char* caption = Meter_getCaption(this);
|
||||
attrset(CRT_colors[METER_TEXT]);
|
||||
mvaddnstr(y, x, this->caption, w - 1);
|
||||
mvaddnstr(y, x, caption, w - 1);
|
||||
attrset(CRT_colors[RESET_COLOR]);
|
||||
|
||||
int captionLen = strlen(this->caption);
|
||||
int captionLen = strlen(caption);
|
||||
x += captionLen;
|
||||
w -= captionLen;
|
||||
if (w <= 0)
|
||||
return;
|
||||
|
||||
RichString_begin(out);
|
||||
Meter_displayBuffer(this, buffer, &out);
|
||||
Meter_displayBuffer(this, &out);
|
||||
RichString_printoffnVal(out, y, x, 0, w - 1);
|
||||
RichString_end(out);
|
||||
RichString_delete(&out);
|
||||
}
|
||||
|
||||
/* ---------- BarMeterMode ---------- */
|
||||
@ -179,13 +175,11 @@ static void TextMeterMode_draw(Meter* this, int x, int y, int w) {
|
||||
static const char BarMeterMode_characters[] = "|#*@$%&.";
|
||||
|
||||
static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
|
||||
char buffer[METER_BUFFER_LEN];
|
||||
Meter_updateValues(this, buffer, sizeof(buffer));
|
||||
|
||||
const char* caption = Meter_getCaption(this);
|
||||
w -= 2;
|
||||
attrset(CRT_colors[METER_TEXT]);
|
||||
int captionLen = 3;
|
||||
mvaddnstr(y, x, this->caption, captionLen);
|
||||
mvaddnstr(y, x, caption, captionLen);
|
||||
x += captionLen;
|
||||
w -= captionLen;
|
||||
attrset(CRT_colors[BAR_BORDER]);
|
||||
@ -202,8 +196,8 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
|
||||
// The text in the bar is right aligned;
|
||||
// Pad with maximal spaces and then calculate needed starting position offset
|
||||
RichString_begin(bar);
|
||||
RichString_appendChr(&bar, ' ', w);
|
||||
RichString_appendWide(&bar, 0, buffer);
|
||||
RichString_appendChr(&bar, 0, ' ', w);
|
||||
RichString_appendWide(&bar, 0, this->txtBuffer);
|
||||
int startPos = RichString_sizeVal(bar) - w;
|
||||
if (startPos > w) {
|
||||
// Text is too large for bar
|
||||
@ -217,7 +211,7 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
|
||||
}
|
||||
}
|
||||
|
||||
// If still to large, print the start not the end
|
||||
// If still too large, print the start not the end
|
||||
startPos = MINIMUM(startPos, w);
|
||||
}
|
||||
assert(startPos >= 0);
|
||||
@ -264,7 +258,7 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
|
||||
RichString_printoffnVal(bar, y, x + offset, startPos + offset, w - offset);
|
||||
}
|
||||
|
||||
RichString_end(bar);
|
||||
RichString_delete(&bar);
|
||||
|
||||
move(y, x + w + 1);
|
||||
attrset(CRT_colors[RESET_COLOR]);
|
||||
@ -293,12 +287,13 @@ static const char* const GraphMeterMode_dotsAscii[] = {
|
||||
};
|
||||
|
||||
static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
|
||||
const ProcessList* pl = this->pl;
|
||||
|
||||
if (!this->drawData) {
|
||||
this->drawData = xCalloc(1, sizeof(GraphData));
|
||||
}
|
||||
GraphData* data = this->drawData;
|
||||
const int nValues = METER_BUFFER_LEN;
|
||||
const int nValues = METER_GRAPHDATA_SIZE;
|
||||
|
||||
const char* const* GraphMeterMode_dots;
|
||||
int GraphMeterMode_pixPerRow;
|
||||
@ -313,29 +308,24 @@ static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
|
||||
GraphMeterMode_pixPerRow = PIXPERROW_ASCII;
|
||||
}
|
||||
|
||||
const char* caption = Meter_getCaption(this);
|
||||
attrset(CRT_colors[METER_TEXT]);
|
||||
int captionLen = 3;
|
||||
mvaddnstr(y, x, this->caption, captionLen);
|
||||
mvaddnstr(y, x, caption, captionLen);
|
||||
x += captionLen;
|
||||
w -= captionLen;
|
||||
|
||||
struct timeval now;
|
||||
gettimeofday(&now, NULL);
|
||||
if (!timercmp(&now, &(data->time), <)) {
|
||||
if (!timercmp(&pl->realtime, &(data->time), <)) {
|
||||
int globalDelay = this->pl->settings->delay;
|
||||
struct timeval delay = { .tv_sec = globalDelay / 10, .tv_usec = (globalDelay - ((globalDelay / 10) * 10)) * 100000 };
|
||||
timeradd(&now, &delay, &(data->time));
|
||||
struct timeval delay = { .tv_sec = globalDelay / 10, .tv_usec = (globalDelay % 10) * 100000L };
|
||||
timeradd(&pl->realtime, &delay, &(data->time));
|
||||
|
||||
for (int i = 0; i < nValues - 1; i++)
|
||||
data->values[i] = data->values[i + 1];
|
||||
|
||||
char buffer[METER_BUFFER_LEN];
|
||||
Meter_updateValues(this, buffer, sizeof(buffer));
|
||||
|
||||
double value = 0.0;
|
||||
for (uint8_t i = 0; i < this->curItems; i++)
|
||||
value += this->values[i];
|
||||
value /= this->total;
|
||||
data->values[nValues - 1] = value;
|
||||
}
|
||||
|
||||
@ -346,8 +336,10 @@ static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
|
||||
}
|
||||
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);
|
||||
if (this->total < 1)
|
||||
this->total = 1;
|
||||
int v1 = CLAMP((int) lround(data->values[i] / this->total * pix), 1, pix);
|
||||
int v2 = CLAMP((int) lround(data->values[i + 1] / this->total * pix), 1, pix);
|
||||
|
||||
int colorIdx = GRAPH_1;
|
||||
for (int line = 0; line < GRAPH_HEIGHT; line++) {
|
||||
@ -384,12 +376,10 @@ static const char* const* LEDMeterMode_digits;
|
||||
|
||||
static void LEDMeterMode_drawDigit(int x, int y, int n) {
|
||||
for (int i = 0; i < 3; i++)
|
||||
mvaddstr(y+i, x, LEDMeterMode_digits[i * 10 + n]);
|
||||
mvaddstr(y + i, x, LEDMeterMode_digits[i * 10 + n]);
|
||||
}
|
||||
|
||||
static void LEDMeterMode_draw(Meter* this, int x, int y, int w) {
|
||||
(void) w;
|
||||
|
||||
#ifdef HAVE_LIBNCURSESW
|
||||
if (CRT_utf8)
|
||||
LEDMeterMode_digits = LEDMeterMode_digitsUtf8;
|
||||
@ -397,11 +387,8 @@ static void LEDMeterMode_draw(Meter* this, int x, int y, int w) {
|
||||
#endif
|
||||
LEDMeterMode_digits = LEDMeterMode_digitsAscii;
|
||||
|
||||
char buffer[METER_BUFFER_LEN];
|
||||
Meter_updateValues(this, buffer, sizeof(buffer));
|
||||
|
||||
RichString_begin(out);
|
||||
Meter_displayBuffer(this, buffer, &out);
|
||||
Meter_displayBuffer(this, &out);
|
||||
|
||||
int yText =
|
||||
#ifdef HAVE_LIBNCURSESW
|
||||
@ -409,21 +396,32 @@ static void LEDMeterMode_draw(Meter* this, int x, int y, int w) {
|
||||
#endif
|
||||
y + 2;
|
||||
attrset(CRT_colors[LED_COLOR]);
|
||||
mvaddstr(yText, x, this->caption);
|
||||
int xx = x + strlen(this->caption);
|
||||
const char* caption = Meter_getCaption(this);
|
||||
mvaddstr(yText, x, caption);
|
||||
int xx = x + strlen(caption);
|
||||
int len = RichString_sizeVal(out);
|
||||
for (int i = 0; i < len; i++) {
|
||||
int c = RichString_getCharVal(out, i);
|
||||
if (c >= '0' && c <= '9') {
|
||||
LEDMeterMode_drawDigit(xx, y, c - 48);
|
||||
if (xx - x + 4 > w)
|
||||
break;
|
||||
|
||||
LEDMeterMode_drawDigit(xx, y, c - '0');
|
||||
xx += 4;
|
||||
} else {
|
||||
if (xx - x + 1 > w)
|
||||
break;
|
||||
#ifdef HAVE_LIBNCURSESW
|
||||
const cchar_t wc = { .chars = { c, '\0' }, .attr = 0 }; /* use LED_COLOR from attrset() */
|
||||
mvadd_wch(yText, xx, &wc);
|
||||
#else
|
||||
mvaddch(yText, xx, c);
|
||||
#endif
|
||||
xx += 1;
|
||||
}
|
||||
}
|
||||
attrset(CRT_colors[RESET_COLOR]);
|
||||
RichString_end(out);
|
||||
RichString_delete(&out);
|
||||
}
|
||||
|
||||
static MeterMode BarMeterMode = {
|
||||
@ -461,14 +459,11 @@ const MeterMode* const Meter_modes[] = {
|
||||
|
||||
/* Blank meter */
|
||||
|
||||
static void BlankMeter_updateValues(ATTR_UNUSED Meter* this, char* buffer, size_t size) {
|
||||
if (size > 0) {
|
||||
*buffer = 0;
|
||||
}
|
||||
static void BlankMeter_updateValues(Meter* this) {
|
||||
this->txtBuffer[0] = '\0';
|
||||
}
|
||||
|
||||
static void BlankMeter_display(ATTR_UNUSED const Object* cast, RichString* out) {
|
||||
RichString_prune(out);
|
||||
static void BlankMeter_display(ATTR_UNUSED const Object* cast, ATTR_UNUSED RichString* out) {
|
||||
}
|
||||
|
||||
static const int BlankMeter_attributes[] = {
|
||||
|
29
Meter.h
29
Meter.h
@ -10,6 +10,7 @@ in the source distribution for its full text.
|
||||
#include "config.h" // IWYU pragma: keep
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
@ -18,7 +19,8 @@ in the source distribution for its full text.
|
||||
#include "ProcessList.h"
|
||||
|
||||
|
||||
#define METER_BUFFER_LEN 256
|
||||
#define METER_TXTBUFFER_LEN 256
|
||||
#define METER_GRAPHDATA_SIZE 256
|
||||
|
||||
#define METER_BUFFER_CHECK(buffer, size, written) \
|
||||
do { \
|
||||
@ -49,16 +51,20 @@ typedef struct Meter_ Meter;
|
||||
typedef void(*Meter_Init)(Meter*);
|
||||
typedef void(*Meter_Done)(Meter*);
|
||||
typedef void(*Meter_UpdateMode)(Meter*, int);
|
||||
typedef void(*Meter_UpdateValues)(Meter*, char*, size_t);
|
||||
typedef void(*Meter_UpdateValues)(Meter*);
|
||||
typedef void(*Meter_Draw)(Meter*, int, int, int);
|
||||
typedef const char* (*Meter_GetCaption)(const Meter*);
|
||||
typedef void(*Meter_GetUiName)(const Meter*, char*, size_t);
|
||||
|
||||
typedef struct MeterClass_ {
|
||||
const ObjectClass super;
|
||||
const Meter_Init init;
|
||||
const Meter_Done done;
|
||||
const Meter_UpdateMode updateMode;
|
||||
const Meter_Draw draw;
|
||||
const Meter_UpdateValues updateValues;
|
||||
const Meter_Draw draw;
|
||||
const Meter_GetCaption getCaption;
|
||||
const Meter_GetUiName getUiName;
|
||||
const int defaultMode;
|
||||
const double total;
|
||||
const int* const attributes;
|
||||
@ -77,8 +83,11 @@ typedef struct MeterClass_ {
|
||||
#define Meter_updateMode(this_, m_) As_Meter(this_)->updateMode((Meter*)(this_), m_)
|
||||
#define Meter_drawFn(this_) As_Meter(this_)->draw
|
||||
#define Meter_doneFn(this_) As_Meter(this_)->done
|
||||
#define Meter_updateValues(this_, buf_, sz_) \
|
||||
As_Meter(this_)->updateValues((Meter*)(this_), buf_, sz_)
|
||||
#define Meter_updateValues(this_) As_Meter(this_)->updateValues((Meter*)(this_))
|
||||
#define Meter_getUiNameFn(this_) As_Meter(this_)->getUiName
|
||||
#define Meter_getUiName(this_,n_,l_) As_Meter(this_)->getUiName((const Meter*)(this_),n_,l_)
|
||||
#define Meter_getCaptionFn(this_) As_Meter(this_)->getCaption
|
||||
#define Meter_getCaption(this_) (Meter_getCaptionFn(this_) ? As_Meter(this_)->getCaption((const Meter*)(this_)) : (this_)->caption)
|
||||
#define Meter_defaultMode(this_) As_Meter(this_)->defaultMode
|
||||
#define Meter_attributes(this_) As_Meter(this_)->attributes
|
||||
#define Meter_name(this_) As_Meter(this_)->name
|
||||
@ -86,7 +95,7 @@ typedef struct MeterClass_ {
|
||||
|
||||
typedef struct GraphData_ {
|
||||
struct timeval time;
|
||||
double values[METER_BUFFER_LEN];
|
||||
double values[METER_GRAPHDATA_SIZE];
|
||||
} GraphData;
|
||||
|
||||
struct Meter_ {
|
||||
@ -95,12 +104,14 @@ struct Meter_ {
|
||||
|
||||
char* caption;
|
||||
int mode;
|
||||
int param;
|
||||
unsigned int param;
|
||||
GraphData* drawData;
|
||||
int h;
|
||||
int columnWidthCount; /**< only used internally by the Header */
|
||||
const ProcessList* pl;
|
||||
uint8_t curItems;
|
||||
const int* curAttributes;
|
||||
char txtBuffer[METER_TXTBUFFER_LEN];
|
||||
double* values;
|
||||
double total;
|
||||
void* meterData;
|
||||
@ -123,7 +134,7 @@ typedef enum {
|
||||
|
||||
extern const MeterClass Meter_class;
|
||||
|
||||
Meter* Meter_new(const ProcessList* pl, int param, const MeterClass* type);
|
||||
Meter* Meter_new(const ProcessList* pl, unsigned int param, const MeterClass* type);
|
||||
|
||||
int Meter_humanUnit(char* buffer, unsigned long int value, size_t size);
|
||||
|
||||
@ -133,7 +144,7 @@ void Meter_setCaption(Meter* this, const char* caption);
|
||||
|
||||
void Meter_setMode(Meter* this, int modeIndex);
|
||||
|
||||
ListItem* Meter_toListItem(Meter* this, bool moving);
|
||||
ListItem* Meter_toListItem(const Meter* this, bool moving);
|
||||
|
||||
extern const MeterMode* const Meter_modes[];
|
||||
|
||||
|
@ -185,8 +185,7 @@ static HandlerResult MetersPanel_eventHandler(Panel* super, int ch) {
|
||||
Header* header = this->scr->header;
|
||||
this->settings->changed = true;
|
||||
Header_calculateHeight(header);
|
||||
Header_draw(header);
|
||||
ScreenManager_resize(this->scr, this->scr->x1, header->height, this->scr->x2, this->scr->y2);
|
||||
ScreenManager_resize(this->scr);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -216,7 +215,7 @@ MetersPanel* MetersPanel_new(Settings* settings, const char* header, Vector* met
|
||||
this->leftNeighbor = NULL;
|
||||
Panel_setHeader(super, header);
|
||||
for (int i = 0; i < Vector_size(meters); i++) {
|
||||
Meter* meter = (Meter*) Vector_get(meters, i);
|
||||
const Meter* meter = (const Meter*) Vector_get(meters, i);
|
||||
Panel_add(super, (Object*) Meter_toListItem(meter, false));
|
||||
}
|
||||
return this;
|
||||
|
@ -1,13 +1,14 @@
|
||||
#include "NetworkIOMeter.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <sys/time.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "CRT.h"
|
||||
#include "Macros.h"
|
||||
#include "Object.h"
|
||||
#include "Platform.h"
|
||||
#include "Process.h"
|
||||
#include "ProcessList.h"
|
||||
#include "RichString.h"
|
||||
#include "XUtils.h"
|
||||
|
||||
@ -19,71 +20,81 @@ static const int NetworkIOMeter_attributes[] = {
|
||||
|
||||
static bool hasData = false;
|
||||
|
||||
static unsigned long int cached_rxb_diff = 0;
|
||||
static unsigned long int cached_rxp_diff = 0;
|
||||
static unsigned long int cached_txb_diff = 0;
|
||||
static unsigned long int cached_txp_diff = 0;
|
||||
static uint32_t cached_rxb_diff;
|
||||
static uint32_t cached_rxp_diff;
|
||||
static uint32_t cached_txb_diff;
|
||||
static uint32_t cached_txp_diff;
|
||||
|
||||
static void NetworkIOMeter_updateValues(ATTR_UNUSED Meter* this, char* buffer, size_t len) {
|
||||
static unsigned long long int cached_last_update = 0;
|
||||
static void NetworkIOMeter_updateValues(Meter* this) {
|
||||
const ProcessList* pl = this->pl;
|
||||
static uint64_t cached_last_update = 0;
|
||||
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
unsigned long long int timeInMilliSeconds = (unsigned long long int)tv.tv_sec * 1000 + (unsigned long long int)tv.tv_usec / 1000;
|
||||
unsigned long long int passedTimeInMs = timeInMilliSeconds - cached_last_update;
|
||||
uint64_t passedTimeInMs = pl->realtimeMs - cached_last_update;
|
||||
|
||||
/* update only every 500ms */
|
||||
if (passedTimeInMs > 500) {
|
||||
static unsigned long int cached_rxb_total = 0;
|
||||
static unsigned long int cached_rxp_total = 0;
|
||||
static unsigned long int cached_txb_total = 0;
|
||||
static unsigned long int cached_txp_total = 0;
|
||||
static uint64_t cached_rxb_total;
|
||||
static uint64_t cached_rxp_total;
|
||||
static uint64_t cached_txb_total;
|
||||
static uint64_t cached_txp_total;
|
||||
uint64_t diff;
|
||||
|
||||
cached_last_update = timeInMilliSeconds;
|
||||
cached_last_update = pl->realtimeMs;
|
||||
|
||||
unsigned long int bytesReceived, packetsReceived, bytesTransmitted, packetsTransmitted;
|
||||
|
||||
hasData = Platform_getNetworkIO(&bytesReceived, &packetsReceived, &bytesTransmitted, &packetsTransmitted);
|
||||
NetworkIOData data;
|
||||
hasData = Platform_getNetworkIO(&data);
|
||||
if (!hasData) {
|
||||
xSnprintf(buffer, len, "no data");
|
||||
xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "no data");
|
||||
return;
|
||||
}
|
||||
|
||||
if (bytesReceived > cached_rxb_total) {
|
||||
cached_rxb_diff = (bytesReceived - cached_rxb_total) / 1024; /* Meter_humanUnit() expects unit in kilo */
|
||||
cached_rxb_diff = 1000.0 * cached_rxb_diff / passedTimeInMs; /* convert to per second */
|
||||
if (data.bytesReceived > cached_rxb_total) {
|
||||
diff = data.bytesReceived - cached_rxb_total;
|
||||
diff /= ONE_K; /* Meter_humanUnit() expects unit in kilo */
|
||||
diff = (1000 * diff) / passedTimeInMs; /* convert to per second */
|
||||
cached_rxb_diff = (uint32_t)diff;
|
||||
} else {
|
||||
cached_rxb_diff = 0;
|
||||
}
|
||||
cached_rxb_total = bytesReceived;
|
||||
cached_rxb_total = data.bytesReceived;
|
||||
|
||||
if (packetsReceived > cached_rxp_total) {
|
||||
cached_rxp_diff = packetsReceived - cached_rxp_total;
|
||||
if (data.packetsReceived > cached_rxp_total) {
|
||||
diff = data.packetsReceived - cached_rxp_total;
|
||||
cached_rxp_diff = (uint32_t)diff;
|
||||
} else {
|
||||
cached_rxp_diff = 0;
|
||||
}
|
||||
cached_rxp_total = packetsReceived;
|
||||
cached_rxp_total = data.packetsReceived;
|
||||
|
||||
if (bytesTransmitted > cached_txb_total) {
|
||||
cached_txb_diff = (bytesTransmitted - cached_txb_total) / 1024; /* Meter_humanUnit() expects unit in kilo */
|
||||
cached_txb_diff = 1000.0 * cached_txb_diff / passedTimeInMs; /* convert to per second */
|
||||
if (data.bytesTransmitted > cached_txb_total) {
|
||||
diff = data.bytesTransmitted - cached_txb_total;
|
||||
diff /= ONE_K; /* Meter_humanUnit() expects unit in kilo */
|
||||
diff = (1000 * diff) / passedTimeInMs; /* convert to per second */
|
||||
cached_txb_diff = (uint32_t)diff;
|
||||
} else {
|
||||
cached_txb_diff = 0;
|
||||
}
|
||||
cached_txb_total = bytesTransmitted;
|
||||
cached_txb_total = data.bytesTransmitted;
|
||||
|
||||
if (packetsTransmitted > cached_txp_total) {
|
||||
cached_txp_diff = packetsTransmitted - cached_txp_total;
|
||||
if (data.packetsTransmitted > cached_txp_total) {
|
||||
diff = data.packetsTransmitted - cached_txp_total;
|
||||
cached_txp_diff = (uint32_t)diff;
|
||||
} else {
|
||||
cached_txp_diff = 0;
|
||||
}
|
||||
cached_txp_total = packetsTransmitted;
|
||||
cached_txp_total = data.packetsTransmitted;
|
||||
}
|
||||
|
||||
this->values[0] = cached_rxb_diff;
|
||||
this->values[1] = cached_txb_diff;
|
||||
if (cached_rxb_diff + cached_txb_diff > this->total) {
|
||||
this->total = cached_rxb_diff + cached_txb_diff;
|
||||
}
|
||||
|
||||
char bufferBytesReceived[12], bufferBytesTransmitted[12];
|
||||
Meter_humanUnit(bufferBytesReceived, cached_rxb_diff, sizeof(bufferBytesReceived));
|
||||
Meter_humanUnit(bufferBytesTransmitted, cached_txb_diff, sizeof(bufferBytesTransmitted));
|
||||
xSnprintf(buffer, len, "rx:%siB/s tx:%siB/s", bufferBytesReceived, bufferBytesTransmitted);
|
||||
xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "rx:%siB/s tx:%siB/s", bufferBytesReceived, bufferBytesTransmitted);
|
||||
}
|
||||
|
||||
static void NetworkIOMeter_display(ATTR_UNUSED const Object* cast, RichString* out) {
|
||||
@ -93,6 +104,7 @@ static void NetworkIOMeter_display(ATTR_UNUSED const Object* cast, RichString* o
|
||||
}
|
||||
|
||||
char buffer[64];
|
||||
int len;
|
||||
|
||||
RichString_writeAscii(out, CRT_colors[METER_TEXT], "rx: ");
|
||||
Meter_humanUnit(buffer, cached_rxb_diff, sizeof(buffer));
|
||||
@ -104,8 +116,8 @@ static void NetworkIOMeter_display(ATTR_UNUSED const Object* cast, RichString* o
|
||||
RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], buffer);
|
||||
RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], "iB/s");
|
||||
|
||||
xSnprintf(buffer, sizeof(buffer), " (%lu/%lu packets) ", cached_rxp_diff, cached_txp_diff);
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], buffer);
|
||||
len = xSnprintf(buffer, sizeof(buffer), " (%u/%u packets) ", cached_rxp_diff, cached_txp_diff);
|
||||
RichString_appendnAscii(out, CRT_colors[METER_TEXT], buffer, len);
|
||||
}
|
||||
|
||||
const MeterClass NetworkIOMeter_class = {
|
||||
@ -116,7 +128,7 @@ const MeterClass NetworkIOMeter_class = {
|
||||
},
|
||||
.updateValues = NetworkIOMeter_updateValues,
|
||||
.defaultMode = TEXT_METERMODE,
|
||||
.maxItems = 0,
|
||||
.maxItems = 2,
|
||||
.total = 100.0,
|
||||
.attributes = NetworkIOMeter_attributes,
|
||||
.name = "NetworkIO",
|
||||
|
@ -3,6 +3,14 @@
|
||||
|
||||
#include "Meter.h"
|
||||
|
||||
|
||||
typedef struct NetworkIOData_ {
|
||||
uint64_t bytesReceived;
|
||||
uint64_t packetsReceived;
|
||||
uint64_t bytesTransmitted;
|
||||
uint64_t packetsTransmitted;
|
||||
} NetworkIOData;
|
||||
|
||||
extern const MeterClass NetworkIOMeter_class;
|
||||
|
||||
#endif /* HEADER_NetworkIOMeter */
|
||||
|
4
Object.c
4
Object.c
@ -15,8 +15,6 @@ const ObjectClass Object_class = {
|
||||
.extends = NULL
|
||||
};
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
||||
bool Object_isA(const Object* o, const ObjectClass* klass) {
|
||||
if (!o)
|
||||
return false;
|
||||
@ -29,5 +27,3 @@ bool Object_isA(const Object* o, const ObjectClass* klass) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif /* NDEBUG */
|
||||
|
9
Object.h
9
Object.h
@ -11,14 +11,11 @@ in the source distribution for its full text.
|
||||
#include "config.h" // IWYU pragma: keep
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "RichString.h"
|
||||
#include "XUtils.h" // IWYU pragma: keep
|
||||
|
||||
#ifndef NDEBUG
|
||||
#include <stdbool.h>
|
||||
#endif
|
||||
|
||||
|
||||
struct Object_;
|
||||
typedef struct Object_ Object;
|
||||
@ -57,10 +54,6 @@ typedef union {
|
||||
|
||||
extern const ObjectClass Object_class;
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
||||
bool Object_isA(const Object* o, const ObjectClass* klass);
|
||||
|
||||
#endif /* NDEBUG */
|
||||
|
||||
#endif
|
||||
|
@ -13,6 +13,7 @@ in the source distribution for its full text.
|
||||
#include "Object.h"
|
||||
#include "Process.h"
|
||||
|
||||
|
||||
typedef struct OpenFilesScreen_ {
|
||||
InfoScreen super;
|
||||
pid_t pid;
|
||||
|
@ -53,7 +53,7 @@ static void NumberItem_display(const Object* cast, RichString* out) {
|
||||
} else {
|
||||
written = xSnprintf(buffer, sizeof(buffer), "%d", NumberItem_get(this));
|
||||
}
|
||||
RichString_appendAscii(out, CRT_colors[CHECK_MARK], buffer);
|
||||
RichString_appendnAscii(out, CRT_colors[CHECK_MARK], buffer, written);
|
||||
RichString_appendAscii(out, CRT_colors[CHECK_BOX], "]");
|
||||
for (int i = written; i < 5; i++) {
|
||||
RichString_appendAscii(out, CRT_colors[CHECK_BOX], " ");
|
||||
|
24
Panel.c
24
Panel.c
@ -69,7 +69,7 @@ void Panel_done(Panel* this) {
|
||||
free(this->eventHandlerState);
|
||||
Vector_delete(this->items);
|
||||
FunctionBar_delete(this->defaultBar);
|
||||
RichString_end(this->header);
|
||||
RichString_delete(&this->header);
|
||||
}
|
||||
|
||||
void Panel_setSelectionColor(Panel* this, ColorElements colorId) {
|
||||
@ -172,13 +172,13 @@ void Panel_moveSelectedDown(Panel* this) {
|
||||
}
|
||||
}
|
||||
|
||||
int Panel_getSelectedIndex(Panel* this) {
|
||||
int Panel_getSelectedIndex(const Panel* this) {
|
||||
assert (this != NULL);
|
||||
|
||||
return this->selected;
|
||||
}
|
||||
|
||||
int Panel_size(Panel* this) {
|
||||
int Panel_size(const Panel* this) {
|
||||
assert (this != NULL);
|
||||
|
||||
return Vector_size(this->items);
|
||||
@ -246,8 +246,8 @@ void Panel_draw(Panel* this, bool force_redraw, bool focus, bool highlightSelect
|
||||
if (this->scrollV < 0) {
|
||||
this->scrollV = 0;
|
||||
this->needsRedraw = true;
|
||||
} else if (this->scrollV >= size) {
|
||||
this->scrollV = MAXIMUM(size - 1, 0);
|
||||
} else if (this->scrollV > size - h) {
|
||||
this->scrollV = MAXIMUM(size - h, 0);
|
||||
this->needsRedraw = true;
|
||||
}
|
||||
// ensure selection is on screen
|
||||
@ -269,7 +269,7 @@ void Panel_draw(Panel* this, bool force_redraw, bool focus, bool highlightSelect
|
||||
if (this->needsRedraw || force_redraw) {
|
||||
int line = 0;
|
||||
for (int i = first; line < h && i < upTo; i++) {
|
||||
Object* itemObj = Vector_get(this->items, i);
|
||||
const Object* itemObj = Vector_get(this->items, i);
|
||||
RichString_begin(item);
|
||||
Object_display(itemObj, &item);
|
||||
int itemLen = RichString_sizeVal(item);
|
||||
@ -287,7 +287,7 @@ void Panel_draw(Panel* this, bool force_redraw, bool focus, bool highlightSelect
|
||||
RichString_printoffnVal(item, y + line, x, scrollH, amt);
|
||||
if (item.highlightAttr)
|
||||
attrset(CRT_colors[RESET_COLOR]);
|
||||
RichString_end(item);
|
||||
RichString_delete(&item);
|
||||
line++;
|
||||
}
|
||||
while (line < h) {
|
||||
@ -296,11 +296,11 @@ void Panel_draw(Panel* this, bool force_redraw, bool focus, bool highlightSelect
|
||||
}
|
||||
|
||||
} else {
|
||||
Object* oldObj = Vector_get(this->items, this->oldSelected);
|
||||
const Object* oldObj = Vector_get(this->items, this->oldSelected);
|
||||
RichString_begin(old);
|
||||
Object_display(oldObj, &old);
|
||||
int oldLen = RichString_sizeVal(old);
|
||||
Object* newObj = Vector_get(this->items, this->selected);
|
||||
const Object* newObj = Vector_get(this->items, this->selected);
|
||||
RichString_begin(new);
|
||||
Object_display(newObj, &new);
|
||||
int newLen = RichString_sizeVal(new);
|
||||
@ -316,8 +316,8 @@ void Panel_draw(Panel* this, bool force_redraw, bool focus, bool highlightSelect
|
||||
RichString_printoffnVal(new, y + this->selected - first, x,
|
||||
scrollH, MINIMUM(newLen - scrollH, this->w));
|
||||
attrset(CRT_colors[RESET_COLOR]);
|
||||
RichString_end(new);
|
||||
RichString_end(old);
|
||||
RichString_delete(&new);
|
||||
RichString_delete(&old);
|
||||
}
|
||||
|
||||
if (focus && (this->needsRedraw || force_redraw || !this->wasFocus)) {
|
||||
@ -454,7 +454,7 @@ HandlerResult Panel_selectByTyping(Panel* this, int ch) {
|
||||
|
||||
if (len < 99) {
|
||||
buffer[len] = ch;
|
||||
buffer[len+1] = '\0';
|
||||
buffer[len + 1] = '\0';
|
||||
}
|
||||
|
||||
for (int try = 0; try < 2; try++) {
|
||||
|
10
Panel.h
10
Panel.h
@ -7,6 +7,9 @@ Released under the GNU GPLv2, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
#include "config.h" // IWYU pragma: keep
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "CRT.h"
|
||||
@ -26,7 +29,8 @@ typedef enum HandlerResult_ {
|
||||
REFRESH = 0x08,
|
||||
REDRAW = 0x10,
|
||||
RESCAN = 0x20,
|
||||
SYNTH_KEY = 0x40,
|
||||
RESIZE = 0x40,
|
||||
SYNTH_KEY = 0x80,
|
||||
} HandlerResult;
|
||||
|
||||
#define EVENT_SET_SELECTED (-1)
|
||||
@ -112,9 +116,9 @@ void Panel_moveSelectedUp(Panel* this);
|
||||
|
||||
void Panel_moveSelectedDown(Panel* this);
|
||||
|
||||
int Panel_getSelectedIndex(Panel* this);
|
||||
int Panel_getSelectedIndex(const Panel* this);
|
||||
|
||||
int Panel_size(Panel* this);
|
||||
int Panel_size(const Panel* this);
|
||||
|
||||
void Panel_setSelected(Panel* this, int selected);
|
||||
|
||||
|
262
Process.h
262
Process.h
@ -17,7 +17,9 @@ in the source distribution for its full text.
|
||||
#include "RichString.h"
|
||||
|
||||
|
||||
#define PROCESS_FLAG_IO 0x0001
|
||||
#define PROCESS_FLAG_IO 0x00000001
|
||||
#define PROCESS_FLAG_CWD 0x00000002
|
||||
|
||||
#define DEFAULT_HIGHLIGHT_SECS 5
|
||||
|
||||
typedef enum ProcessField_ {
|
||||
@ -28,7 +30,7 @@ typedef enum ProcessField_ {
|
||||
PPID = 4,
|
||||
PGRP = 5,
|
||||
SESSION = 6,
|
||||
TTY_NR = 7,
|
||||
TTY = 7,
|
||||
TPGID = 8,
|
||||
MINFLT = 10,
|
||||
MAJFLT = 12,
|
||||
@ -46,90 +48,242 @@ typedef enum ProcessField_ {
|
||||
NLWP = 51,
|
||||
TGID = 52,
|
||||
PERCENT_NORM_CPU = 53,
|
||||
ELAPSED = 54,
|
||||
PROC_COMM = 124,
|
||||
PROC_EXE = 125,
|
||||
CWD = 126,
|
||||
|
||||
/* Platform specific fields, defined in ${platform}/ProcessField.h */
|
||||
PLATFORM_PROCESS_FIELDS
|
||||
|
||||
/* Do not add new fields after this entry (dynamic entries follow) */
|
||||
LAST_PROCESSFIELD
|
||||
} ProcessField;
|
||||
|
||||
struct Settings_;
|
||||
|
||||
/* Holds information about regions of the cmdline that should be
|
||||
* highlighted (e.g. program basename, delimiter, comm). */
|
||||
typedef struct ProcessCmdlineHighlight_ {
|
||||
size_t offset; /* first character to highlight */
|
||||
size_t length; /* How many characters to highlight, zero if unused */
|
||||
int attr; /* The attributes used to highlight */
|
||||
int flags; /* Special flags used for selective highlighting, zero for always */
|
||||
} ProcessCmdlineHighlight;
|
||||
|
||||
/* ProcessMergedCommand is populated by Process_makeCommandStr: It
|
||||
* contains the merged Command string, and the information needed by
|
||||
* Process_writeCommand to color the string. str will be NULL for kernel
|
||||
* threads and zombies */
|
||||
typedef struct ProcessMergedCommand_ {
|
||||
char* str; /* merged Command string */
|
||||
size_t highlightCount; /* how many portions of cmdline to highlight */
|
||||
ProcessCmdlineHighlight highlights[8]; /* which portions of cmdline to highlight */
|
||||
bool separateComm : 1; /* whether comm is a separate field */
|
||||
bool unmatchedExe : 1; /* whether exe matched with cmdline */
|
||||
bool cmdlineChanged : 1; /* whether cmdline changed */
|
||||
bool exeChanged : 1; /* whether exe changed */
|
||||
bool commChanged : 1; /* whether comm changed */
|
||||
bool prevMergeSet : 1; /* whether showMergedCommand was set */
|
||||
bool prevPathSet : 1; /* whether showProgramPath was set */
|
||||
bool prevCommSet : 1; /* whether findCommInCmdline was set */
|
||||
bool prevCmdlineSet : 1; /* whether stripExeFromCmdline was set */
|
||||
bool prevShowThreadNames : 1; /* whether showThreadNames was set */
|
||||
} ProcessMergedCommand;
|
||||
|
||||
typedef struct Process_ {
|
||||
/* Super object for emulated OOP */
|
||||
Object super;
|
||||
|
||||
/* Pointer to quasi-global data structures */
|
||||
const struct ProcessList_* processList;
|
||||
const struct Settings_* settings;
|
||||
|
||||
unsigned long long int time;
|
||||
/* Process identifier */
|
||||
pid_t pid;
|
||||
|
||||
/* Parent process identifier */
|
||||
pid_t ppid;
|
||||
|
||||
/* Thread group identifier */
|
||||
pid_t tgid;
|
||||
char* comm; /* use Process_getCommand() for Command actually displayed */
|
||||
int commLen;
|
||||
int indent;
|
||||
|
||||
int basenameOffset;
|
||||
bool updated;
|
||||
/* Process group identifier */
|
||||
int pgrp;
|
||||
|
||||
char state;
|
||||
bool tag;
|
||||
bool showChildren;
|
||||
bool show;
|
||||
bool wasShown;
|
||||
unsigned int pgrp;
|
||||
unsigned int session;
|
||||
unsigned int tty_nr;
|
||||
/* Session identifier */
|
||||
int session;
|
||||
|
||||
/* Foreground group identifier of the controlling terminal */
|
||||
int tpgid;
|
||||
uid_t st_uid;
|
||||
unsigned long int flags;
|
||||
int processor;
|
||||
|
||||
float percent_cpu;
|
||||
float percent_mem;
|
||||
/* This is a kernel (helper) task */
|
||||
bool isKernelThread;
|
||||
|
||||
/* This is a userland thread / LWP */
|
||||
bool isUserlandThread;
|
||||
|
||||
/* Controlling terminal identifier of the process */
|
||||
unsigned long int tty_nr;
|
||||
|
||||
/* Controlling terminal name of the process */
|
||||
char* tty_name;
|
||||
|
||||
/* User identifier */
|
||||
uid_t st_uid;
|
||||
|
||||
/* User name */
|
||||
const char* user;
|
||||
|
||||
/* Process runtime (in hundredth of a second) */
|
||||
unsigned long long int time;
|
||||
|
||||
/*
|
||||
* Process name including arguments.
|
||||
* Use Process_getCommand() for Command actually displayed.
|
||||
*/
|
||||
char* cmdline;
|
||||
|
||||
/* End Offset in cmdline of the process basename */
|
||||
int cmdlineBasenameEnd;
|
||||
|
||||
/* Start Offset in cmdline of the process basename */
|
||||
int cmdlineBasenameStart;
|
||||
|
||||
/* The process' "command" name */
|
||||
char* procComm;
|
||||
|
||||
/* The main process executable */
|
||||
char* procExe;
|
||||
|
||||
/* The process/thread working directory */
|
||||
char* procCwd;
|
||||
|
||||
/* Offset in procExe of the process basename */
|
||||
int procExeBasenameOffset;
|
||||
|
||||
/* Tells if the executable has been replaced in the filesystem since start */
|
||||
bool procExeDeleted;
|
||||
|
||||
/* Tells if the process uses replaced shared libraries since start */
|
||||
bool usesDeletedLib;
|
||||
|
||||
/* CPU number last executed on */
|
||||
int processor;
|
||||
|
||||
/* CPU usage during last cycle (in percent) */
|
||||
float percent_cpu;
|
||||
|
||||
/* Memory usage during last cycle (in percent) */
|
||||
float percent_mem;
|
||||
|
||||
/* Scheduling priority */
|
||||
long int priority;
|
||||
|
||||
/* Nice value */
|
||||
long int nice;
|
||||
|
||||
/* Number of threads in this process */
|
||||
long int nlwp;
|
||||
char starttime_show[8];
|
||||
|
||||
/* Process start time (in seconds elapsed since the Epoch) */
|
||||
time_t starttime_ctime;
|
||||
|
||||
/* Process start time (cached formatted string) */
|
||||
char starttime_show[8];
|
||||
|
||||
/* Total program size (in kilobytes) */
|
||||
long m_virt;
|
||||
|
||||
/* Resident set size (in kilobytes) */
|
||||
long m_resident;
|
||||
|
||||
int exit_signal;
|
||||
|
||||
time_t seenTs;
|
||||
time_t tombTs;
|
||||
|
||||
/* Number of minor faults the process has made which have not required loading a memory page from disk */
|
||||
unsigned long int minflt;
|
||||
|
||||
/* Number of major faults the process has made which have required loading a memory page from disk */
|
||||
unsigned long int majflt;
|
||||
|
||||
/*
|
||||
* Process state (platform dependent):
|
||||
* D - Waiting
|
||||
* I - Idle
|
||||
* L - Acquiring lock
|
||||
* R - Running
|
||||
* S - Sleeping
|
||||
* T - Stopped (on a signal)
|
||||
* X - Dead
|
||||
* Z - Zombie
|
||||
* t - Tracing stop
|
||||
* ? - Unknown
|
||||
*/
|
||||
char state;
|
||||
|
||||
/* Whether the process was updated during the current scan */
|
||||
bool updated;
|
||||
|
||||
/* Whether the process was tagged by the user */
|
||||
bool tag;
|
||||
|
||||
/* Whether to display this process */
|
||||
bool show;
|
||||
|
||||
/* Whether this process was shown last cycle */
|
||||
bool wasShown;
|
||||
|
||||
/* Whether to show children of this process in tree-mode */
|
||||
bool showChildren;
|
||||
|
||||
/*
|
||||
* Internal time counts for showing new and exited processes.
|
||||
*/
|
||||
uint64_t seenStampMs;
|
||||
uint64_t tombStampMs;
|
||||
|
||||
/*
|
||||
* Internal state for tree-mode.
|
||||
*/
|
||||
int indent;
|
||||
unsigned int tree_left;
|
||||
unsigned int tree_right;
|
||||
unsigned int tree_depth;
|
||||
unsigned int tree_index;
|
||||
|
||||
/*
|
||||
* Internal state for merged Command display
|
||||
*/
|
||||
ProcessMergedCommand mergedCommand;
|
||||
} Process;
|
||||
|
||||
typedef struct ProcessFieldData_ {
|
||||
/* Name (displayed in setup menu) */
|
||||
const char* name;
|
||||
|
||||
/* Title (display in main screen); must have same width as the printed values */
|
||||
const char* title;
|
||||
|
||||
/* Description (displayed in setup menu) */
|
||||
const char* description;
|
||||
|
||||
/* Scan flag to enable scan-method otherwise not run */
|
||||
uint32_t flags;
|
||||
|
||||
/* Whether the values are process identifiers; adjusts the width of title and values if true */
|
||||
bool pidColumn;
|
||||
|
||||
/* Whether the column should be sorted in descending order by default */
|
||||
bool defaultSortDesc;
|
||||
} ProcessFieldData;
|
||||
|
||||
// Implemented in platform-specific code:
|
||||
void Process_writeField(const Process* this, RichString* str, ProcessField field);
|
||||
int Process_compare(const void* v1, const void* v2);
|
||||
void Process_delete(Object* cast);
|
||||
bool Process_isThread(const Process* this);
|
||||
extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD];
|
||||
#define PROCESS_MAX_PID_DIGITS 19
|
||||
extern int Process_pidDigits;
|
||||
|
||||
typedef Process*(*Process_New)(const struct Settings_*);
|
||||
typedef Process* (*Process_New)(const struct Settings_*);
|
||||
typedef void (*Process_WriteField)(const Process*, RichString*, ProcessField);
|
||||
typedef int (*Process_CompareByKey)(const Process*, const Process*, ProcessField);
|
||||
typedef const char* (*Process_GetCommandStr)(const Process*);
|
||||
@ -143,7 +297,7 @@ typedef struct ProcessClass_ {
|
||||
|
||||
#define As_Process(this_) ((const ProcessClass*)((this_)->super.klass))
|
||||
|
||||
#define Process_getCommand(this_) (As_Process(this_)->getCommandStr ? As_Process(this_)->getCommandStr((const Process*)(this_)) : ((const Process*)(this_))->comm)
|
||||
#define Process_getCommand(this_) (As_Process(this_)->getCommandStr ? As_Process(this_)->getCommandStr((const Process*)(this_)) : Process_getCommandStr((const Process*)(this_)))
|
||||
#define Process_compareByKey(p1_, p2_, key_) (As_Process(p1_)->compareByKey ? (As_Process(p1_)->compareByKey(p1_, p2_, key_)) : Process_compareByKey_Base(p1_, p2_, key_))
|
||||
|
||||
static inline pid_t Process_getParentPid(const Process* this) {
|
||||
@ -154,31 +308,54 @@ static inline bool Process_isChildOf(const Process* this, pid_t pid) {
|
||||
return pid == Process_getParentPid(this);
|
||||
}
|
||||
|
||||
#define Process_sortState(state) ((state) == 'I' ? 0x100 : (state))
|
||||
static inline bool Process_isKernelThread(const Process* this) {
|
||||
return this->isKernelThread;
|
||||
}
|
||||
|
||||
static inline bool Process_isUserlandThread(const Process* this) {
|
||||
return this->isUserlandThread;
|
||||
}
|
||||
|
||||
static inline bool Process_isThread(const Process* this) {
|
||||
return Process_isUserlandThread(this) || Process_isKernelThread(this);
|
||||
}
|
||||
|
||||
#define CMDLINE_HIGHLIGHT_FLAG_SEPARATOR 0x00000001
|
||||
#define CMDLINE_HIGHLIGHT_FLAG_BASENAME 0x00000002
|
||||
#define CMDLINE_HIGHLIGHT_FLAG_COMM 0x00000004
|
||||
#define CMDLINE_HIGHLIGHT_FLAG_DELETED 0x00000008
|
||||
|
||||
#define ONE_K 1024UL
|
||||
#define ONE_M (ONE_K * ONE_K)
|
||||
#define ONE_G (ONE_M * ONE_K)
|
||||
#define ONE_T (1ULL * ONE_G * ONE_K)
|
||||
#define ONE_P (1ULL * ONE_T * ONE_K)
|
||||
|
||||
#define ONE_DECIMAL_K 1000UL
|
||||
#define ONE_DECIMAL_M (ONE_DECIMAL_K * ONE_DECIMAL_K)
|
||||
#define ONE_DECIMAL_G (ONE_DECIMAL_M * ONE_DECIMAL_K)
|
||||
#define ONE_DECIMAL_T (1ULL * ONE_DECIMAL_G * ONE_DECIMAL_K)
|
||||
#define ONE_DECIMAL_P (1ULL * ONE_DECIMAL_T * ONE_DECIMAL_K)
|
||||
|
||||
void Process_setupColumnWidths(void);
|
||||
|
||||
void Process_humanNumber(RichString* str, unsigned long long number, bool coloring);
|
||||
/* Takes number in bytes (base 1024). Prints 6 columns. */
|
||||
void Process_printBytes(RichString* str, unsigned long long number, bool coloring);
|
||||
|
||||
void Process_colorNumber(RichString* str, unsigned long long number, bool coloring);
|
||||
/* Takes number in kilo bytes (base 1024). Prints 6 columns. */
|
||||
void Process_printKBytes(RichString* str, unsigned long long number, bool coloring);
|
||||
|
||||
void Process_printTime(RichString* str, unsigned long long totalHundredths);
|
||||
/* Takes number as count (base 1000). Prints 12 columns. */
|
||||
void Process_printCount(RichString* str, unsigned long long number, bool coloring);
|
||||
|
||||
/* Takes time in hundredths of a seconds. Prints 9 columns. */
|
||||
void Process_printTime(RichString* str, unsigned long long totalHundredths, bool coloring);
|
||||
|
||||
/* Takes rate in bare unit (base 1024) per second. Prints 12 columns. */
|
||||
void Process_printRate(RichString* str, double rate, bool coloring);
|
||||
|
||||
void Process_fillStarttimeBuffer(Process* this);
|
||||
|
||||
void Process_outputRate(RichString* str, char* buffer, size_t n, double rate, int coloring);
|
||||
|
||||
void Process_printLeftAlignedField(RichString* str, int attr, const char* content, unsigned int width);
|
||||
|
||||
void Process_display(const Object* cast, RichString* out);
|
||||
@ -205,4 +382,17 @@ int Process_pidCompare(const void* v1, const void* v2);
|
||||
|
||||
int Process_compareByKey_Base(const Process* p1, const Process* p2, ProcessField key);
|
||||
|
||||
// Avoid direct calls, use Process_getCommand instead
|
||||
const char* Process_getCommandStr(const Process* this);
|
||||
|
||||
void Process_updateComm(Process* this, const char* comm);
|
||||
void Process_updateCmdline(Process* this, const char* cmdline, int basenameStart, int basenameEnd);
|
||||
void Process_updateExe(Process* this, const char* exe);
|
||||
|
||||
/* This function constructs the string that is displayed by
|
||||
* Process_writeCommand and also returned by Process_getCommandStr */
|
||||
void Process_makeCommandStr(Process* this);
|
||||
|
||||
void Process_writeCommand(const Process* this, int attr, int baseAttr, RichString* str);
|
||||
|
||||
#endif
|
||||
|
153
ProcessList.c
153
ProcessList.c
@ -11,15 +11,16 @@ in the source distribution for its full text.
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "Compat.h"
|
||||
#include "CRT.h"
|
||||
#include "DynamicColumn.h"
|
||||
#include "Hashtable.h"
|
||||
#include "Macros.h"
|
||||
#include "Platform.h"
|
||||
#include "Vector.h"
|
||||
#include "XUtils.h"
|
||||
|
||||
|
||||
ProcessList* ProcessList_init(ProcessList* this, const ObjectClass* klass, UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) {
|
||||
ProcessList* ProcessList_init(ProcessList* this, const ObjectClass* klass, UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* dynamicColumns, Hashtable* pidMatchList, uid_t userId) {
|
||||
this->processes = Vector_new(klass, true, DEFAULT_SIZE);
|
||||
this->processes2 = Vector_new(klass, true, DEFAULT_SIZE); // tree-view auxiliary buffer
|
||||
|
||||
@ -29,13 +30,18 @@ ProcessList* ProcessList_init(ProcessList* this, const ObjectClass* klass, Users
|
||||
|
||||
this->usersTable = usersTable;
|
||||
this->pidMatchList = pidMatchList;
|
||||
this->dynamicMeters = dynamicMeters;
|
||||
this->dynamicColumns = dynamicColumns;
|
||||
|
||||
this->userId = userId;
|
||||
|
||||
// set later by platform-specific code
|
||||
this->cpuCount = 0;
|
||||
this->activeCPUs = 0;
|
||||
this->existingCPUs = 0;
|
||||
this->monotonicMs = 0;
|
||||
|
||||
this->scanTs = 0;
|
||||
// always maintain valid realtime timestamps
|
||||
Platform_gettime_realtime(&this->realtime, &this->realtimeMs);
|
||||
|
||||
#ifdef HAVE_LIBHWLOC
|
||||
this->topologyOk = false;
|
||||
@ -79,7 +85,22 @@ void ProcessList_setPanel(ProcessList* this, Panel* panel) {
|
||||
this->panel = panel;
|
||||
}
|
||||
|
||||
static const char* alignedProcessFieldTitle(ProcessField field) {
|
||||
static const char* alignedDynamicColumnTitle(const ProcessList* this, int key) {
|
||||
const DynamicColumn* column = Hashtable_get(this->dynamicColumns, key);
|
||||
if (column == NULL)
|
||||
return "- ";
|
||||
static char titleBuffer[DYNAMIC_MAX_COLUMN_WIDTH + /* space */ 1 + /* null terminator */ + 1];
|
||||
int width = column->width;
|
||||
if (!width || abs(width) > DYNAMIC_MAX_COLUMN_WIDTH)
|
||||
width = DYNAMIC_DEFAULT_COLUMN_WIDTH;
|
||||
xSnprintf(titleBuffer, sizeof(titleBuffer), "%*s", width, column->heading);
|
||||
return titleBuffer;
|
||||
}
|
||||
|
||||
static const char* alignedProcessFieldTitle(const ProcessList* this, ProcessField field) {
|
||||
if (field >= LAST_PROCESSFIELD)
|
||||
return alignedDynamicColumnTitle(this, field);
|
||||
|
||||
const char* title = Process_fields[field].title;
|
||||
if (!title)
|
||||
return "- ";
|
||||
@ -93,8 +114,8 @@ static const char* alignedProcessFieldTitle(ProcessField field) {
|
||||
return titleBuffer;
|
||||
}
|
||||
|
||||
void ProcessList_printHeader(ProcessList* this, RichString* header) {
|
||||
RichString_prune(header);
|
||||
void ProcessList_printHeader(const ProcessList* this, RichString* header) {
|
||||
RichString_rewind(header, RichString_size(header));
|
||||
|
||||
const Settings* settings = this->settings;
|
||||
const ProcessField* fields = settings->fields;
|
||||
@ -111,12 +132,12 @@ void ProcessList_printHeader(ProcessList* this, RichString* header) {
|
||||
color = CRT_colors[PANEL_HEADER_FOCUS];
|
||||
}
|
||||
|
||||
RichString_appendWide(header, color, alignedProcessFieldTitle(fields[i]));
|
||||
RichString_appendWide(header, color, alignedProcessFieldTitle(this, fields[i]));
|
||||
if (key == fields[i] && RichString_getCharVal(*header, RichString_size(header) - 1) == ' ') {
|
||||
header->chlen--; // rewind to override space
|
||||
RichString_rewind(header, 1); // rewind to override space
|
||||
RichString_appendnWide(header,
|
||||
CRT_colors[PANEL_SELECTION_FOCUS],
|
||||
CRT_treeStr[Settings_getActiveDirection(this->settings) == 1 ? TREE_STR_DESC : TREE_STR_ASC],
|
||||
CRT_treeStr[Settings_getActiveDirection(this->settings) == 1 ? TREE_STR_ASC : TREE_STR_DESC],
|
||||
1);
|
||||
}
|
||||
if (COMM == fields[i] && settings->showMergedCommand) {
|
||||
@ -131,7 +152,7 @@ void ProcessList_add(ProcessList* this, Process* p) {
|
||||
p->processList = this;
|
||||
|
||||
// highlighting processes found in first scan by first scan marked "far in the past"
|
||||
p->seenTs = this->scanTs;
|
||||
p->seenStampMs = this->monotonicMs;
|
||||
|
||||
Vector_add(this->processes, p);
|
||||
Hashtable_put(this->processTable, p->pid, p);
|
||||
@ -141,11 +162,11 @@ void ProcessList_add(ProcessList* this, Process* p) {
|
||||
assert(Hashtable_count(this->processTable) == Vector_count(this->processes));
|
||||
}
|
||||
|
||||
void ProcessList_remove(ProcessList* this, Process* p) {
|
||||
void ProcessList_remove(ProcessList* this, const Process* p) {
|
||||
assert(Vector_indexOf(this->processes, p, Process_pidCompare) != -1);
|
||||
assert(Hashtable_get(this->processTable, p->pid) != NULL);
|
||||
|
||||
Process* pp = Hashtable_remove(this->processTable, p->pid);
|
||||
const Process* pp = Hashtable_remove(this->processTable, p->pid);
|
||||
assert(pp == p); (void)pp;
|
||||
|
||||
pid_t pid = p->pid;
|
||||
@ -165,14 +186,6 @@ void ProcessList_remove(ProcessList* this, Process* p) {
|
||||
assert(Hashtable_count(this->processTable) == Vector_count(this->processes));
|
||||
}
|
||||
|
||||
Process* ProcessList_get(ProcessList* this, int idx) {
|
||||
return (Process*)Vector_get(this->processes, idx);
|
||||
}
|
||||
|
||||
int ProcessList_size(ProcessList* this) {
|
||||
return Vector_size(this->processes);
|
||||
}
|
||||
|
||||
// ProcessList_updateTreeSetLayer sorts this->displayTreeSet,
|
||||
// relying only on itself.
|
||||
//
|
||||
@ -202,7 +215,7 @@ static void ProcessList_updateTreeSetLayer(ProcessList* this, unsigned int leftB
|
||||
if (layerSize == 0)
|
||||
return;
|
||||
|
||||
Vector* layer = Vector_new(this->processes->type, false, layerSize);
|
||||
Vector* layer = Vector_new(Vector_type(this->processes), false, layerSize);
|
||||
|
||||
// Find all processes on the same layer (process with the same `deep` value
|
||||
// and included in a range from `leftBound` to `rightBound`).
|
||||
@ -311,6 +324,11 @@ static void ProcessList_updateTreeSet(ProcessList* this) {
|
||||
}
|
||||
|
||||
static void ProcessList_buildTreeBranch(ProcessList* this, pid_t pid, int level, int indent, int direction, bool show, int* node_counter, int* node_index) {
|
||||
// On OpenBSD the kernel thread 'swapper' has pid 0.
|
||||
// Do not treat it as root of any tree.
|
||||
if (pid == 0)
|
||||
return;
|
||||
|
||||
Vector* children = Vector_new(Class(Process), false, DEFAULT_SIZE);
|
||||
|
||||
for (int i = Vector_size(this->processes) - 1; i >= 0; i--) {
|
||||
@ -361,15 +379,15 @@ static void ProcessList_buildTreeBranch(ProcessList* this, pid_t pid, int level,
|
||||
}
|
||||
|
||||
static int ProcessList_treeProcessCompare(const void* v1, const void* v2) {
|
||||
const Process *p1 = (const Process*)v1;
|
||||
const Process *p2 = (const Process*)v2;
|
||||
const Process* p1 = (const Process*)v1;
|
||||
const Process* p2 = (const Process*)v2;
|
||||
|
||||
return SPACESHIP_NUMBER(p1->tree_left, p2->tree_left);
|
||||
}
|
||||
|
||||
static int ProcessList_treeProcessCompareByPID(const void* v1, const void* v2) {
|
||||
const Process *p1 = (const Process*)v1;
|
||||
const Process *p2 = (const Process*)v2;
|
||||
const Process* p1 = (const Process*)v1;
|
||||
const Process* p2 = (const Process*)v2;
|
||||
|
||||
return SPACESHIP_NUMBER(p1->pid, p2->pid);
|
||||
}
|
||||
@ -477,7 +495,7 @@ ProcessField ProcessList_keyAt(const ProcessList* this, int at) {
|
||||
const ProcessField* fields = this->settings->fields;
|
||||
ProcessField field;
|
||||
for (int i = 0; (field = fields[i]); i++) {
|
||||
int len = strlen(alignedProcessFieldTitle(field));
|
||||
int len = strlen(alignedProcessFieldTitle(this, field));
|
||||
if (at >= x && at <= x + len) {
|
||||
return field;
|
||||
}
|
||||
@ -494,17 +512,40 @@ void ProcessList_expandTree(ProcessList* this) {
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessList_collapseAllBranches(ProcessList* this) {
|
||||
int size = Vector_size(this->processes);
|
||||
for (int i = 0; i < size; i++) {
|
||||
Process* process = (Process*) Vector_get(this->processes, i);
|
||||
// FreeBSD has pid 0 = kernel and pid 1 = init, so init has tree_depth = 1
|
||||
if (process->tree_depth > 0 && process->pid > 1)
|
||||
process->showChildren = false;
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessList_rebuildPanel(ProcessList* this) {
|
||||
const char* incFilter = this->incFilter;
|
||||
|
||||
int currPos = Panel_getSelectedIndex(this->panel);
|
||||
int currScrollV = this->panel->scrollV;
|
||||
const int currPos = Panel_getSelectedIndex(this->panel);
|
||||
const int currScrollV = this->panel->scrollV;
|
||||
const int currSize = Panel_size(this->panel);
|
||||
|
||||
Panel_prune(this->panel);
|
||||
int size = ProcessList_size(this);
|
||||
|
||||
/* Follow main process if followed a userland thread and threads are now hidden */
|
||||
const Settings* settings = this->settings;
|
||||
if (this->following != -1 && settings->hideUserlandThreads) {
|
||||
const Process* followedProcess = (const Process*) Hashtable_get(this->processTable, this->following);
|
||||
if (followedProcess && Process_isThread(followedProcess) && Hashtable_get(this->processTable, followedProcess->tgid) != NULL) {
|
||||
this->following = followedProcess->tgid;
|
||||
}
|
||||
}
|
||||
|
||||
const int processCount = Vector_size(this->processes);
|
||||
int idx = 0;
|
||||
for (int i = 0; i < size; i++) {
|
||||
Process* p = ProcessList_get(this, i);
|
||||
bool foundFollowed = false;
|
||||
|
||||
for (int i = 0; i < processCount; i++) {
|
||||
Process* p = (Process*) Vector_get(this->processes, i);
|
||||
|
||||
if ( (!p->show)
|
||||
|| (this->userId != (uid_t) -1 && (p->st_uid != this->userId))
|
||||
@ -513,31 +554,47 @@ void ProcessList_rebuildPanel(ProcessList* this) {
|
||||
continue;
|
||||
|
||||
Panel_set(this->panel, idx, (Object*)p);
|
||||
if ((this->following == -1 && idx == currPos) || (this->following != -1 && p->pid == this->following)) {
|
||||
|
||||
if (this->following != -1 && p->pid == this->following) {
|
||||
foundFollowed = true;
|
||||
Panel_setSelected(this->panel, idx);
|
||||
this->panel->scrollV = currScrollV;
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
|
||||
if (this->following != -1 && !foundFollowed) {
|
||||
/* Reset if current followed pid not found */
|
||||
this->following = -1;
|
||||
Panel_setSelectionColor(this->panel, PANEL_SELECTION_FOCUS);
|
||||
}
|
||||
|
||||
if (this->following == -1) {
|
||||
/* If the last item was selected, keep the new last item selected */
|
||||
if (currPos > 0 && currPos == currSize - 1)
|
||||
Panel_setSelected(this->panel, Panel_size(this->panel) - 1);
|
||||
else
|
||||
Panel_setSelected(this->panel, currPos);
|
||||
|
||||
this->panel->scrollV = currScrollV;
|
||||
}
|
||||
}
|
||||
|
||||
Process* ProcessList_getProcess(ProcessList* this, pid_t pid, bool* preExisting, Process_New constructor) {
|
||||
Process* proc = (Process*) Hashtable_get(this->processTable, pid);
|
||||
*preExisting = proc;
|
||||
*preExisting = proc != NULL;
|
||||
if (proc) {
|
||||
assert(Vector_indexOf(this->processes, proc, Process_pidCompare) != -1);
|
||||
assert(proc->pid == pid);
|
||||
} else {
|
||||
proc = constructor(this->settings);
|
||||
assert(proc->comm == NULL);
|
||||
assert(proc->cmdline == NULL);
|
||||
proc->pid = pid;
|
||||
}
|
||||
return proc;
|
||||
}
|
||||
|
||||
void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate) {
|
||||
struct timespec now;
|
||||
|
||||
// in pause mode only gather global data for meters (CPU/memory/...)
|
||||
if (pauseProcessUpdate) {
|
||||
ProcessList_goThroughEntries(this, true);
|
||||
@ -558,37 +615,35 @@ void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate) {
|
||||
this->runningTasks = 0;
|
||||
|
||||
|
||||
// set scanTs
|
||||
// set scan timestamp
|
||||
static bool firstScanDone = false;
|
||||
if (!firstScanDone) {
|
||||
this->scanTs = 0;
|
||||
if (firstScanDone) {
|
||||
Platform_gettime_monotonic(&this->monotonicMs);
|
||||
} else {
|
||||
this->monotonicMs = 0;
|
||||
firstScanDone = true;
|
||||
} else if (Compat_clock_monotonic_gettime(&now) == 0) {
|
||||
// save time in millisecond, so with a delay in deciseconds
|
||||
// there are no irregularities
|
||||
this->scanTs = 1000 * now.tv_sec + now.tv_nsec / 1000000;
|
||||
}
|
||||
|
||||
ProcessList_goThroughEntries(this, false);
|
||||
|
||||
for (int i = Vector_size(this->processes) - 1; i >= 0; i--) {
|
||||
Process* p = (Process*) Vector_get(this->processes, i);
|
||||
if (p->tombTs > 0) {
|
||||
Process_makeCommandStr(p);
|
||||
|
||||
if (p->tombStampMs > 0) {
|
||||
// remove tombed process
|
||||
if (this->scanTs >= p->tombTs) {
|
||||
if (this->monotonicMs >= p->tombStampMs) {
|
||||
ProcessList_remove(this, p);
|
||||
}
|
||||
} else if (p->updated == false) {
|
||||
// process no longer exists
|
||||
if (this->settings->highlightChanges && p->wasShown) {
|
||||
// mark tombed
|
||||
p->tombTs = this->scanTs + 1000 * this->settings->highlightDelaySecs;
|
||||
p->tombStampMs = this->monotonicMs + 1000 * this->settings->highlightDelaySecs;
|
||||
} else {
|
||||
// immediately remove
|
||||
ProcessList_remove(this, p);
|
||||
}
|
||||
} else {
|
||||
p->updated = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,10 @@ in the source distribution for its full text.
|
||||
|
||||
#include "config.h" // IWYU pragma: keep
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "Hashtable.h"
|
||||
@ -34,6 +37,9 @@ in the source distribution for its full text.
|
||||
#define MAX_READ 2048
|
||||
#endif
|
||||
|
||||
typedef unsigned long long int memory_t;
|
||||
#define MEMORY_MAX ULLONG_MAX
|
||||
|
||||
typedef struct ProcessList_ {
|
||||
const Settings* settings;
|
||||
|
||||
@ -45,6 +51,13 @@ typedef struct ProcessList_ {
|
||||
Hashtable* displayTreeSet;
|
||||
Hashtable* draftingTreeSet;
|
||||
|
||||
Hashtable* dynamicMeters; /* runtime-discovered meters */
|
||||
Hashtable* dynamicColumns; /* runtime-discovered Columns */
|
||||
|
||||
struct timeval realtime; /* time of the current sample */
|
||||
uint64_t realtimeMs; /* current time in milliseconds */
|
||||
uint64_t monotonicMs; /* same, but from monotonic clock */
|
||||
|
||||
Panel* panel;
|
||||
int following;
|
||||
uid_t userId;
|
||||
@ -56,44 +69,44 @@ typedef struct ProcessList_ {
|
||||
bool topologyOk;
|
||||
#endif
|
||||
|
||||
int totalTasks;
|
||||
int runningTasks;
|
||||
int userlandThreads;
|
||||
int kernelThreads;
|
||||
unsigned int totalTasks;
|
||||
unsigned int runningTasks;
|
||||
unsigned int userlandThreads;
|
||||
unsigned int kernelThreads;
|
||||
|
||||
unsigned long long int totalMem;
|
||||
unsigned long long int usedMem;
|
||||
unsigned long long int buffersMem;
|
||||
unsigned long long int cachedMem;
|
||||
unsigned long long int totalSwap;
|
||||
unsigned long long int usedSwap;
|
||||
unsigned long long int freeSwap;
|
||||
memory_t totalMem;
|
||||
memory_t usedMem;
|
||||
memory_t buffersMem;
|
||||
memory_t cachedMem;
|
||||
memory_t sharedMem;
|
||||
memory_t availableMem;
|
||||
|
||||
int cpuCount;
|
||||
memory_t totalSwap;
|
||||
memory_t usedSwap;
|
||||
memory_t cachedSwap;
|
||||
|
||||
time_t scanTs;
|
||||
unsigned int activeCPUs;
|
||||
unsigned int existingCPUs;
|
||||
} ProcessList;
|
||||
|
||||
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId);
|
||||
/* Implemented by platforms */
|
||||
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* dynamicColumns, Hashtable* pidMatchList, uid_t userId);
|
||||
void ProcessList_delete(ProcessList* pl);
|
||||
void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate);
|
||||
bool ProcessList_isCPUonline(const ProcessList* super, unsigned int id);
|
||||
|
||||
|
||||
ProcessList* ProcessList_init(ProcessList* this, const ObjectClass* klass, UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId);
|
||||
ProcessList* ProcessList_init(ProcessList* this, const ObjectClass* klass, UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* dynamicColumns, Hashtable* pidMatchList, uid_t userId);
|
||||
|
||||
void ProcessList_done(ProcessList* this);
|
||||
|
||||
void ProcessList_setPanel(ProcessList* this, Panel* panel);
|
||||
|
||||
void ProcessList_printHeader(ProcessList* this, RichString* header);
|
||||
void ProcessList_printHeader(const 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);
|
||||
|
||||
int ProcessList_size(ProcessList* this);
|
||||
void ProcessList_remove(ProcessList* this, const Process* p);
|
||||
|
||||
void ProcessList_sort(ProcessList* this);
|
||||
|
||||
@ -101,10 +114,16 @@ ProcessField ProcessList_keyAt(const ProcessList* this, int at);
|
||||
|
||||
void ProcessList_expandTree(ProcessList* this);
|
||||
|
||||
void ProcessList_collapseAllBranches(ProcessList* this);
|
||||
|
||||
void ProcessList_rebuildPanel(ProcessList* this);
|
||||
|
||||
Process* ProcessList_getProcess(ProcessList* this, pid_t pid, bool* preExisting, Process_New constructor);
|
||||
|
||||
void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate);
|
||||
|
||||
static inline Process* ProcessList_findProcess(ProcessList* this, pid_t pid) {
|
||||
return (Process*) Hashtable_get(this->processTable, pid);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -12,7 +12,7 @@ in the source distribution for its full text.
|
||||
|
||||
// IWYU pragma: begin_exports
|
||||
|
||||
#ifdef HAVE_NCURSESW_CURSES_H
|
||||
#if defined(HAVE_NCURSESW_CURSES_H)
|
||||
#include <ncursesw/curses.h>
|
||||
#elif defined(HAVE_NCURSES_NCURSES_H)
|
||||
#include <ncurses/ncurses.h>
|
||||
|
115
README
115
README
@ -3,9 +3,8 @@
|
||||
[](https://github.com/htop-dev/htop/actions)
|
||||
[](https://scan.coverity.com/projects/21665)
|
||||
[](https://groups.io/g/htop)
|
||||
[](https://webchat.freenode.net/#htop)
|
||||
[](https://web.libera.chat/#htop)
|
||||
[](https://github.com/htop-dev/htop/releases/latest)
|
||||
[](https://bintray.com/htop/source/htop/_latestVersion)
|
||||
|
||||

|
||||
|
||||
@ -25,25 +24,123 @@ For more information and details on how to contribute to `htop` visit [htop.dev]
|
||||
|
||||
## Build instructions
|
||||
|
||||
This program is distributed as a standard GNU autotools-based package.
|
||||
### Prerequisite
|
||||
List of build-time dependencies:
|
||||
* `build-essential` standard GNU autotools-based
|
||||
* `autoconf`
|
||||
* `autotools`
|
||||
* `ncurses`
|
||||
|
||||
Compiling `htop` requires the header files for `ncurses` (libncursesw*-dev). Install these and other required packages for C development from your package manager.
|
||||
**Note about `ncurses`:**
|
||||
> htop requires ncurses 6.0. Be aware the appropriate package is sometimes still called libncurses5 (on Debian/Ubuntu). Also ncurses usually comes in two flavours:
|
||||
>* With Unicode support.
|
||||
>* Without Unicode support.
|
||||
>
|
||||
> This is also something that is reflected in the package name on Debian/Ubuntu (via the additional 'w' - 'w'ide character support).
|
||||
|
||||
Then, when compiling from a [release tarball](https://bintray.com/htop/source/htop), run:
|
||||
List of additional build-time dependencies (based on feature flags):
|
||||
* `sensors`
|
||||
* `hwloc`
|
||||
* `libcap`
|
||||
|
||||
Compiling `htop` requires the header files for `ncurses` . Install these and other required packages for C development from your package manager.
|
||||
|
||||
**Debian/Ubuntu**
|
||||
~~~ shell
|
||||
./configure && make
|
||||
sudo apt install libncursesw5-dev autotools-dev autoconf
|
||||
~~~
|
||||
|
||||
Alternatively, for compiling sources downloaded from the Git repository (`git clone` or downloads from [Github releases](https://github.com/htop-dev/htop/releases/)),
|
||||
install the header files for `ncurses` (libncursesw*-dev) and other required development packages from your distribution's package manager. Then run:
|
||||
**Fedora/RHEL**
|
||||
~~~ shell
|
||||
sudo dnf install ncurses-devel automake autoconf
|
||||
~~~
|
||||
|
||||
### Compiling from source:
|
||||
To compile from sources downloaded from the Git repository (`git clone` or downloads from [Github releases](https://github.com/htop-dev/htop/releases/)), then run:
|
||||
~~~ shell
|
||||
./autogen.sh && ./configure && make
|
||||
~~~
|
||||
|
||||
By default `make install` will install into `/usr/local`, for changing the path use `./configure --prefix=/some/path`.
|
||||
|
||||
### Install
|
||||
To install on the local system run `make install`. By default `make install` installs into `/usr/local`. To change this path use `./configure --prefix=/some/path`.
|
||||
|
||||
### Build Options
|
||||
|
||||
`htop` has several build-time options to enable/disable additional features.
|
||||
|
||||
#### Generic
|
||||
|
||||
* `--enable-unicode`:
|
||||
enable Unicode support
|
||||
dependency: *libncursesw*
|
||||
default: *yes*
|
||||
* `--enable-pcp`:
|
||||
enable Performance Co-Pilot support via a new pcp-htop utility
|
||||
dependency: *libpcp*
|
||||
default: *no*
|
||||
* `--enable-affinity`:
|
||||
enable `sched_setaffinity(2)` and `sched_getaffinity(2)` for affinity support; conflicts with hwloc
|
||||
default: *check*
|
||||
* `--enable-hwloc`:
|
||||
enable hwloc support for CPU affinity; disables affinity support
|
||||
dependency: *libhwloc*
|
||||
default: *no*
|
||||
* `--enable-static`:
|
||||
build a static htop binary; hwloc and delay accounting are not supported
|
||||
default: *no*
|
||||
* `--enable-debug`:
|
||||
Enable asserts and internal sanity checks; implies a performance penalty
|
||||
default: *no*
|
||||
|
||||
#### Linux
|
||||
|
||||
* `--enable-sensors`:
|
||||
enable libsensors(3) support for reading temperature data
|
||||
dependencies: *libsensors-dev*(build-time), at runtime *libsensors* is loaded via `dlopen(3)` if available
|
||||
default: *check*
|
||||
* `--enable-capabilities`:
|
||||
enable Linux capabilities support
|
||||
dependency: *libcap*
|
||||
default: *check*
|
||||
* `--with-proc`:
|
||||
location of a Linux-compatible proc filesystem
|
||||
default: */proc*
|
||||
* `--enable-openvz`:
|
||||
enable OpenVZ support
|
||||
default: *no*
|
||||
* `--enable-vserver`:
|
||||
enable VServer support
|
||||
default: *no*
|
||||
* `--enable-ancient-vserver`:
|
||||
enable ancient VServer support (implies `--enable-vserver`)
|
||||
default: *no*
|
||||
* `--enable-delayacct`:
|
||||
enable Linux delay accounting support
|
||||
dependencies: *pkg-config*(build-time), *libnl-3* and *libnl-genl-3*
|
||||
default: *check*
|
||||
|
||||
|
||||
## Runtime dependencies:
|
||||
`htop` has a set of fixed minimum runtime dependencies, which is kept as minimal as possible:
|
||||
* `ncurses` libraries for terminal handling (wide character support).
|
||||
|
||||
### Runtime optional dependencies:
|
||||
`htop` has a set of fixed optional dependencies, depending on build/configure option used:
|
||||
* `libdl`, if not building static and support for some of the optional libraries is enabled, is always required when support for to optionally load dependencies (i.e. `libsensors`, `systemd`) is present.
|
||||
* `libcap`, user-space interfaces to the POSIX 1003.1e, is always required when `--enable-capabilities` was used to configure `htop`.
|
||||
* `libsensors`, readout of temperatures and CPU speeds, is optional even when `--enable-sensors` was used to configure `htop`.
|
||||
* `systemd` is optional when `--enable-static` was not used to configure `htop` (Linux only). If building statically and `libsystemd` is not found by `configure` support for the SystemD meter is disabled entirely.
|
||||
|
||||
`htop` checks for the availability of the actual runtime lib as `htop` runs.
|
||||
|
||||
**BSD**
|
||||
On most *BSD systems you also have `kvm` as a static requirement to read all the kernel information.
|
||||
|
||||
More information on required and optional dependencies can be found in [configure.ac](configure.ac).
|
||||
|
||||
## Usage
|
||||
See the manual page (`man htop`) or the on-line help ('F1' or 'h' inside `htop`) for a list of supported key commands.
|
||||
|
||||
## Support
|
||||
@ -54,7 +151,7 @@ If you have trouble running `htop` please consult your Operating System / Linux
|
||||
|
||||
We have a [development mailing list](https://htop.dev/mailinglist.html). Feel free to subscribe for release announcements or asking questions on the development of htop.
|
||||
|
||||
You can also join our IRC channel #htop on freenode and talk to the developers there.
|
||||
You can also join our IRC channel #htop on Libera.Chat and talk to the developers there.
|
||||
|
||||
If you have found an issue with the source of htop, please check whether this has already been reported in our [Github issue tracker](https://github.com/htop-dev/htop/issues).
|
||||
If not, please file a new issue describing the problem you have found, the location in the source code you are referring to and a possible fix.
|
||||
|
85
RichString.c
85
RichString.c
@ -46,6 +46,10 @@ static void RichString_setLen(RichString* this, int len) {
|
||||
}
|
||||
}
|
||||
|
||||
void RichString_rewind(RichString* this, int count) {
|
||||
RichString_setLen(this, this->chlen - count);
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBNCURSESW
|
||||
|
||||
static inline int RichString_writeFromWide(RichString* this, int attrs, const char* data_c, int from, int len) {
|
||||
@ -60,7 +64,37 @@ static inline int RichString_writeFromWide(RichString* this, int attrs, const ch
|
||||
this->chptr[i] = (CharType) { .attr = attrs & 0xffffff, .chars = { (iswprint(data[j]) ? data[j] : '?') } };
|
||||
}
|
||||
|
||||
return wcswidth(data, len);
|
||||
return len;
|
||||
}
|
||||
|
||||
int RichString_appendnWideColumns(RichString* this, int attrs, const char* data_c, int len, int* columns) {
|
||||
wchar_t data[len + 1];
|
||||
len = mbstowcs(data, data_c, len);
|
||||
if (len <= 0)
|
||||
return 0;
|
||||
|
||||
int from = this->chlen;
|
||||
int newLen = from + len;
|
||||
RichString_setLen(this, newLen);
|
||||
int columnsWritten = 0;
|
||||
int pos = from;
|
||||
for (int j = 0; j < len; j++) {
|
||||
wchar_t c = iswprint(data[j]) ? data[j] : '?';
|
||||
int cwidth = wcwidth(c);
|
||||
if (cwidth > *columns)
|
||||
break;
|
||||
|
||||
*columns -= cwidth;
|
||||
columnsWritten += cwidth;
|
||||
|
||||
this->chptr[pos] = (CharType) { .attr = attrs & 0xffffff, .chars = { c, '\0' } };
|
||||
pos++;
|
||||
}
|
||||
|
||||
RichString_setLen(this, pos);
|
||||
*columns = columnsWritten;
|
||||
|
||||
return pos - from;
|
||||
}
|
||||
|
||||
static inline int RichString_writeFromAscii(RichString* this, int attrs, const char* data, int from, int len) {
|
||||
@ -80,9 +114,18 @@ inline void RichString_setAttrn(RichString* this, int attrs, int start, int char
|
||||
}
|
||||
}
|
||||
|
||||
int RichString_findChar(RichString* this, char c, int start) {
|
||||
wchar_t wc = btowc(c);
|
||||
cchar_t* ch = this->chptr + start;
|
||||
void RichString_appendChr(RichString* this, int attrs, char c, int count) {
|
||||
int from = this->chlen;
|
||||
int newLen = from + count;
|
||||
RichString_setLen(this, newLen);
|
||||
for (int i = from; i < newLen; i++) {
|
||||
this->chptr[i] = (CharType) { .attr = attrs, .chars = { c, 0 } };
|
||||
}
|
||||
}
|
||||
|
||||
int RichString_findChar(const RichString* this, char c, int start) {
|
||||
const wchar_t wc = btowc(c);
|
||||
const cchar_t* ch = this->chptr + start;
|
||||
for (int i = start; i < this->chlen; i++) {
|
||||
if (ch->chars[0] == wc)
|
||||
return i;
|
||||
@ -104,6 +147,12 @@ static inline int RichString_writeFromWide(RichString* this, int attrs, const ch
|
||||
return len;
|
||||
}
|
||||
|
||||
int RichString_appendnWideColumns(RichString* this, int attrs, const char* data_c, int len, int* columns) {
|
||||
int written = RichString_writeFromWide(this, attrs, data_c, this->chlen, MINIMUM(len, *columns));
|
||||
*columns = written;
|
||||
return written;
|
||||
}
|
||||
|
||||
static inline int RichString_writeFromAscii(RichString* this, int attrs, const char* data_c, int from, int len) {
|
||||
return RichString_writeFromWide(this, attrs, data_c, from, len);
|
||||
}
|
||||
@ -115,8 +164,17 @@ void RichString_setAttrn(RichString* this, int attrs, int start, int charcount)
|
||||
}
|
||||
}
|
||||
|
||||
int RichString_findChar(RichString* this, char c, int start) {
|
||||
chtype* ch = this->chptr + start;
|
||||
void RichString_appendChr(RichString* this, int attrs, char c, int count) {
|
||||
int from = this->chlen;
|
||||
int newLen = from + count;
|
||||
RichString_setLen(this, newLen);
|
||||
for (int i = from; i < newLen; i++) {
|
||||
this->chptr[i] = c | attrs;
|
||||
}
|
||||
}
|
||||
|
||||
int RichString_findChar(const RichString* this, char c, int start) {
|
||||
const chtype* ch = this->chptr + start;
|
||||
for (int i = start; i < this->chlen; i++) {
|
||||
if ((*ch & 0xff) == (chtype) c)
|
||||
return i;
|
||||
@ -127,19 +185,10 @@ int RichString_findChar(RichString* this, char c, int start) {
|
||||
|
||||
#endif /* HAVE_LIBNCURSESW */
|
||||
|
||||
void RichString_prune(RichString* this) {
|
||||
if (this->chlen > RICHSTRING_MAXLEN)
|
||||
void RichString_delete(RichString* this) {
|
||||
if (this->chlen > RICHSTRING_MAXLEN) {
|
||||
free(this->chptr);
|
||||
memset(this, 0, sizeof(RichString));
|
||||
this->chptr = this->chstr;
|
||||
}
|
||||
|
||||
void RichString_appendChr(RichString* this, char c, int count) {
|
||||
int from = this->chlen;
|
||||
int newLen = from + count;
|
||||
RichString_setLen(this, newLen);
|
||||
for (int i = from; i < newLen; i++) {
|
||||
RichString_setChar(this, i, c);
|
||||
this->chptr = this->chstr;
|
||||
}
|
||||
}
|
||||
|
||||
|
30
RichString.h
30
RichString.h
@ -15,20 +15,25 @@ in the source distribution for its full text.
|
||||
#define RichString_size(this) ((this)->chlen)
|
||||
#define RichString_sizeVal(this) ((this).chlen)
|
||||
|
||||
#define RichString_begin(this) RichString (this); RichString_beginAllocated(this)
|
||||
#define RichString_beginAllocated(this) do { memset(&(this), 0, sizeof(RichString)); (this).chptr = (this).chstr; } while(0)
|
||||
#define RichString_end(this) RichString_prune(&(this))
|
||||
#define RichString_begin(this) RichString this; RichString_beginAllocated(this)
|
||||
#define RichString_beginAllocated(this) \
|
||||
do { \
|
||||
(this).chlen = 0, \
|
||||
(this).chptr = (this).chstr; \
|
||||
RichString_setChar(&(this), 0, 0); \
|
||||
(this).highlightAttr = 0; \
|
||||
} while(0)
|
||||
|
||||
#ifdef HAVE_LIBNCURSESW
|
||||
#define RichString_printVal(this, y, x) mvadd_wchstr(y, x, (this).chptr)
|
||||
#define RichString_printoffnVal(this, y, x, off, n) mvadd_wchnstr(y, x, (this).chptr + (off), n)
|
||||
#define RichString_getCharVal(this, i) ((this).chptr[i].chars[0] & 255)
|
||||
#define RichString_getCharVal(this, i) ((this).chptr[i].chars[0])
|
||||
#define RichString_setChar(this, at, ch) do { (this)->chptr[(at)] = (CharType) { .chars = { ch, 0 } }; } while (0)
|
||||
#define CharType cchar_t
|
||||
#else
|
||||
#define RichString_printVal(this, y, x) mvaddchstr(y, x, (this).chptr)
|
||||
#define RichString_printoffnVal(this, y, x, off, n) mvaddchnstr(y, x, (this).chptr + (off), n)
|
||||
#define RichString_getCharVal(this, i) ((this).chptr[i])
|
||||
#define RichString_getCharVal(this, i) ((this).chptr[i] & 0xff)
|
||||
#define RichString_setChar(this, at, ch) do { (this)->chptr[(at)] = ch; } while (0)
|
||||
#define CharType chtype
|
||||
#endif
|
||||
@ -42,20 +47,27 @@ typedef struct RichString_ {
|
||||
int highlightAttr;
|
||||
} RichString;
|
||||
|
||||
void RichString_delete(RichString* this);
|
||||
|
||||
void RichString_rewind(RichString* this, int count);
|
||||
|
||||
void RichString_setAttrn(RichString* this, int attrs, int start, int charcount);
|
||||
|
||||
int RichString_findChar(RichString* this, char c, int start);
|
||||
|
||||
void RichString_prune(RichString* this);
|
||||
int RichString_findChar(const RichString* this, char c, int start);
|
||||
|
||||
void RichString_setAttr(RichString* this, int attrs);
|
||||
|
||||
void RichString_appendChr(RichString* this, char c, int count);
|
||||
void RichString_appendChr(RichString* this, int attrs, char c, int count);
|
||||
|
||||
/* All appending and writing functions return the number of written characters (not columns). */
|
||||
|
||||
int RichString_appendWide(RichString* this, int attrs, const char* data);
|
||||
|
||||
int RichString_appendnWide(RichString* this, int attrs, const char* data, int len);
|
||||
|
||||
/* columns takes the maximum number of columns to write and contains on return the number of columns written. */
|
||||
int RichString_appendnWideColumns(RichString* this, int attrs, const char* data, int len, int* columns);
|
||||
|
||||
int RichString_writeWide(RichString* this, int attrs, const char* data);
|
||||
|
||||
int RichString_appendAscii(RichString* this, int attrs, const char* data);
|
||||
|
@ -5,6 +5,8 @@ Released under the GNU GPLv2, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
#include "config.h" // IWYU pragma: keep
|
||||
|
||||
#include "ScreenManager.h"
|
||||
|
||||
#include <assert.h>
|
||||
@ -15,6 +17,7 @@ in the source distribution for its full text.
|
||||
#include "CRT.h"
|
||||
#include "FunctionBar.h"
|
||||
#include "Object.h"
|
||||
#include "Platform.h"
|
||||
#include "ProcessList.h"
|
||||
#include "ProvideCurses.h"
|
||||
#include "XUtils.h"
|
||||
@ -24,7 +27,7 @@ ScreenManager* ScreenManager_new(Header* header, const Settings* settings, const
|
||||
ScreenManager* this;
|
||||
this = xMalloc(sizeof(ScreenManager));
|
||||
this->x1 = 0;
|
||||
this->y1 = header->height;
|
||||
this->y1 = 0;
|
||||
this->x2 = 0;
|
||||
this->y2 = -1;
|
||||
this->panels = Vector_new(Class(Panel), owner, DEFAULT_SIZE);
|
||||
@ -32,7 +35,6 @@ ScreenManager* ScreenManager_new(Header* header, const Settings* settings, const
|
||||
this->header = header;
|
||||
this->settings = settings;
|
||||
this->state = state;
|
||||
this->owner = owner;
|
||||
this->allowFocusChange = true;
|
||||
return this;
|
||||
}
|
||||
@ -42,23 +44,23 @@ void ScreenManager_delete(ScreenManager* this) {
|
||||
free(this);
|
||||
}
|
||||
|
||||
inline int ScreenManager_size(ScreenManager* this) {
|
||||
inline int ScreenManager_size(const ScreenManager* this) {
|
||||
return this->panelCount;
|
||||
}
|
||||
|
||||
void ScreenManager_add(ScreenManager* this, Panel* item, int size) {
|
||||
int lastX = 0;
|
||||
if (this->panelCount > 0) {
|
||||
Panel* last = (Panel*) Vector_get(this->panels, this->panelCount - 1);
|
||||
const Panel* last = (const Panel*) Vector_get(this->panels, this->panelCount - 1);
|
||||
lastX = last->x + last->w + 1;
|
||||
}
|
||||
int height = LINES - this->y1 + this->y2;
|
||||
int height = LINES - this->y1 - (this->header ? this->header->height : 0) + this->y2;
|
||||
if (size > 0) {
|
||||
Panel_resize(item, size, height);
|
||||
} else {
|
||||
Panel_resize(item, COLS - this->x1 + this->x2 - lastX, height);
|
||||
}
|
||||
Panel_move(item, lastX, this->y1);
|
||||
Panel_move(item, lastX, this->y1 + (this->header ? this->header->height : 0));
|
||||
Vector_add(this->panels, item);
|
||||
item->needsRedraw = true;
|
||||
this->panelCount++;
|
||||
@ -71,30 +73,26 @@ Panel* ScreenManager_remove(ScreenManager* this, int idx) {
|
||||
return panel;
|
||||
}
|
||||
|
||||
void ScreenManager_resize(ScreenManager* this, int x1, int y1, int x2, int y2) {
|
||||
this->x1 = x1;
|
||||
this->y1 = y1;
|
||||
this->x2 = x2;
|
||||
this->y2 = y2;
|
||||
void ScreenManager_resize(ScreenManager* this) {
|
||||
int y1_header = this->y1 + (this->header ? this->header->height : 0);
|
||||
int panels = this->panelCount;
|
||||
int lastX = 0;
|
||||
for (int i = 0; i < panels - 1; i++) {
|
||||
Panel* panel = (Panel*) Vector_get(this->panels, i);
|
||||
Panel_resize(panel, panel->w, LINES - y1 + y2);
|
||||
Panel_move(panel, lastX, y1);
|
||||
Panel_resize(panel, panel->w, LINES - y1_header + this->y2);
|
||||
Panel_move(panel, lastX, y1_header);
|
||||
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_resize(panel, COLS - this->x1 + this->x2 - lastX, LINES - y1_header + this->y2);
|
||||
Panel_move(panel, lastX, y1_header);
|
||||
}
|
||||
|
||||
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);
|
||||
Platform_gettime_realtime(&pl->realtime, &pl->realtimeMs);
|
||||
double newTime = ((double)pl->realtime.tv_sec * 10) + ((double)pl->realtime.tv_usec / 100000);
|
||||
|
||||
*timedOut = (newTime - *oldTime > this->settings->delay);
|
||||
*rescan |= *timedOut;
|
||||
@ -105,7 +103,10 @@ static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTi
|
||||
|
||||
if (*rescan) {
|
||||
*oldTime = newTime;
|
||||
// scan processes first - some header values are calculated there
|
||||
ProcessList_scan(pl, this->state->pauseProcessUpdate);
|
||||
// always update header, especially to avoid gaps in graph meters
|
||||
Header_updateData(this->header);
|
||||
if (!this->state->pauseProcessUpdate && (*sortTimeout == 0 || this->settings->treeView)) {
|
||||
ProcessList_sort(pl);
|
||||
*sortTimeout = 1;
|
||||
@ -123,7 +124,11 @@ static void ScreenManager_drawPanels(ScreenManager* this, int focus, bool force_
|
||||
const int nPanels = this->panelCount;
|
||||
for (int i = 0; i < nPanels; i++) {
|
||||
Panel* panel = (Panel*) Vector_get(this->panels, i);
|
||||
Panel_draw(panel, force_redraw, i == focus, !((panel == this->state->panel) && this->state->hideProcessSelection), State_hideFunctionBar(this->state));
|
||||
Panel_draw(panel,
|
||||
force_redraw,
|
||||
i == focus,
|
||||
panel != (Panel*)this->state->mainPanel || !this->state->hideProcessSelection,
|
||||
State_hideFunctionBar(this->state));
|
||||
mvvline(panel->y, panel->x + panel->w, ' ', panel->h + (State_hideFunctionBar(this->state) ? 1 : 0));
|
||||
}
|
||||
}
|
||||
@ -157,10 +162,13 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
|
||||
}
|
||||
|
||||
int prevCh = ch;
|
||||
#ifdef HAVE_SET_ESCDELAY
|
||||
set_escdelay(25);
|
||||
#endif
|
||||
ch = getch();
|
||||
|
||||
HandlerResult result = IGNORED;
|
||||
#ifdef HAVE_GETMOUSE
|
||||
if (ch == KEY_MOUSE && this->settings->enableMouse) {
|
||||
ch = ERR;
|
||||
MEVENT mevent;
|
||||
@ -181,7 +189,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
|
||||
if (panel == panelFocus || this->allowFocusChange) {
|
||||
focus = i;
|
||||
panelFocus = panel;
|
||||
Object* oldSelection = Panel_getSelected(panel);
|
||||
const Object* oldSelection = Panel_getSelected(panel);
|
||||
Panel_setSelected(panel, mevent.y - panel->y + panel->scrollV - 1);
|
||||
if (Panel_getSelected(panel) == oldSelection) {
|
||||
ch = KEY_RECLICK;
|
||||
@ -201,8 +209,10 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (ch == ERR) {
|
||||
sortTimeout--;
|
||||
if (sortTimeout > 0)
|
||||
sortTimeout--;
|
||||
if (prevCh == ch && !timedOut) {
|
||||
closeTimeout++;
|
||||
if (closeTimeout == 100) {
|
||||
@ -233,6 +243,10 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
|
||||
if (result & REDRAW) {
|
||||
force_redraw = true;
|
||||
}
|
||||
if (result & RESIZE) {
|
||||
ScreenManager_resize(this);
|
||||
force_redraw = true;
|
||||
}
|
||||
if (result & RESCAN) {
|
||||
rescan = true;
|
||||
sortTimeout = 0;
|
||||
@ -247,7 +261,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
|
||||
switch (ch) {
|
||||
case KEY_RESIZE:
|
||||
{
|
||||
ScreenManager_resize(this, this->x1, this->y1, this->x2, this->y2);
|
||||
ScreenManager_resize(this);
|
||||
continue;
|
||||
}
|
||||
case KEY_LEFT:
|
||||
|
@ -26,7 +26,6 @@ typedef struct ScreenManager_ {
|
||||
Header* header;
|
||||
const Settings* settings;
|
||||
const State* state;
|
||||
bool owner;
|
||||
bool allowFocusChange;
|
||||
} ScreenManager;
|
||||
|
||||
@ -34,13 +33,13 @@ ScreenManager* ScreenManager_new(Header* header, const Settings* settings, const
|
||||
|
||||
void ScreenManager_delete(ScreenManager* this);
|
||||
|
||||
int ScreenManager_size(ScreenManager* this);
|
||||
int ScreenManager_size(const ScreenManager* this);
|
||||
|
||||
void ScreenManager_add(ScreenManager* this, Panel* item, int size);
|
||||
|
||||
Panel* ScreenManager_remove(ScreenManager* this, int idx);
|
||||
|
||||
void ScreenManager_resize(ScreenManager* this, int x1, int y1, int x2, int y2);
|
||||
void ScreenManager_resize(ScreenManager* this);
|
||||
|
||||
void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey);
|
||||
|
||||
|
423
Settings.c
423
Settings.c
@ -7,12 +7,17 @@ in the source distribution for its full text.
|
||||
|
||||
#include "Settings.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "CRT.h"
|
||||
#include "DynamicColumn.h"
|
||||
#include "Macros.h"
|
||||
#include "Meter.h"
|
||||
#include "Platform.h"
|
||||
@ -22,21 +27,27 @@ in the source distribution for its full text.
|
||||
void Settings_delete(Settings* this) {
|
||||
free(this->filename);
|
||||
free(this->fields);
|
||||
for (unsigned int i = 0; i < ARRAYSIZE(this->columns); i++) {
|
||||
String_freeArray(this->columns[i].names);
|
||||
free(this->columns[i].modes);
|
||||
for (unsigned int i = 0; i < HeaderLayout_getColumns(this->hLayout); i++) {
|
||||
if (this->hColumns[i].names) {
|
||||
for (uint8_t j = 0; j < this->hColumns[i].len; j++)
|
||||
free(this->hColumns[i].names[j]);
|
||||
free(this->hColumns[i].names);
|
||||
}
|
||||
free(this->hColumns[i].modes);
|
||||
}
|
||||
free(this->hColumns);
|
||||
free(this);
|
||||
}
|
||||
|
||||
static void Settings_readMeters(Settings* this, char* line, int column) {
|
||||
static void Settings_readMeters(Settings* this, const char* line, unsigned int column) {
|
||||
char* trim = String_trim(line);
|
||||
char** ids = String_split(trim, ' ', NULL);
|
||||
free(trim);
|
||||
this->columns[column].names = ids;
|
||||
column = MINIMUM(column, HeaderLayout_getColumns(this->hLayout) - 1);
|
||||
this->hColumns[column].names = ids;
|
||||
}
|
||||
|
||||
static void Settings_readMeterModes(Settings* this, char* line, int column) {
|
||||
static void Settings_readMeterModes(Settings* this, const char* line, unsigned int column) {
|
||||
char* trim = String_trim(line);
|
||||
char** ids = String_split(trim, ' ', NULL);
|
||||
free(trim);
|
||||
@ -44,86 +55,120 @@ static void Settings_readMeterModes(Settings* this, char* line, int column) {
|
||||
for (int i = 0; ids[i]; i++) {
|
||||
len++;
|
||||
}
|
||||
this->columns[column].len = len;
|
||||
int* modes = xCalloc(len, sizeof(int));
|
||||
column = MINIMUM(column, HeaderLayout_getColumns(this->hLayout) - 1);
|
||||
this->hColumns[column].len = len;
|
||||
int* modes = len ? xCalloc(len, sizeof(int)) : NULL;
|
||||
for (int i = 0; i < len; i++) {
|
||||
modes[i] = atoi(ids[i]);
|
||||
}
|
||||
String_freeArray(ids);
|
||||
this->columns[column].modes = modes;
|
||||
this->hColumns[column].modes = modes;
|
||||
}
|
||||
|
||||
static void Settings_defaultMeters(Settings* this, int initialCpuCount) {
|
||||
static void Settings_defaultMeters(Settings* this, unsigned int initialCpuCount) {
|
||||
int sizes[] = { 3, 3 };
|
||||
if (initialCpuCount > 4) {
|
||||
if (initialCpuCount > 4 && initialCpuCount <= 128) {
|
||||
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];
|
||||
this->hColumns[i].names = xCalloc(sizes[i] + 1, sizeof(char*));
|
||||
this->hColumns[i].modes = xCalloc(sizes[i], sizeof(int));
|
||||
this->hColumns[i].len = sizes[i];
|
||||
}
|
||||
int r = 0;
|
||||
if (initialCpuCount > 8) {
|
||||
this->columns[0].names[0] = xStrdup("LeftCPUs2");
|
||||
this->columns[0].modes[0] = BAR_METERMODE;
|
||||
this->columns[1].names[r] = xStrdup("RightCPUs2");
|
||||
this->columns[1].modes[r++] = BAR_METERMODE;
|
||||
|
||||
if (initialCpuCount > 128) {
|
||||
// Just show the average, ricers need to config for impressive screenshots
|
||||
this->hColumns[0].names[0] = xStrdup("CPU");
|
||||
this->hColumns[0].modes[0] = BAR_METERMODE;
|
||||
} else if (initialCpuCount > 32) {
|
||||
this->hColumns[0].names[0] = xStrdup("LeftCPUs8");
|
||||
this->hColumns[0].modes[0] = BAR_METERMODE;
|
||||
this->hColumns[1].names[r] = xStrdup("RightCPUs8");
|
||||
this->hColumns[1].modes[r++] = BAR_METERMODE;
|
||||
} else if (initialCpuCount > 16) {
|
||||
this->hColumns[0].names[0] = xStrdup("LeftCPUs4");
|
||||
this->hColumns[0].modes[0] = BAR_METERMODE;
|
||||
this->hColumns[1].names[r] = xStrdup("RightCPUs4");
|
||||
this->hColumns[1].modes[r++] = BAR_METERMODE;
|
||||
} else if (initialCpuCount > 8) {
|
||||
this->hColumns[0].names[0] = xStrdup("LeftCPUs2");
|
||||
this->hColumns[0].modes[0] = BAR_METERMODE;
|
||||
this->hColumns[1].names[r] = xStrdup("RightCPUs2");
|
||||
this->hColumns[1].modes[r++] = BAR_METERMODE;
|
||||
} else if (initialCpuCount > 4) {
|
||||
this->columns[0].names[0] = xStrdup("LeftCPUs");
|
||||
this->columns[0].modes[0] = BAR_METERMODE;
|
||||
this->columns[1].names[r] = xStrdup("RightCPUs");
|
||||
this->columns[1].modes[r++] = BAR_METERMODE;
|
||||
this->hColumns[0].names[0] = xStrdup("LeftCPUs");
|
||||
this->hColumns[0].modes[0] = BAR_METERMODE;
|
||||
this->hColumns[1].names[r] = xStrdup("RightCPUs");
|
||||
this->hColumns[1].modes[r++] = BAR_METERMODE;
|
||||
} else {
|
||||
this->columns[0].names[0] = xStrdup("AllCPUs");
|
||||
this->columns[0].modes[0] = BAR_METERMODE;
|
||||
this->hColumns[0].names[0] = xStrdup("AllCPUs");
|
||||
this->hColumns[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;
|
||||
this->hColumns[0].names[1] = xStrdup("Memory");
|
||||
this->hColumns[0].modes[1] = BAR_METERMODE;
|
||||
this->hColumns[0].names[2] = xStrdup("Swap");
|
||||
this->hColumns[0].modes[2] = BAR_METERMODE;
|
||||
this->hColumns[1].names[r] = xStrdup("Tasks");
|
||||
this->hColumns[1].modes[r++] = TEXT_METERMODE;
|
||||
this->hColumns[1].names[r] = xStrdup("LoadAverage");
|
||||
this->hColumns[1].modes[r++] = TEXT_METERMODE;
|
||||
this->hColumns[1].names[r] = xStrdup("Uptime");
|
||||
this->hColumns[1].modes[r++] = TEXT_METERMODE;
|
||||
}
|
||||
|
||||
static void readFields(ProcessField* fields, uint32_t* flags, const char* line) {
|
||||
static void Settings_readFields(Settings* settings, const char* line) {
|
||||
char* trim = String_trim(line);
|
||||
char** ids = String_split(trim, ' ', NULL);
|
||||
free(trim);
|
||||
int i, j;
|
||||
*flags = 0;
|
||||
for (j = 0, i = 0; i < LAST_PROCESSFIELD && ids[i]; i++) {
|
||||
|
||||
settings->flags = 0;
|
||||
|
||||
unsigned int i, j;
|
||||
for (j = 0, i = 0; ids[i]; i++) {
|
||||
if (j >= UINT_MAX / sizeof(ProcessField))
|
||||
continue;
|
||||
if (j >= LAST_PROCESSFIELD) {
|
||||
settings->fields = xRealloc(settings->fields, j * sizeof(ProcessField));
|
||||
memset(&settings->fields[j], 0, sizeof(ProcessField));
|
||||
}
|
||||
|
||||
// Dynamically-defined columns are always stored by-name.
|
||||
char* end, dynamic[32] = {0};
|
||||
if (sscanf(ids[i], "Dynamic(%30s)", dynamic)) {
|
||||
if ((end = strrchr(dynamic, ')')) == NULL)
|
||||
continue;
|
||||
*end = '\0';
|
||||
unsigned int key;
|
||||
if (!DynamicColumn_search(settings->dynamicColumns, dynamic, &key))
|
||||
continue;
|
||||
settings->fields[j++] = key;
|
||||
continue;
|
||||
}
|
||||
// This "+1" is for compatibility with the older enum format.
|
||||
int id = atoi(ids[i]) + 1;
|
||||
if (id > 0 && id < LAST_PROCESSFIELD && Process_fields[id].name) {
|
||||
fields[j] = id;
|
||||
*flags |= Process_fields[id].flags;
|
||||
j++;
|
||||
settings->flags |= Process_fields[id].flags;
|
||||
settings->fields[j++] = id;
|
||||
}
|
||||
}
|
||||
fields[j] = NULL_PROCESSFIELD;
|
||||
settings->fields[j] = NULL_PROCESSFIELD;
|
||||
String_freeArray(ids);
|
||||
}
|
||||
|
||||
static bool Settings_read(Settings* this, const char* fileName, int initialCpuCount) {
|
||||
FILE* fd;
|
||||
CRT_dropPrivileges();
|
||||
fd = fopen(fileName, "r");
|
||||
CRT_restorePrivileges();
|
||||
static bool Settings_read(Settings* this, const char* fileName, unsigned int initialCpuCount) {
|
||||
FILE* fd = fopen(fileName, "r");
|
||||
if (!fd)
|
||||
return false;
|
||||
|
||||
bool didReadMeters = false;
|
||||
bool didReadFields = false;
|
||||
bool didReadAny = false;
|
||||
for (;;) {
|
||||
char* line = String_readLine(fd);
|
||||
if (!line) {
|
||||
break;
|
||||
}
|
||||
didReadAny = true;
|
||||
size_t nOptions;
|
||||
char** option = String_split(line, '=', &nOptions);
|
||||
free (line);
|
||||
@ -131,9 +176,16 @@ static bool Settings_read(Settings* this, const char* fileName, int initialCpuCo
|
||||
String_freeArray(option);
|
||||
continue;
|
||||
}
|
||||
if (String_eq(option[0], "fields")) {
|
||||
readFields(this->fields, &(this->flags), option[1]);
|
||||
didReadFields = true;
|
||||
if (String_eq(option[0], "config_reader_min_version")) {
|
||||
this->config_version = atoi(option[1]);
|
||||
if (this->config_version > CONFIG_READER_MIN_VERSION) {
|
||||
// the version of the config file on disk is newer than what we can read
|
||||
fprintf(stderr, "WARNING: %s specifies configuration format version v%d, but this %s binary supports up to v%d.", fileName, this->config_version, PACKAGE, CONFIG_READER_MIN_VERSION);
|
||||
fprintf(stderr, " The configuration version will be downgraded to v%d when %s exits.\n", CONFIG_READER_MIN_VERSION, PACKAGE);
|
||||
return false;
|
||||
}
|
||||
} else if (String_eq(option[0], "fields")) {
|
||||
Settings_readFields(this, option[1]);
|
||||
} else if (String_eq(option[0], "sort_key")) {
|
||||
// This "+1" is for compatibility with the older enum format.
|
||||
this->sortKey = atoi(option[1]) + 1;
|
||||
@ -148,6 +200,8 @@ static bool Settings_read(Settings* this, const char* fileName, int initialCpuCo
|
||||
this->treeView = atoi(option[1]);
|
||||
} else if (String_eq(option[0], "tree_view_always_by_pid")) {
|
||||
this->treeViewAlwaysByPID = atoi(option[1]);
|
||||
} else if (String_eq(option[0], "all_branches_collapsed")) {
|
||||
this->allBranchesCollapsed = atoi(option[1]);
|
||||
} else if (String_eq(option[0], "hide_kernel_threads")) {
|
||||
this->hideKernelThreads = atoi(option[1]);
|
||||
} else if (String_eq(option[0], "hide_userland_threads")) {
|
||||
@ -160,6 +214,8 @@ static bool Settings_read(Settings* this, const char* fileName, int initialCpuCo
|
||||
this->showProgramPath = atoi(option[1]);
|
||||
} else if (String_eq(option[0], "highlight_base_name")) {
|
||||
this->highlightBaseName = atoi(option[1]);
|
||||
} else if (String_eq(option[0], "highlight_deleted_exe")) {
|
||||
this->highlightDeletedExe = atoi(option[1]);
|
||||
} else if (String_eq(option[0], "highlight_megabytes")) {
|
||||
this->highlightMegabytes = atoi(option[1]);
|
||||
} else if (String_eq(option[0], "highlight_threads")) {
|
||||
@ -167,7 +223,7 @@ static bool Settings_read(Settings* this, const char* fileName, int initialCpuCo
|
||||
} else if (String_eq(option[0], "highlight_changes")) {
|
||||
this->highlightChanges = atoi(option[1]);
|
||||
} else if (String_eq(option[0], "highlight_changes_delay_secs")) {
|
||||
this->highlightDelaySecs = CLAMP(atoi(option[1]), 1, 24*60*60);
|
||||
this->highlightDelaySecs = CLAMP(atoi(option[1]), 1, 24 * 60 * 60);
|
||||
} else if (String_eq(option[0], "find_comm_in_cmdline")) {
|
||||
this->findCommInCmdline = atoi(option[1]);
|
||||
} else if (String_eq(option[0], "strip_exe_from_cmdline")) {
|
||||
@ -190,7 +246,7 @@ static bool Settings_read(Settings* this, const char* fileName, int initialCpuCo
|
||||
this->showCPUUsage = atoi(option[1]);
|
||||
} else if (String_eq(option[0], "show_cpu_frequency")) {
|
||||
this->showCPUFrequency = atoi(option[1]);
|
||||
#ifdef HAVE_SENSORS_SENSORS_H
|
||||
#ifdef BUILD_WITH_CPU_TEMP
|
||||
} else if (String_eq(option[0], "show_cpu_temperature")) {
|
||||
this->showCPUTemperature = atoi(option[1]);
|
||||
} else if (String_eq(option[0], "degree_fahrenheit")) {
|
||||
@ -207,8 +263,16 @@ static bool Settings_read(Settings* this, const char* fileName, int initialCpuCo
|
||||
if (this->colorScheme < 0 || this->colorScheme >= LAST_COLORSCHEME) {
|
||||
this->colorScheme = 0;
|
||||
}
|
||||
#ifdef HAVE_GETMOUSE
|
||||
} else if (String_eq(option[0], "enable_mouse")) {
|
||||
this->enableMouse = atoi(option[1]);
|
||||
#endif
|
||||
} else if (String_eq(option[0], "header_layout")) {
|
||||
this->hLayout = isdigit((unsigned char)option[1][0]) ? ((HeaderLayout) atoi(option[1])) : HeaderLayout_fromName(option[1]);
|
||||
if (this->hLayout < 0 || this->hLayout >= LAST_HEADER_LAYOUT)
|
||||
this->hLayout = HF_TWO_50_50;
|
||||
free(this->hColumns);
|
||||
this->hColumns = xCalloc(HeaderLayout_getColumns(this->hLayout), sizeof(MeterColumnSetting));
|
||||
} else if (String_eq(option[0], "left_meters")) {
|
||||
Settings_readMeters(this, option[1], 0);
|
||||
didReadMeters = true;
|
||||
@ -221,6 +285,12 @@ static bool Settings_read(Settings* this, const char* fileName, int initialCpuCo
|
||||
} else if (String_eq(option[0], "right_meter_modes")) {
|
||||
Settings_readMeterModes(this, option[1], 1);
|
||||
didReadMeters = true;
|
||||
} else if (String_startsWith(option[0], "column_meters_")) {
|
||||
Settings_readMeters(this, option[1], atoi(option[0] + strlen("column_meters_")));
|
||||
didReadMeters = true;
|
||||
} else if (String_startsWith(option[0], "column_meter_modes_")) {
|
||||
Settings_readMeterModes(this, option[1], atoi(option[0] + strlen("column_meter_modes_")));
|
||||
didReadMeters = true;
|
||||
} else if (String_eq(option[0], "hide_function_bar")) {
|
||||
this->hideFunctionBar = atoi(option[1]);
|
||||
#ifdef HAVE_LIBHWLOC
|
||||
@ -234,116 +304,160 @@ static bool Settings_read(Settings* this, const char* fileName, int initialCpuCo
|
||||
if (!didReadMeters) {
|
||||
Settings_defaultMeters(this, initialCpuCount);
|
||||
}
|
||||
return didReadFields;
|
||||
return didReadAny;
|
||||
}
|
||||
|
||||
static void writeFields(FILE* fd, ProcessField* fields, const char* name) {
|
||||
static void writeFields(FILE* fd, const ProcessField* fields, Hashtable* columns, const char* name, char separator) {
|
||||
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);
|
||||
for (unsigned int i = 0; fields[i]; i++) {
|
||||
if (fields[i] >= LAST_PROCESSFIELD) {
|
||||
const DynamicColumn* column = DynamicColumn_lookup(columns, fields[i]);
|
||||
fprintf(fd, "%sDynamic(%s)", sep, column->name);
|
||||
} else {
|
||||
// This "-1" is for compatibility with the older enum format.
|
||||
fprintf(fd, "%s%d", sep, (int) fields[i] - 1);
|
||||
}
|
||||
sep = " ";
|
||||
}
|
||||
fprintf(fd, "\n");
|
||||
fputc(separator, fd);
|
||||
}
|
||||
|
||||
static void writeMeters(Settings* this, FILE* fd, int column) {
|
||||
static void writeMeters(const Settings* this, FILE* fd, char separator, unsigned 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]);
|
||||
for (uint8_t i = 0; i < this->hColumns[column].len; i++) {
|
||||
fprintf(fd, "%s%s", sep, this->hColumns[column].names[i]);
|
||||
sep = " ";
|
||||
}
|
||||
fprintf(fd, "\n");
|
||||
fputc(separator, fd);
|
||||
}
|
||||
|
||||
static void writeMeterModes(Settings* this, FILE* fd, int column) {
|
||||
static void writeMeterModes(const Settings* this, FILE* fd, char separator, unsigned 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]);
|
||||
for (uint8_t i = 0; i < this->hColumns[column].len; i++) {
|
||||
fprintf(fd, "%s%d", sep, this->hColumns[column].modes[i]);
|
||||
sep = " ";
|
||||
}
|
||||
fprintf(fd, "\n");
|
||||
fputc(separator, fd);
|
||||
}
|
||||
|
||||
bool Settings_write(Settings* this) {
|
||||
int Settings_write(const Settings* this, bool onCrash) {
|
||||
FILE* fd;
|
||||
|
||||
CRT_dropPrivileges();
|
||||
fd = fopen(this->filename, "w");
|
||||
CRT_restorePrivileges();
|
||||
|
||||
if (fd == NULL) {
|
||||
return false;
|
||||
char separator;
|
||||
if (onCrash) {
|
||||
fd = stderr;
|
||||
separator = ';';
|
||||
} else {
|
||||
fd = fopen(this->filename, "w");
|
||||
if (fd == NULL)
|
||||
return -errno;
|
||||
separator = '\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");
|
||||
writeFields(fd, this->fields, "fields");
|
||||
|
||||
#define printSettingInteger(setting_, value_) \
|
||||
fprintf(fd, setting_ "=%d%c", (int) value_, separator);
|
||||
#define printSettingString(setting_, value_) \
|
||||
fprintf(fd, setting_ "=%s%c", value_, separator);
|
||||
|
||||
if (!onCrash) {
|
||||
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");
|
||||
}
|
||||
printSettingString("htop_version", VERSION);
|
||||
printSettingInteger("config_reader_min_version", CONFIG_READER_MIN_VERSION);
|
||||
writeFields(fd, this->fields, this->dynamicColumns, "fields", separator);
|
||||
// This "-1" is for compatibility with the older enum format.
|
||||
fprintf(fd, "sort_key=%d\n", (int) this->sortKey - 1);
|
||||
fprintf(fd, "sort_direction=%d\n", (int) this->direction);
|
||||
fprintf(fd, "tree_sort_key=%d\n", (int) this->treeSortKey - 1);
|
||||
fprintf(fd, "tree_sort_direction=%d\n", (int) this->treeDirection);
|
||||
fprintf(fd, "hide_kernel_threads=%d\n", (int) this->hideKernelThreads);
|
||||
fprintf(fd, "hide_userland_threads=%d\n", (int) this->hideUserlandThreads);
|
||||
fprintf(fd, "shadow_other_users=%d\n", (int) this->shadowOtherUsers);
|
||||
fprintf(fd, "show_thread_names=%d\n", (int) this->showThreadNames);
|
||||
fprintf(fd, "show_program_path=%d\n", (int) this->showProgramPath);
|
||||
fprintf(fd, "highlight_base_name=%d\n", (int) this->highlightBaseName);
|
||||
fprintf(fd, "highlight_megabytes=%d\n", (int) this->highlightMegabytes);
|
||||
fprintf(fd, "highlight_threads=%d\n", (int) this->highlightThreads);
|
||||
fprintf(fd, "highlight_changes=%d\n", (int) this->highlightChanges);
|
||||
fprintf(fd, "highlight_changes_delay_secs=%d\n", (int) this->highlightDelaySecs);
|
||||
fprintf(fd, "find_comm_in_cmdline=%d\n", (int) this->findCommInCmdline);
|
||||
fprintf(fd, "strip_exe_from_cmdline=%d\n", (int) this->stripExeFromCmdline);
|
||||
fprintf(fd, "show_merged_command=%d\n", (int) this->showMergedCommand);
|
||||
fprintf(fd, "tree_view=%d\n", (int) this->treeView);
|
||||
fprintf(fd, "tree_view_always_by_pid=%d\n", (int) this->treeViewAlwaysByPID);
|
||||
fprintf(fd, "header_margin=%d\n", (int) this->headerMargin);
|
||||
fprintf(fd, "detailed_cpu_time=%d\n", (int) this->detailedCPUTime);
|
||||
fprintf(fd, "cpu_count_from_one=%d\n", (int) this->countCPUsFromOne);
|
||||
fprintf(fd, "show_cpu_usage=%d\n", (int) this->showCPUUsage);
|
||||
fprintf(fd, "show_cpu_frequency=%d\n", (int) this->showCPUFrequency);
|
||||
#ifdef HAVE_SENSORS_SENSORS_H
|
||||
fprintf(fd, "show_cpu_temperature=%d\n", (int) this->showCPUTemperature);
|
||||
fprintf(fd, "degree_fahrenheit=%d\n", (int) this->degreeFahrenheit);
|
||||
printSettingInteger("sort_key", this->sortKey - 1);
|
||||
printSettingInteger("sort_direction", this->direction);
|
||||
printSettingInteger("tree_sort_key", this->treeSortKey - 1);
|
||||
printSettingInteger("tree_sort_direction", this->treeDirection);
|
||||
printSettingInteger("hide_kernel_threads", this->hideKernelThreads);
|
||||
printSettingInteger("hide_userland_threads", this->hideUserlandThreads);
|
||||
printSettingInteger("shadow_other_users", this->shadowOtherUsers);
|
||||
printSettingInteger("show_thread_names", this->showThreadNames);
|
||||
printSettingInteger("show_program_path", this->showProgramPath);
|
||||
printSettingInteger("highlight_base_name", this->highlightBaseName);
|
||||
printSettingInteger("highlight_deleted_exe", this->highlightDeletedExe);
|
||||
printSettingInteger("highlight_megabytes", this->highlightMegabytes);
|
||||
printSettingInteger("highlight_threads", this->highlightThreads);
|
||||
printSettingInteger("highlight_changes", this->highlightChanges);
|
||||
printSettingInteger("highlight_changes_delay_secs", this->highlightDelaySecs);
|
||||
printSettingInteger("find_comm_in_cmdline", this->findCommInCmdline);
|
||||
printSettingInteger("strip_exe_from_cmdline", this->stripExeFromCmdline);
|
||||
printSettingInteger("show_merged_command", this->showMergedCommand);
|
||||
printSettingInteger("tree_view", this->treeView);
|
||||
printSettingInteger("tree_view_always_by_pid", this->treeViewAlwaysByPID);
|
||||
printSettingInteger("all_branches_collapsed", this->allBranchesCollapsed);
|
||||
printSettingInteger("header_margin", this->headerMargin);
|
||||
printSettingInteger("detailed_cpu_time", this->detailedCPUTime);
|
||||
printSettingInteger("cpu_count_from_one", this->countCPUsFromOne);
|
||||
printSettingInteger("show_cpu_usage", this->showCPUUsage);
|
||||
printSettingInteger("show_cpu_frequency", this->showCPUFrequency);
|
||||
#ifdef BUILD_WITH_CPU_TEMP
|
||||
printSettingInteger("show_cpu_temperature", this->showCPUTemperature);
|
||||
printSettingInteger("degree_fahrenheit", this->degreeFahrenheit);
|
||||
#endif
|
||||
fprintf(fd, "update_process_names=%d\n", (int) this->updateProcessNames);
|
||||
fprintf(fd, "account_guest_in_cpu_meter=%d\n", (int) this->accountGuestInCPUMeter);
|
||||
fprintf(fd, "color_scheme=%d\n", (int) this->colorScheme);
|
||||
fprintf(fd, "enable_mouse=%d\n", (int) this->enableMouse);
|
||||
fprintf(fd, "delay=%d\n", (int) this->delay);
|
||||
fprintf(fd, "left_meters="); writeMeters(this, fd, 0);
|
||||
fprintf(fd, "left_meter_modes="); writeMeterModes(this, fd, 0);
|
||||
fprintf(fd, "right_meters="); writeMeters(this, fd, 1);
|
||||
fprintf(fd, "right_meter_modes="); writeMeterModes(this, fd, 1);
|
||||
fprintf(fd, "hide_function_bar=%d\n", (int) this->hideFunctionBar);
|
||||
printSettingInteger("update_process_names", this->updateProcessNames);
|
||||
printSettingInteger("account_guest_in_cpu_meter", this->accountGuestInCPUMeter);
|
||||
printSettingInteger("color_scheme", this->colorScheme);
|
||||
#ifdef HAVE_GETMOUSE
|
||||
printSettingInteger("enable_mouse", this->enableMouse);
|
||||
#endif
|
||||
printSettingInteger("delay", (int) this->delay);
|
||||
printSettingInteger("hide_function_bar", (int) this->hideFunctionBar);
|
||||
#ifdef HAVE_LIBHWLOC
|
||||
fprintf(fd, "topology_affinity=%d\n", (int) this->topologyAffinity);
|
||||
printSettingInteger("topology_affinity", this->topologyAffinity);
|
||||
#endif
|
||||
fclose(fd);
|
||||
return true;
|
||||
|
||||
printSettingString("header_layout", HeaderLayout_getName(this->hLayout));
|
||||
for (unsigned int i = 0; i < HeaderLayout_getColumns(this->hLayout); i++) {
|
||||
fprintf(fd, "column_meters_%u=", i);
|
||||
writeMeters(this, fd, separator, i);
|
||||
fprintf(fd, "column_meter_modes_%u=", i);
|
||||
writeMeterModes(this, fd, separator, i);
|
||||
}
|
||||
|
||||
#undef printSettingString
|
||||
#undef printSettingInteger
|
||||
|
||||
if (onCrash)
|
||||
return 0;
|
||||
|
||||
int r = 0;
|
||||
|
||||
if (ferror(fd) != 0)
|
||||
r = (errno != 0) ? -errno : -EBADF;
|
||||
|
||||
if (fclose(fd) != 0)
|
||||
r = r ? r : -errno;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
Settings* Settings_new(int initialCpuCount) {
|
||||
Settings* Settings_new(unsigned int initialCpuCount, Hashtable* dynamicColumns) {
|
||||
Settings* this = xCalloc(1, sizeof(Settings));
|
||||
|
||||
this->dynamicColumns = dynamicColumns;
|
||||
this->hLayout = HF_TWO_50_50;
|
||||
this->hColumns = xCalloc(HeaderLayout_getColumns(this->hLayout), sizeof(MeterColumnSetting));
|
||||
this->sortKey = PERCENT_CPU;
|
||||
this->treeSortKey = PID;
|
||||
this->direction = 1;
|
||||
this->direction = -1;
|
||||
this->treeDirection = 1;
|
||||
this->shadowOtherUsers = false;
|
||||
this->showThreadNames = false;
|
||||
this->hideKernelThreads = false;
|
||||
this->hideKernelThreads = true;
|
||||
this->hideUserlandThreads = false;
|
||||
this->treeView = false;
|
||||
this->allBranchesCollapsed = false;
|
||||
this->highlightBaseName = false;
|
||||
this->highlightMegabytes = false;
|
||||
this->highlightDeletedExe = true;
|
||||
this->highlightMegabytes = true;
|
||||
this->detailedCPUTime = false;
|
||||
this->countCPUsFromOne = false;
|
||||
this->showCPUUsage = true;
|
||||
this->showCPUFrequency = false;
|
||||
#ifdef HAVE_SENSORS_SENSORS_H
|
||||
#ifdef BUILD_WITH_CPU_TEMP
|
||||
this->showCPUTemperature = false;
|
||||
this->degreeFahrenheit = false;
|
||||
#endif
|
||||
@ -356,6 +470,7 @@ Settings* Settings_new(int initialCpuCount) {
|
||||
this->stripExeFromCmdline = true;
|
||||
this->showMergedCommand = false;
|
||||
this->hideFunctionBar = 0;
|
||||
this->headerMargin = true;
|
||||
#ifdef HAVE_LIBHWLOC
|
||||
this->topologyAffinity = false;
|
||||
#endif
|
||||
@ -370,7 +485,7 @@ Settings* Settings_new(int initialCpuCount) {
|
||||
}
|
||||
|
||||
char* legacyDotfile = NULL;
|
||||
char* rcfile = getenv("HTOPRC");
|
||||
const char* rcfile = getenv("HTOPRC");
|
||||
if (rcfile) {
|
||||
this->filename = xStrdup(rcfile);
|
||||
} else {
|
||||
@ -391,7 +506,6 @@ Settings* Settings_new(int initialCpuCount) {
|
||||
htopDir = String_cat(home, "/.config/htop");
|
||||
}
|
||||
legacyDotfile = String_cat(home, "/.htoprc");
|
||||
CRT_dropPrivileges();
|
||||
(void) mkdir(configDir, 0700);
|
||||
(void) mkdir(htopDir, 0700);
|
||||
free(htopDir);
|
||||
@ -402,10 +516,11 @@ Settings* Settings_new(int initialCpuCount) {
|
||||
free(legacyDotfile);
|
||||
legacyDotfile = NULL;
|
||||
}
|
||||
CRT_restorePrivileges();
|
||||
}
|
||||
this->colorScheme = 0;
|
||||
#ifdef HAVE_GETMOUSE
|
||||
this->enableMouse = true;
|
||||
#endif
|
||||
this->changed = false;
|
||||
this->delay = DEFAULT_DELAY;
|
||||
bool ok = false;
|
||||
@ -413,7 +528,7 @@ Settings* Settings_new(int initialCpuCount) {
|
||||
ok = Settings_read(this, legacyDotfile, initialCpuCount);
|
||||
if (ok) {
|
||||
// Transition to new location and delete old configuration file
|
||||
if (Settings_write(this)) {
|
||||
if (Settings_write(this, false) == 0) {
|
||||
unlink(legacyDotfile);
|
||||
}
|
||||
}
|
||||
@ -424,20 +539,7 @@ Settings* Settings_new(int initialCpuCount) {
|
||||
}
|
||||
if (!ok) {
|
||||
this->changed = true;
|
||||
// TODO: how to get SYSCONFDIR correctly through Autoconf?
|
||||
char* systemSettings = String_cat(SYSCONFDIR, "/htoprc");
|
||||
ok = Settings_read(this, systemSettings, initialCpuCount);
|
||||
free(systemSettings);
|
||||
}
|
||||
if (!ok) {
|
||||
Settings_defaultMeters(this, initialCpuCount);
|
||||
this->hideKernelThreads = true;
|
||||
this->highlightMegabytes = true;
|
||||
this->highlightThreads = true;
|
||||
this->findCommInCmdline = true;
|
||||
this->stripExeFromCmdline = true;
|
||||
this->showMergedCommand = false;
|
||||
this->headerMargin = true;
|
||||
Settings_read(this, SYSCONFDIR "/htoprc", initialCpuCount);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
@ -450,10 +552,43 @@ void Settings_invertSortOrder(Settings* this) {
|
||||
void Settings_setSortKey(Settings* this, ProcessField sortKey) {
|
||||
if (this->treeViewAlwaysByPID || !this->treeView) {
|
||||
this->sortKey = sortKey;
|
||||
this->direction = 1;
|
||||
this->direction = (Process_fields[sortKey].defaultSortDesc) ? -1 : 1;
|
||||
this->treeView = false;
|
||||
} else {
|
||||
this->treeSortKey = sortKey;
|
||||
this->treeDirection = 1;
|
||||
this->treeDirection = (Process_fields[sortKey].defaultSortDesc) ? -1 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
static bool readonly = false;
|
||||
|
||||
void Settings_enableReadonly(void) {
|
||||
readonly = true;
|
||||
}
|
||||
|
||||
bool Settings_isReadonly(void) {
|
||||
return readonly;
|
||||
}
|
||||
|
||||
void Settings_setHeaderLayout(Settings* this, HeaderLayout hLayout) {
|
||||
unsigned int oldColumns = HeaderLayout_getColumns(this->hLayout);
|
||||
unsigned int newColumns = HeaderLayout_getColumns(hLayout);
|
||||
|
||||
if (newColumns > oldColumns) {
|
||||
this->hColumns = xReallocArray(this->hColumns, newColumns, sizeof(MeterColumnSetting));
|
||||
memset(this->hColumns + oldColumns, 0, (newColumns - oldColumns) * sizeof(MeterColumnSetting));
|
||||
} else if (newColumns < oldColumns) {
|
||||
for (unsigned int i = newColumns; i < oldColumns; i++) {
|
||||
if (this->hColumns[i].names) {
|
||||
for (uint8_t j = 0; j < this->hColumns[i].len; j++)
|
||||
free(this->hColumns[i].names[j]);
|
||||
free(this->hColumns[i].names);
|
||||
}
|
||||
free(this->hColumns[i].modes);
|
||||
}
|
||||
this->hColumns = xReallocArray(this->hColumns, newColumns, sizeof(MeterColumnSetting));
|
||||
}
|
||||
|
||||
this->hLayout = hLayout;
|
||||
this->changed = true;
|
||||
}
|
||||
|
29
Settings.h
29
Settings.h
@ -12,20 +12,27 @@ in the source distribution for its full text.
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "Hashtable.h"
|
||||
#include "HeaderLayout.h"
|
||||
#include "Process.h"
|
||||
|
||||
|
||||
#define DEFAULT_DELAY 15
|
||||
|
||||
#define CONFIG_READER_MIN_VERSION 2
|
||||
|
||||
typedef struct {
|
||||
int len;
|
||||
uint8_t len;
|
||||
char** names;
|
||||
int* modes;
|
||||
} MeterColumnSettings;
|
||||
} MeterColumnSetting;
|
||||
|
||||
typedef struct Settings_ {
|
||||
char* filename;
|
||||
MeterColumnSettings columns[2];
|
||||
int config_version;
|
||||
HeaderLayout hLayout;
|
||||
MeterColumnSetting* hColumns;
|
||||
Hashtable* dynamicColumns;
|
||||
|
||||
ProcessField* fields;
|
||||
uint32_t flags;
|
||||
@ -41,18 +48,20 @@ typedef struct Settings_ {
|
||||
bool detailedCPUTime;
|
||||
bool showCPUUsage;
|
||||
bool showCPUFrequency;
|
||||
#ifdef HAVE_SENSORS_SENSORS_H
|
||||
#ifdef BUILD_WITH_CPU_TEMP
|
||||
bool showCPUTemperature;
|
||||
bool degreeFahrenheit;
|
||||
#endif
|
||||
bool treeView;
|
||||
bool treeViewAlwaysByPID;
|
||||
bool allBranchesCollapsed;
|
||||
bool showProgramPath;
|
||||
bool shadowOtherUsers;
|
||||
bool showThreadNames;
|
||||
bool hideKernelThreads;
|
||||
bool hideUserlandThreads;
|
||||
bool highlightBaseName;
|
||||
bool highlightDeletedExe;
|
||||
bool highlightMegabytes;
|
||||
bool highlightThreads;
|
||||
bool highlightChanges;
|
||||
@ -63,7 +72,9 @@ typedef struct Settings_ {
|
||||
bool updateProcessNames;
|
||||
bool accountGuestInCPUMeter;
|
||||
bool headerMargin;
|
||||
#ifdef HAVE_GETMOUSE
|
||||
bool enableMouse;
|
||||
#endif
|
||||
int hideFunctionBar; // 0 - off, 1 - on ESC until next input, 2 - permanently
|
||||
#ifdef HAVE_LIBHWLOC
|
||||
bool topologyAffinity;
|
||||
@ -86,12 +97,18 @@ static inline int Settings_getActiveDirection(const Settings* this) {
|
||||
|
||||
void Settings_delete(Settings* this);
|
||||
|
||||
bool Settings_write(Settings* this);
|
||||
int Settings_write(const Settings* this, bool onCrash);
|
||||
|
||||
Settings* Settings_new(int initialCpuCount);
|
||||
Settings* Settings_new(unsigned int initialCpuCount, Hashtable* dynamicColumns);
|
||||
|
||||
void Settings_invertSortOrder(Settings* this);
|
||||
|
||||
void Settings_setSortKey(Settings* this, ProcessField sortKey);
|
||||
|
||||
void Settings_enableReadonly(void);
|
||||
|
||||
bool Settings_isReadonly(void);
|
||||
|
||||
void Settings_setHeaderLayout(Settings* this, HeaderLayout hLayout);
|
||||
|
||||
#endif
|
||||
|
@ -9,6 +9,7 @@ in the source distribution for its full text.
|
||||
|
||||
#include "Panel.h"
|
||||
|
||||
|
||||
typedef struct SignalItem_ {
|
||||
const char* name;
|
||||
int number;
|
||||
|
22
SwapMeter.c
22
SwapMeter.c
@ -5,8 +5,13 @@ Released under the GNU GPLv2, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
#include "config.h" // IWYU pragma: keep
|
||||
|
||||
#include "SwapMeter.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "CRT.h"
|
||||
#include "Object.h"
|
||||
#include "Platform.h"
|
||||
@ -14,11 +19,16 @@ in the source distribution for its full text.
|
||||
|
||||
|
||||
static const int SwapMeter_attributes[] = {
|
||||
SWAP
|
||||
SWAP,
|
||||
SWAP_CACHE
|
||||
};
|
||||
|
||||
static void SwapMeter_updateValues(Meter* this, char* buffer, size_t size) {
|
||||
static void SwapMeter_updateValues(Meter* this) {
|
||||
char* buffer = this->txtBuffer;
|
||||
size_t size = sizeof(this->txtBuffer);
|
||||
int written;
|
||||
|
||||
this->values[1] = NAN; /* 'cached' not present on all platforms */
|
||||
Platform_setSwapValues(this);
|
||||
|
||||
written = Meter_humanUnit(buffer, this->values[0], size);
|
||||
@ -38,6 +48,12 @@ static void SwapMeter_display(const Object* cast, RichString* out) {
|
||||
Meter_humanUnit(buffer, this->values[0], sizeof(buffer));
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], " used:");
|
||||
RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer);
|
||||
|
||||
if (!isnan(this->values[1])) {
|
||||
Meter_humanUnit(buffer, this->values[1], sizeof(buffer));
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], " cache:");
|
||||
RichString_appendAscii(out, CRT_colors[SWAP_CACHE], buffer);
|
||||
}
|
||||
}
|
||||
|
||||
const MeterClass SwapMeter_class = {
|
||||
@ -48,7 +64,7 @@ const MeterClass SwapMeter_class = {
|
||||
},
|
||||
.updateValues = SwapMeter_updateValues,
|
||||
.defaultMode = BAR_METERMODE,
|
||||
.maxItems = 1,
|
||||
.maxItems = 2,
|
||||
.total = 100.0,
|
||||
.attributes = SwapMeter_attributes,
|
||||
.name = "Swap",
|
||||
|
@ -9,6 +9,7 @@ in the source distribution for its full text.
|
||||
|
||||
#include "Meter.h"
|
||||
|
||||
|
||||
extern const MeterClass SwapMeter_class;
|
||||
|
||||
#endif
|
||||
|
44
SysArchMeter.c
Normal file
44
SysArchMeter.c
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
htop - SysArchMeter.c
|
||||
(C) 2021 htop dev team
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
#include "config.h" // IWYU pragma: keep
|
||||
|
||||
#include "SysArchMeter.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "CRT.h"
|
||||
#include "Object.h"
|
||||
#include "Platform.h"
|
||||
#include "XUtils.h"
|
||||
|
||||
|
||||
static const int SysArchMeter_attributes[] = {HOSTNAME};
|
||||
|
||||
static void SysArchMeter_updateValues(Meter* this) {
|
||||
static char* string;
|
||||
|
||||
if (string == NULL)
|
||||
Platform_getRelease(&string);
|
||||
|
||||
String_safeStrncpy(this->txtBuffer, string, sizeof(this->txtBuffer));
|
||||
}
|
||||
|
||||
const MeterClass SysArchMeter_class = {
|
||||
.super = {
|
||||
.extends = Class(Meter),
|
||||
.delete = Meter_delete
|
||||
},
|
||||
.updateValues = SysArchMeter_updateValues,
|
||||
.defaultMode = TEXT_METERMODE,
|
||||
.maxItems = 0,
|
||||
.total = 100.0,
|
||||
.attributes = SysArchMeter_attributes,
|
||||
.name = "System",
|
||||
.uiName = "System",
|
||||
.caption = "System: ",
|
||||
};
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user