mirror of
https://github.com/xzeldon/htop.git
synced 2025-07-15 13:34:35 +03:00
Compare commits
868 Commits
Author | SHA1 | Date | |
---|---|---|---|
eebd5e8517
|
|||
7044d546ea
|
|||
765e3de1e7
|
|||
637550ed51
|
|||
fc016ae780
|
|||
36260b5814
|
|||
611ea4606f | |||
4e6ec4a087 | |||
abaec509e6 | |||
f156dfecd5 | |||
2999fff88e | |||
2613db4b0d | |||
9eed30949b | |||
ce50095323 | |||
17e28d5264 | |||
da97d2625a | |||
7694dbc821 | |||
c0a9e92eea | |||
491c6f1044 | |||
98cbdc6dca | |||
9ed9d73ab5 | |||
999801464a | |||
0e29174211 | |||
efe09a5e39 | |||
038f2ae777 | |||
0af08bcfc9 | |||
e053446cbd | |||
3d8fa0b926 | |||
d73cc70566 | |||
37e01cbe33 | |||
d22667725a | |||
ef4cbae5ea | |||
44091705db | |||
87793b8555 | |||
fe7f238e2c | |||
c24681a078 | |||
2da8f71209 | |||
51228b6239 | |||
33973f7e40 | |||
79db69c48d | |||
9fc72c1e9c | |||
db93268968 | |||
4f1269cc9f | |||
0388b30077 | |||
4b8b61fe18 | |||
fae7ff6f03 | |||
e07fce7014 | |||
e08eec813c | |||
549fcb6bb8 | |||
08166b27b1 | |||
0d53245cf9 | |||
c7413fd677 | |||
8f0475cd73 | |||
a155fd0f8b | |||
549543f8e4 | |||
10b541b5e4 | |||
73f08debe0 | |||
8b98d3effb | |||
7e66ee1d28 | |||
a7a6571d14 | |||
cde72dd0b0 | |||
cb61865bb9 | |||
c144bf9ae5 | |||
ae518e20b7 | |||
cdf3f3c50b | |||
1f2f4fe891 | |||
ec809b7f71 | |||
b83ce85d89 | |||
ee1bf2f917 | |||
afc4a9d657 | |||
3f3691886a | |||
99aa906bc5 | |||
df955c8991 | |||
6a7b3fdc7d | |||
72c56691ec | |||
4ccad46045 | |||
7039abe109 | |||
2b7504b522 | |||
8b927ba596 | |||
0ffd772d28 | |||
64fb7181ee | |||
ba4c67942c | |||
3f0c172a60 | |||
7c43e02591 | |||
c6f946edd2 | |||
a2ca7583a9 | |||
469ae7a0bd | |||
c4239335b9 | |||
23b56193d7 | |||
edf319e53d | |||
b6c0667eae | |||
3ba695293c | |||
6133cac721 | |||
da653f8148 | |||
d35db47c9a | |||
978a7c894f | |||
79a6f6cb5b | |||
deb05fe7c2 | |||
82d34deaf1 | |||
fa3e0d06c2 | |||
82dce5cf8e | |||
8d987864e4 | |||
58b42e4cac | |||
2477a5a018 | |||
1a403eb7eb | |||
a3a7958721 | |||
4aeb146ce8 | |||
8c99683b04 | |||
265a7b8a50 | |||
939685dff9 | |||
3e1a27a981 | |||
9512fd7930 | |||
4a664c0df8 | |||
d0d9f202c5 | |||
a0ad0697a8 | |||
a133ffd829 | |||
fde1243443 | |||
6eab39c0ab | |||
2c3a64ac9c | |||
442c1596f6 | |||
f782f821f7 | |||
5b78ad2d53 | |||
1ef8c0e12f | |||
6fcb1994c8 | |||
5bc988ad6d | |||
6e9a5e9e49 | |||
14f428a172 | |||
6388033e10 | |||
b45eaf2fe1 | |||
230dc9c3c1 | |||
d084a80023 | |||
5c8670717a | |||
bc08c7dc2a | |||
1e94b92226 | |||
63fafb4844 | |||
c85e5bbf5c | |||
c9e0bd2002 | |||
df1914f429 | |||
3cfdf66d9a | |||
fa9f260f63 | |||
1da78b5818 | |||
2ae1906479 | |||
0e58784224 | |||
4ef5e4296e | |||
ba3a1df806 | |||
b672e60886 | |||
cd6457ef88 | |||
31fe29c5a7 | |||
cc2547fcf0 | |||
72ba20fa5f | |||
ff4f44b22a | |||
a38f48481e | |||
61c9fe44a3 | |||
ff0ea41c86 | |||
43e9be5a8f | |||
d73783d6db | |||
89b7c4c9f9 | |||
78aefc2a99 | |||
697c502b7e | |||
171aa0faaa | |||
ddfacb8694 | |||
5beef3e737 | |||
27a18830d5 | |||
b98a4f8d56 | |||
55e073a455 | |||
915b558bbe | |||
550100327b | |||
9dc964bb42 | |||
ea4282784d | |||
1c3c149d20 | |||
42c0493021 | |||
1284ab4835 | |||
07496eafb0 | |||
e7a8d14cbd | |||
21cb1c4d59 | |||
e64269df2c | |||
d8dfbbd37c | |||
afa3fe4af1 | |||
2ef70ad7f6 | |||
2977414d54 | |||
696f79fe50 | |||
4374a267be | |||
3e70de64b3 | |||
3f9f52fd29 | |||
dfa62506b7 | |||
731b4003a2 | |||
30c5004cbb | |||
9d3a1d4981 | |||
60cfa2edce | |||
604744f68d | |||
13fe58f54a | |||
e785693ef9 | |||
4cf74de1f1 | |||
0a5890d332 | |||
2128edfba2 | |||
d1c833fe95 | |||
868b0b3574 | |||
04871a2d04 | |||
b7248f6cb8 | |||
74afca7bc1 | |||
2a13e15ad0 | |||
8ac8542b6e | |||
f75a8bc3a1 | |||
d520199e62 | |||
9d8d87e1d5 | |||
57c1538887 | |||
9eb52d5932 | |||
970885edc3 | |||
ae1816e563 | |||
53732ab0bb | |||
8932efece9 | |||
d23627fda9 | |||
4c846921a6 | |||
c5e789db1b | |||
eb94c4f9c9 | |||
8361c6c1ae | |||
07f934ccfb | |||
6f751d5929 | |||
e26a2cf431 | |||
d527bc9132 | |||
dadcb87ad0 | |||
754c0d6bb9 | |||
9f4a4faab9 | |||
a710deeaa7 | |||
466d6f99e2 | |||
1601931bbf | |||
41f9c0ab77 | |||
e28d022716 | |||
d5ac75a5c7 | |||
2ba7d5ff36 | |||
3d99c306b9 | |||
3d3474b3fc | |||
94ad111391 | |||
976fa3b121 | |||
61227793de | |||
e6ded48d1a | |||
87c992739b | |||
29e1fcfa05 | |||
29983ff83a | |||
36b7f57200 | |||
c5fe142256 | |||
691b7dd5af | |||
ad0f9c58bf | |||
a8d0f2a7fe | |||
e6596cbd69 | |||
40c9f89971 | |||
09ad8067f4 | |||
bda3ee2b81 | |||
bdb015ffa3 | |||
80a515abcc | |||
5000cefc13 | |||
bf7d98e7ac | |||
c096712b8d | |||
be82448bd5 | |||
68c00b9cdb | |||
3c8d586a1c | |||
3869c43393 | |||
fcca4c2f2d | |||
dd88510dcd | |||
43ffdb0eda | |||
bf395e10c5 | |||
8f259bc5e1 | |||
38e6136b82 | |||
589b0733d9 | |||
c9b58c7fbe | |||
ca06e68037 | |||
a516e0852e | |||
3f805cf347 | |||
556d7c03e8 | |||
0925c54caa | |||
d0f5b61aa5 | |||
284f8c5e0b | |||
11b65a2861 | |||
b85b718e69 | |||
7891cdc552 | |||
b9fdf1c2a1 | |||
2844429f15 | |||
3834f2a68f | |||
343c2e58be | |||
fd2c4f5ddd | |||
7f95ed8528 | |||
9579d9b7aa | |||
0580dbb202 | |||
c0c2bb98a2 | |||
9b30870eec | |||
25c945e2ef | |||
f94934472f | |||
becd33795c | |||
74f99e3693 | |||
e1f4645bd1 | |||
0afd0fe572 | |||
ff336b652c | |||
58a59c11f4 | |||
3f806368e0 | |||
4855d92469 | |||
b81bb9038c | |||
1f2f567ca1 | |||
393330239e | |||
d2c34259b4 | |||
858ad8029d | |||
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 | |||
ce6d60e7de | |||
5d92a9f20d | |||
b3500ac3b7 | |||
2ba8a81d47 | |||
f2f1c99ad9 | |||
1ffe5d79bd | |||
8502f4e64f | |||
a5db139a0a | |||
8a67d7f086 | |||
0b89c66f58 | |||
3bb731c645 | |||
fbaa0cd146 | |||
a076488809 | |||
6301d5c1da | |||
4979245aa5 | |||
0155980fd6 | |||
2af90b711f | |||
d55f394541 | |||
c7d93a8f30 | |||
2d2a2df6f2 | |||
de645ea16c | |||
6ae56f2578 | |||
bfcb8ca019 | |||
d800d7a3ce | |||
27db9297b7 | |||
330d4fe22f | |||
8e10cde800 | |||
ca2c01bd16 | |||
7043a93eba | |||
8fe04b7494 | |||
43d5c61884 | |||
e103ec0317 | |||
7ff654f2df | |||
7386c6fed0 | |||
ce9e7fd14f | |||
badeaf9e82 | |||
a3cced9fb6 | |||
24c5ca9ddf | |||
eb6f8d569d | |||
8c8149d146 | |||
a150a81669 | |||
90ea3ac3c9 | |||
293c16e22d | |||
f6aa5d29bb | |||
2c06566405 | |||
d609c04fe4 | |||
ca9d7cd708 | |||
debeac49cd | |||
a0b899f29d | |||
8b83a9f055 | |||
495f2292dc | |||
1cc3f8074f | |||
aa08279964 | |||
5359eae28b | |||
f1463fdd64 | |||
3edb6e1ea3 | |||
71ddc6a6a1 | |||
b9336af76f | |||
f46ddd3230 | |||
94d7f0b585 | |||
86d2931255 | |||
0672be7db1 |
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1 @@
|
||||
open_collective: htop
|
51
.github/workflows/build_release.yml
vendored
Normal file
51
.github/workflows/build_release.yml
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
name: Build Source Release
|
||||
|
||||
# Trigger whenever a release is created
|
||||
on:
|
||||
release:
|
||||
types:
|
||||
- created
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: archive
|
||||
id: archive
|
||||
run: |
|
||||
VERSION=${{ github.event.release.tag_name }}
|
||||
PKGNAME="htop-$VERSION"
|
||||
SHASUM=$PKGNAME.tar.xz.sha256
|
||||
autoreconf -i
|
||||
mkdir -p /tmp/$PKGNAME
|
||||
mv * /tmp/$PKGNAME
|
||||
mv /tmp/$PKGNAME .
|
||||
TARBALL=$PKGNAME.tar.xz
|
||||
tar cJf $TARBALL $PKGNAME
|
||||
sha256sum $TARBALL > $SHASUM
|
||||
echo "::set-output name=tarball::$TARBALL"
|
||||
echo "::set-output name=shasum::$SHASUM"
|
||||
- name: upload tarball
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ github.event.release.upload_url }}
|
||||
asset_path: ./${{ steps.archive.outputs.tarball }}
|
||||
asset_name: ${{ steps.archive.outputs.tarball }}
|
||||
asset_content_type: application/gzip
|
||||
|
||||
- name: upload shasum
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ github.event.release.upload_url }}
|
||||
asset_path: ./${{ steps.archive.outputs.shasum }}
|
||||
asset_name: ${{ steps.archive.outputs.shasum }}
|
||||
asset_content_type: text/plain
|
88
.github/workflows/ci.yml
vendored
88
.github/workflows/ci.yml
vendored
@ -2,17 +2,22 @@ name: CI
|
||||
|
||||
on: [ push, pull_request ]
|
||||
|
||||
env:
|
||||
# Enable format attributes in ncurses headers
|
||||
# Enable fortified memory/string handling
|
||||
CPPFLAGS: -DGCC_PRINTF -DGCC_SCANF -D_FORTIFY_SOURCE=2
|
||||
|
||||
jobs:
|
||||
build-ubuntu-latest-minimal-gcc:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Dependencies
|
||||
run: sudo apt-get install libncursesw5-dev
|
||||
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
|
||||
@ -21,86 +26,127 @@ 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 libncursesw5-dev libhwloc-dev libnl-3-dev libnl-genl-3-dev libsensors4-dev
|
||||
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-setuid --enable-delayacct --with-sensors
|
||||
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-setuid --enable-delayacct --with-sensors'
|
||||
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-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 libhwloc-dev libnl-3-dev libnl-genl-3-dev libsensors4-dev
|
||||
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-setuid --enable-delayacct --with-sensors
|
||||
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-setuid --enable-delayacct --with-sensors'
|
||||
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:
|
||||
CFLAGS: -O3 -g -flto
|
||||
LDFLAGS: -O3 -g -flto
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Dependencies
|
||||
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-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-static --enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --disable-hwloc --disable-delayacct --enable-sensors --enable-capabilities'
|
||||
|
||||
build-ubuntu-latest-pcp:
|
||||
# Turns out 'ubuntu-latest' can be older than 20.04, we want PCP v5+
|
||||
runs-on: ubuntu-20.04
|
||||
env:
|
||||
# Until Ubuntu catches up with pcp-5.2.3+:
|
||||
# pcp/Platform.c:309:45: warning: passing argument 2 of ‘pmLookupName’ from incompatible pointer type [-Wincompatible-pointer-types]
|
||||
CFLAGS: -Wno-error=incompatible-pointer-types
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Dependencies
|
||||
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-pcp --enable-unicode
|
||||
- name: Build
|
||||
run: make -k
|
||||
|
||||
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/
|
||||
|
@ -9,7 +9,8 @@ os:
|
||||
|
||||
script:
|
||||
- ./autogen.sh
|
||||
# clang might warn about C11 generic selections in isnan()
|
||||
- CFLAGS=-Wno-c11-extensions ./configure --enable-werror
|
||||
- ./configure --enable-werror
|
||||
- make -k
|
||||
- CFLAGS=-Wno-c11-extensions make distcheck DISTCHECK_CONFIGURE_FLAGS=--enable-werror
|
||||
- make distcheck DISTCHECK_CONFIGURE_FLAGS=--enable-werror
|
||||
- sudo make install
|
||||
- make installcheck
|
||||
|
459
Action.c
459
Action.c
@ -1,7 +1,7 @@
|
||||
/*
|
||||
htop - Action.c
|
||||
(C) 2015 Hisham H. Muhammad
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
@ -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,40 +35,39 @@ 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;
|
||||
}
|
||||
ScreenManager_run(scr, &panelFocus, &ch);
|
||||
ScreenManager_run(scr, &panelFocus, &ch, NULL);
|
||||
if (unfollow) {
|
||||
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,14 +84,11 @@ 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, "Setup");
|
||||
ScreenManager_delete(scr);
|
||||
if (st->settings->changed) {
|
||||
CRT_setMouse(st->settings->enableMouse);
|
||||
Header_writeBackToSettings(st->header);
|
||||
}
|
||||
}
|
||||
@ -104,7 +101,7 @@ static bool changePriority(MainPanel* panel, int delta) {
|
||||
return anyTagged;
|
||||
}
|
||||
|
||||
static void addUserToVector(hkey_t key, void* userCast, void* panelCast) {
|
||||
static void addUserToVector(ht_key_t key, void* userCast, void* panelCast) {
|
||||
const char* user = userCast;
|
||||
Panel* panel = panelCast;
|
||||
Panel_add(panel, (Object*) ListItem_new(user, key));
|
||||
@ -141,7 +138,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;
|
||||
|
||||
@ -158,41 +155,48 @@ static bool collapseIntoParent(Panel* panel) {
|
||||
}
|
||||
|
||||
Htop_Reaction Action_setSortKey(Settings* settings, ProcessField sortKey) {
|
||||
Settings_setSortKey(settings, sortKey);
|
||||
ScreenSettings_setSortKey(settings->ss, sortKey);
|
||||
return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_UPDATE_PANELHDR | HTOP_KEEP_FOLLOWING;
|
||||
}
|
||||
|
||||
static Htop_Reaction sortBy(State* st) {
|
||||
// ----------------------------------------
|
||||
|
||||
static Htop_Reaction actionSetSortColumn(State* st) {
|
||||
Htop_Reaction reaction = HTOP_OK;
|
||||
Panel* sortPanel = Panel_new(0, 0, 0, 0, true, Class(ListItem), FunctionBar_newEnterEsc("Sort ", "Cancel "));
|
||||
Panel* sortPanel = Panel_new(0, 0, 0, 0, Class(ListItem), true, FunctionBar_newEnterEsc("Sort ", "Cancel "));
|
||||
Panel_setHeader(sortPanel, "Sort by");
|
||||
ProcessField* fields = st->settings->fields;
|
||||
const Settings* settings = st->settings;
|
||||
const ProcessField* fields = settings->ss->fields;
|
||||
Hashtable* dynamicColumns = 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))
|
||||
if (fields[i] == ScreenSettings_getActiveSortKey(settings->ss))
|
||||
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);
|
||||
}
|
||||
Object_delete(sortPanel);
|
||||
|
||||
if (st->pauseProcessUpdate)
|
||||
ProcessList_sort(st->pl);
|
||||
st->pl->needsSort = true;
|
||||
|
||||
return reaction | HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
|
||||
}
|
||||
|
||||
// ----------------------------------------
|
||||
|
||||
static Htop_Reaction actionResize(State* st) {
|
||||
clear();
|
||||
Panel_resize(st->panel, COLS, LINES - (st->panel->y) - 1);
|
||||
return HTOP_REDRAW_BAR;
|
||||
static Htop_Reaction actionSortByPID(State* st) {
|
||||
return Action_setSortKey(st->settings, PID);
|
||||
}
|
||||
|
||||
static Htop_Reaction actionSortByMemory(State* st) {
|
||||
@ -209,12 +213,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) {
|
||||
@ -228,74 +232,116 @@ 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;
|
||||
}
|
||||
ScreenSettings* ss = st->settings->ss;
|
||||
ss->treeView = !ss->treeView;
|
||||
|
||||
if (!ss->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) {
|
||||
ScreenSettings* ss = st->settings->ss;
|
||||
if (!ss->treeView) {
|
||||
return HTOP_OK;
|
||||
}
|
||||
ss->allBranchesCollapsed = !ss->allBranchesCollapsed;
|
||||
if (ss->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);
|
||||
return HTOP_REFRESH | HTOP_KEEP_FOLLOWING;
|
||||
}
|
||||
|
||||
static Htop_Reaction actionIncNext(State* st) {
|
||||
IncSet_next(((MainPanel*)st->panel)->inc, INC_SEARCH, st->panel, (IncMode_GetPanelValue) MainPanel_getValue);
|
||||
return HTOP_REFRESH | HTOP_KEEP_FOLLOWING;
|
||||
}
|
||||
|
||||
static Htop_Reaction actionIncPrev(State* st) {
|
||||
IncSet_prev(((MainPanel*)st->panel)->inc, INC_SEARCH, st->panel, (IncMode_GetPanelValue) MainPanel_getValue);
|
||||
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;
|
||||
}
|
||||
|
||||
static Htop_Reaction actionInvertSortOrder(State* st) {
|
||||
Settings_invertSortOrder(st->settings);
|
||||
if (st->pauseProcessUpdate)
|
||||
ProcessList_sort(st->pl);
|
||||
return HTOP_REFRESH | HTOP_SAVE_SETTINGS;
|
||||
}
|
||||
|
||||
static Htop_Reaction actionSetSortColumn(State* st) {
|
||||
return sortBy(st);
|
||||
ScreenSettings_invertSortOrder(st->settings->ss);
|
||||
st->pl->needsSort = true;
|
||||
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;
|
||||
}
|
||||
|
||||
static Htop_Reaction actionCollapseIntoParent(State* st) {
|
||||
if (!st->settings->treeView) {
|
||||
if (!st->settings->ss->treeView) {
|
||||
return HTOP_OK;
|
||||
}
|
||||
bool changed = collapseIntoParent(st->panel);
|
||||
bool changed = collapseIntoParent((Panel*)st->mainPanel);
|
||||
return changed ? HTOP_RECALCULATE : HTOP_OK;
|
||||
}
|
||||
|
||||
static Htop_Reaction actionExpandCollapseOrSortColumn(State* st) {
|
||||
return st->settings->treeView ? actionExpandOrCollapse(st) : actionSetSortColumn(st);
|
||||
return st->settings->ss->treeView ? actionExpandOrCollapse(st) : actionSetSortColumn(st);
|
||||
}
|
||||
|
||||
static Htop_Reaction actionNextScreen(State* st) {
|
||||
Settings* settings = st->settings;
|
||||
settings->ssIndex++;
|
||||
if (settings->ssIndex == settings->nScreens) {
|
||||
settings->ssIndex = 0;
|
||||
}
|
||||
settings->ss = settings->screens[settings->ssIndex];
|
||||
return HTOP_REFRESH;
|
||||
}
|
||||
|
||||
static Htop_Reaction actionPrevScreen(State* st) {
|
||||
Settings* settings = st->settings;
|
||||
if (settings->ssIndex == 0) {
|
||||
settings->ssIndex = settings->nScreens - 1;
|
||||
} else {
|
||||
settings->ssIndex--;
|
||||
}
|
||||
settings->ss = settings->screens[settings->ssIndex];
|
||||
return HTOP_REFRESH;
|
||||
}
|
||||
|
||||
Htop_Reaction Action_setScreenTab(Settings* settings, int x) {
|
||||
int s = 2;
|
||||
for (unsigned int i = 0; i < settings->nScreens; i++) {
|
||||
if (x < s) {
|
||||
return 0;
|
||||
}
|
||||
const char* name = settings->screens[i]->name;
|
||||
int len = strlen(name);
|
||||
if (x <= s + len + 1) {
|
||||
settings->ssIndex = i;
|
||||
settings->ss = settings->screens[i];
|
||||
return HTOP_REFRESH;
|
||||
}
|
||||
s += len + 3;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Htop_Reaction actionQuit(ATTR_UNUSED State* st) {
|
||||
@ -303,13 +349,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;
|
||||
|
||||
@ -319,46 +366,52 @@ 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) {
|
||||
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);
|
||||
if (Settings_isReadonly())
|
||||
return HTOP_OK;
|
||||
|
||||
static int preSelectedSignal = SIGNALSPANEL_INITSELECTEDSIGNAL;
|
||||
|
||||
Panel* signalsPanel = SignalsPanel_new(preSelectedSignal);
|
||||
const ListItem* sgn = (ListItem*) Action_pickFromVector(st, signalsPanel, 14, true);
|
||||
if (sgn && sgn->key != 0) {
|
||||
preSelectedSignal = sgn->key;
|
||||
Panel_setHeader((Panel*)st->mainPanel, "Sending...");
|
||||
Panel_draw((Panel*)st->mainPanel, false, true, true, State_hideFunctionBar(st));
|
||||
refresh();
|
||||
MainPanel_foreachProcess((MainPanel*)st->panel, Process_sendSignal, (Arg) { .i = sgn->key }, NULL);
|
||||
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;
|
||||
}
|
||||
|
||||
static Htop_Reaction actionFilterByUser(State* st) {
|
||||
Panel* usersPanel = Panel_new(0, 0, 0, 0, true, Class(ListItem), FunctionBar_newEnterEsc("Show ", "Cancel "));
|
||||
Panel* usersPanel = Panel_new(0, 0, 0, 0, Class(ListItem), true, FunctionBar_newEnterEsc("Show ", "Cancel "));
|
||||
Panel_setHeader(usersPanel, "Show processes of:");
|
||||
UsersTable_foreach(st->ut, addUserToVector, usersPanel);
|
||||
Vector_insertionSort(usersPanel->items);
|
||||
ListItem* allUsers = ListItem_new("All users", -1);
|
||||
Panel_insert(usersPanel, 0, (Object*) allUsers);
|
||||
ListItem* picked = (ListItem*) Action_pickFromVector(st, usersPanel, 20, false);
|
||||
const ListItem* picked = (ListItem*) Action_pickFromVector(st, usersPanel, 19, false);
|
||||
if (picked) {
|
||||
if (picked == allUsers) {
|
||||
st->pl->userId = (uid_t)-1;
|
||||
@ -371,22 +424,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;
|
||||
|
||||
@ -399,8 +451,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);
|
||||
@ -410,7 +463,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;
|
||||
|
||||
@ -426,12 +482,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;
|
||||
}
|
||||
|
||||
@ -447,49 +503,53 @@ 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 = " P M T: ", .info = "sort by CPU%, MEM% or TIME" },
|
||||
{ .key = " I: ", .info = "invert sort order" },
|
||||
{ .key = " F6 > .: ", .info = "select sort column" },
|
||||
{ .key = " Tab: ", .roInactive = false, .info = "switch to next screen tab" },
|
||||
{ .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 = " S-Tab: ", .roInactive = false, .info = "switch to previous screen tab" },
|
||||
{ .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 = true, .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 }
|
||||
};
|
||||
|
||||
@ -499,8 +559,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]);
|
||||
|
||||
@ -510,45 +568,63 @@ static Htop_Reaction actionHelp(State* st) {
|
||||
int line = 0;
|
||||
|
||||
mvaddstr(line++, 0, "htop " VERSION " - " COPYRIGHT);
|
||||
mvaddstr(line++, 0, "Released under the GNU GPLv2. See 'man' page for more info.");
|
||||
mvaddstr(line++, 0, "Released under the GNU GPLv2+. See 'man' page for more info.");
|
||||
|
||||
attrset(CRT_colors[DEFAULT_COLOR]);
|
||||
line++;
|
||||
mvaddstr(line++, 0, "CPU usage bar: ");
|
||||
|
||||
#define addbartext(attr, prefix, text) \
|
||||
do { \
|
||||
addattrstr(CRT_colors[DEFAULT_COLOR], prefix); \
|
||||
addattrstr(attr, text); \
|
||||
} while(0)
|
||||
|
||||
addattrstr(CRT_colors[BAR_BORDER], "[");
|
||||
if (settings->detailedCPUTime) {
|
||||
addattrstr(CRT_colors[CPU_NICE_TEXT], "low"); addstr("/");
|
||||
addattrstr(CRT_colors[CPU_NORMAL], "normal"); addstr("/");
|
||||
addattrstr(CRT_colors[CPU_SYSTEM], "kernel"); addstr("/");
|
||||
addattrstr(CRT_colors[CPU_IRQ], "irq"); addstr("/");
|
||||
addattrstr(CRT_colors[CPU_SOFTIRQ], "soft-irq"); addstr("/");
|
||||
addattrstr(CRT_colors[CPU_STEAL], "steal"); addstr("/");
|
||||
addattrstr(CRT_colors[CPU_GUEST], "guest"); addstr("/");
|
||||
addattrstr(CRT_colors[CPU_IOWAIT], "io-wait");
|
||||
addattrstr(CRT_colors[BAR_SHADOW], " used%");
|
||||
addbartext(CRT_colors[CPU_NICE_TEXT], "", "low");
|
||||
addbartext(CRT_colors[CPU_NORMAL], "/", "normal");
|
||||
addbartext(CRT_colors[CPU_SYSTEM], "/", "kernel");
|
||||
if (st->settings->detailedCPUTime) {
|
||||
addbartext(CRT_colors[CPU_IRQ], "/", "irq");
|
||||
addbartext(CRT_colors[CPU_SOFTIRQ], "/", "soft-irq");
|
||||
addbartext(CRT_colors[CPU_STEAL], "/", "steal");
|
||||
addbartext(CRT_colors[CPU_GUEST], "/", "guest");
|
||||
addbartext(CRT_colors[CPU_IOWAIT], "/", "io-wait");
|
||||
addbartext(CRT_colors[BAR_SHADOW], " ", "used%");
|
||||
} else {
|
||||
addattrstr(CRT_colors[CPU_NICE_TEXT], "low-priority"); addstr("/");
|
||||
addattrstr(CRT_colors[CPU_NORMAL], "normal"); addstr("/");
|
||||
addattrstr(CRT_colors[CPU_SYSTEM], "kernel"); addstr("/");
|
||||
addattrstr(CRT_colors[CPU_GUEST], "virtualiz");
|
||||
addattrstr(CRT_colors[BAR_SHADOW], " used%");
|
||||
addbartext(CRT_colors[CPU_GUEST], "/", "guest");
|
||||
addbartext(CRT_colors[BAR_SHADOW], " ", "used%");
|
||||
}
|
||||
addattrstr(CRT_colors[BAR_BORDER], "]");
|
||||
|
||||
attrset(CRT_colors[DEFAULT_COLOR]);
|
||||
mvaddstr(line++, 0, "Memory bar: ");
|
||||
addattrstr(CRT_colors[BAR_BORDER], "[");
|
||||
addattrstr(CRT_colors[MEMORY_USED], "used"); addstr("/");
|
||||
addattrstr(CRT_colors[MEMORY_BUFFERS_TEXT], "buffers"); addstr("/");
|
||||
addattrstr(CRT_colors[MEMORY_CACHE], "cache");
|
||||
addattrstr(CRT_colors[BAR_SHADOW], " used/total");
|
||||
addbartext(CRT_colors[MEMORY_USED], "", "used");
|
||||
addbartext(CRT_colors[MEMORY_BUFFERS_TEXT], "/", "buffers");
|
||||
addbartext(CRT_colors[MEMORY_SHARED], "/", "shared");
|
||||
addbartext(CRT_colors[MEMORY_CACHE], "/", "cache");
|
||||
addbartext(CRT_colors[BAR_SHADOW], " ", "used");
|
||||
addbartext(CRT_colors[BAR_SHADOW], "/", "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");
|
||||
addattrstr(CRT_colors[BAR_SHADOW], " used/total");
|
||||
addbartext(CRT_colors[SWAP], "", "used");
|
||||
#ifdef HTOP_LINUX
|
||||
addbartext(CRT_colors[SWAP_CACHE], "/", "cache");
|
||||
#else
|
||||
addbartext(CRT_colors[SWAP_CACHE], " ", "");
|
||||
#endif
|
||||
addbartext(CRT_colors[BAR_SHADOW], " ", "used");
|
||||
addbartext(CRT_colors[BAR_SHADOW], "/", "total");
|
||||
addattrstr(CRT_colors[BAR_BORDER], "]");
|
||||
|
||||
line++;
|
||||
|
||||
#undef addbartext
|
||||
|
||||
attrset(CRT_colors[DEFAULT_COLOR]);
|
||||
mvaddstr(line++, 0, "Type and layout of header meters are configurable in the setup screen.");
|
||||
if (CRT_colorScheme == COLORSCHEME_MONOCHROME) {
|
||||
@ -556,31 +632,47 @@ static Htop_Reaction actionHelp(State* st) {
|
||||
}
|
||||
line++;
|
||||
|
||||
mvaddstr(line++, 0, "Process state: R: running; S: sleeping; T: traced/stopped; Z: zombie; D: disk sleep");
|
||||
#define addattrstatestr(attr, state, desc) \
|
||||
do { \
|
||||
addattrstr(attr, state); \
|
||||
addattrstr(CRT_colors[DEFAULT_COLOR], ": " desc); \
|
||||
} while(0)
|
||||
|
||||
line++;
|
||||
mvaddstr(line, 0, "Process state: ");
|
||||
addattrstatestr(CRT_colors[PROCESS_RUN_STATE], "R", "running; ");
|
||||
addattrstatestr(CRT_colors[PROCESS_SHADOW], "S", "sleeping; ");
|
||||
addattrstatestr(CRT_colors[PROCESS_RUN_STATE], "t", "traced/stopped; ");
|
||||
addattrstatestr(CRT_colors[PROCESS_D_STATE], "Z", "zombie; ");
|
||||
addattrstatestr(CRT_colors[PROCESS_D_STATE], "D", "disk sleep");
|
||||
attrset(CRT_colors[DEFAULT_COLOR]);
|
||||
|
||||
#undef addattrstatestr
|
||||
|
||||
line += 2;
|
||||
|
||||
const bool readonly = Settings_isReadonly();
|
||||
|
||||
int item;
|
||||
for (item = 0; helpLeft[item].key; item++) {
|
||||
attrset(CRT_colors[DEFAULT_COLOR]);
|
||||
mvaddstr(line + item, 9, helpLeft[item].info);
|
||||
attrset(CRT_colors[HELP_BOLD]);
|
||||
mvaddstr(line + item, 0, helpLeft[item].key);
|
||||
attrset((helpLeft[item].roInactive && readonly) ? CRT_colors[HELP_SHADOW] : CRT_colors[DEFAULT_COLOR]);
|
||||
mvaddstr(line + item, 10, helpLeft[item].info);
|
||||
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]);
|
||||
mvaddstr(line + item, 32, "threads");
|
||||
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]);
|
||||
mvaddstr(line + item, 26, "threads");
|
||||
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]);
|
||||
mvaddstr(line + item, 40, helpRight[item].key);
|
||||
attrset(CRT_colors[DEFAULT_COLOR]);
|
||||
mvaddstr(line + item, 49, helpRight[item].info);
|
||||
attrset((helpRight[item].roInactive && readonly) ? CRT_colors[HELP_SHADOW] : CRT_colors[HELP_BOLD]);
|
||||
mvaddstr(line + item, 41, helpRight[item].key);
|
||||
attrset((helpRight[item].roInactive && readonly) ? CRT_colors[HELP_SHADOW] : CRT_colors[DEFAULT_COLOR]);
|
||||
mvaddstr(line + item, 50, helpRight[item].info);
|
||||
}
|
||||
line += MAXIMUM(leftHelpItems, item);
|
||||
line++;
|
||||
@ -592,28 +684,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;
|
||||
|
||||
@ -626,7 +718,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;
|
||||
|
||||
@ -640,6 +732,7 @@ static Htop_Reaction actionShowCommandScreen(State* st) {
|
||||
|
||||
void Action_setBindings(Htop_Action* keys) {
|
||||
keys[' '] = actionTag;
|
||||
keys['*'] = actionExpandOrCollapseAllBranches;
|
||||
keys['+'] = actionExpandOrCollapse;
|
||||
keys[','] = actionSetSortColumn;
|
||||
keys['-'] = actionExpandOrCollapse;
|
||||
@ -655,7 +748,7 @@ void Action_setBindings(Htop_Action* keys) {
|
||||
keys['I'] = actionInvertSortOrder;
|
||||
keys['K'] = actionToggleKernelThreads;
|
||||
keys['M'] = actionSortByMemory;
|
||||
keys['N'] = actionIncPrev;
|
||||
keys['N'] = actionSortByPID;
|
||||
keys['P'] = actionSortByCPU;
|
||||
keys['S'] = actionSetup;
|
||||
keys['T'] = actionSortByTime;
|
||||
@ -673,7 +766,6 @@ void Action_setBindings(Htop_Action* keys) {
|
||||
keys['k'] = actionKill;
|
||||
keys['l'] = actionLsof;
|
||||
keys['m'] = actionToggleMergedCommand;
|
||||
keys['n'] = actionIncNext;
|
||||
keys['p'] = actionToggleProgramPath;
|
||||
keys['q'] = actionQuit;
|
||||
keys['s'] = actionStrace;
|
||||
@ -693,5 +785,6 @@ void Action_setBindings(Htop_Action* keys) {
|
||||
keys[KEY_F(10)] = actionQuit;
|
||||
keys[KEY_F(18)] = actionExpandCollapseOrSortColumn;
|
||||
keys[KEY_RECLICK] = actionExpandOrCollapse;
|
||||
keys[KEY_RESIZE] = actionResize;
|
||||
keys[KEY_SHIFT_TAB] = actionPrevScreen;
|
||||
keys['\t'] = actionNextScreen;
|
||||
}
|
||||
|
18
Action.h
18
Action.h
@ -3,7 +3,7 @@
|
||||
/*
|
||||
htop - Action.h
|
||||
(C) 2015 Hisham H. Muhammad
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
@ -20,27 +20,35 @@ 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_RECALCULATE = 0x02 | 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_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;
|
||||
} State;
|
||||
|
||||
static inline bool State_hideFunctionBar(const State* st) {
|
||||
return st->settings->hideFunctionBar == 2 || (st->settings->hideFunctionBar == 1 && st->hideProcessSelection);
|
||||
}
|
||||
|
||||
typedef Htop_Reaction (*Htop_Action)(State* st);
|
||||
|
||||
Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess);
|
||||
@ -49,6 +57,8 @@ bool Action_setUserOnly(const char* userName, uid_t* userId);
|
||||
|
||||
Htop_Reaction Action_setSortKey(Settings* settings, ProcessField sortKey);
|
||||
|
||||
Htop_Reaction Action_setScreenTab(Settings* settings, int x);
|
||||
|
||||
Htop_Reaction Action_follow(State* st);
|
||||
|
||||
void Action_setBindings(Htop_Action* keys);
|
||||
|
34
Affinity.c
34
Affinity.c
@ -2,7 +2,7 @@
|
||||
htop - Affinity.c
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
(C) 2020 Red Hat, Inc. All Rights Reserved.
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
@ -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,32 +40,32 @@ 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 {
|
||||
unsigned int id;
|
||||
hwloc_bitmap_foreach_begin(id, cpuset);
|
||||
Affinity_add(affinity, id);
|
||||
int id;
|
||||
hwloc_bitmap_foreach_begin(id, cpuset)
|
||||
Affinity_add(affinity, (unsigned)id);
|
||||
hwloc_bitmap_foreach_end();
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
|
22
Affinity.h
22
Affinity.h
@ -4,7 +4,7 @@
|
||||
htop - Affinity.h
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
(C) 2020 Red Hat, Inc. All Rights Reserved.
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
@ -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
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
htop - AffinityPanel.c
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
@ -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;
|
||||
hwloc_bitmap_foreach_begin(i, this->workCpuset)
|
||||
Affinity_add(affinity, i);
|
||||
Affinity_add(affinity, (unsigned)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);
|
||||
}
|
||||
|
@ -3,17 +3,18 @@
|
||||
/*
|
||||
htop - AffinityPanel.h
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
#include "Panel.h"
|
||||
#include "Affinity.h"
|
||||
#include "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);
|
||||
|
||||
|
@ -1,21 +1,23 @@
|
||||
/*
|
||||
htop - AvailableColumnsPanel.c
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
#include "AvailableColumnsPanel.h"
|
||||
|
||||
#include <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,9 +54,8 @@ 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));
|
||||
AvailableColumnsPanel_insert(this, at, selected->key);
|
||||
Panel_setSelected(this->columns, at + 1);
|
||||
ColumnsPanel_update(this->columns);
|
||||
result = HANDLED;
|
||||
@ -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;
|
||||
}
|
||||
|
@ -3,12 +3,14 @@
|
||||
/*
|
||||
htop - AvailableColumnsPanel.h
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
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
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
htop - AvailableMetersPanel.c
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
@ -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;
|
||||
@ -75,9 +79,11 @@ static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) {
|
||||
}
|
||||
if (update) {
|
||||
this->settings->changed = true;
|
||||
this->settings->lastUpdate++;
|
||||
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 +96,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 +148,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;
|
||||
}
|
||||
|
@ -3,28 +3,32 @@
|
||||
/*
|
||||
htop - AvailableMetersPanel.h
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
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
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
htop - BatteryMeter.c
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
|
||||
This meter written by Ian P. Hands (iphands@gmail.com, ihands@redhat.com).
|
||||
@ -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 = {
|
||||
|
@ -3,7 +3,7 @@
|
||||
/*
|
||||
htop - BatteryMeter.h
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
|
||||
This meter written by Ian P. Hands (iphands@gmail.com, ihands@redhat.com).
|
||||
@ -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
|
||||
-----------
|
||||
|
46
COPYING
46
COPYING
@ -1,8 +1,8 @@
|
||||
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.
|
||||
|
||||
@ -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,7 +55,7 @@ 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
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
@ -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
|
||||
@ -278,8 +278,8 @@ 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
|
||||
|
||||
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.
|
||||
|
156
CPUMeter.c
156
CPUMeter.c
@ -1,7 +1,7 @@
|
||||
/*
|
||||
htop - CPUMeter.c
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
@ -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, length, "%s %u", Meter_uiName(this), this->param);
|
||||
else
|
||||
xSnprintf(buffer, 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,7 +379,9 @@ const MeterClass AllCPUs2Meter_class = {
|
||||
.delete = Meter_delete,
|
||||
.display = CPUMeter_display
|
||||
},
|
||||
.updateValues = AllCPUsMeter_updateValues,
|
||||
.defaultMode = CUSTOM_METERMODE,
|
||||
.isMultiColumn = true,
|
||||
.total = 100.0,
|
||||
.attributes = CPUMeter_attributes,
|
||||
.name = "AllCPUs2",
|
||||
@ -370,7 +400,9 @@ const MeterClass LeftCPUsMeter_class = {
|
||||
.delete = Meter_delete,
|
||||
.display = CPUMeter_display
|
||||
},
|
||||
.updateValues = AllCPUsMeter_updateValues,
|
||||
.defaultMode = CUSTOM_METERMODE,
|
||||
.isMultiColumn = true,
|
||||
.total = 100.0,
|
||||
.attributes = CPUMeter_attributes,
|
||||
.name = "LeftCPUs",
|
||||
@ -389,7 +421,9 @@ const MeterClass RightCPUsMeter_class = {
|
||||
.delete = Meter_delete,
|
||||
.display = CPUMeter_display
|
||||
},
|
||||
.updateValues = AllCPUsMeter_updateValues,
|
||||
.defaultMode = CUSTOM_METERMODE,
|
||||
.isMultiColumn = true,
|
||||
.total = 100.0,
|
||||
.attributes = CPUMeter_attributes,
|
||||
.name = "RightCPUs",
|
||||
@ -408,7 +442,9 @@ const MeterClass LeftCPUs2Meter_class = {
|
||||
.delete = Meter_delete,
|
||||
.display = CPUMeter_display
|
||||
},
|
||||
.updateValues = AllCPUsMeter_updateValues,
|
||||
.defaultMode = CUSTOM_METERMODE,
|
||||
.isMultiColumn = true,
|
||||
.total = 100.0,
|
||||
.attributes = CPUMeter_attributes,
|
||||
.name = "LeftCPUs2",
|
||||
@ -427,7 +463,9 @@ const MeterClass RightCPUs2Meter_class = {
|
||||
.delete = Meter_delete,
|
||||
.display = CPUMeter_display
|
||||
},
|
||||
.updateValues = AllCPUsMeter_updateValues,
|
||||
.defaultMode = CUSTOM_METERMODE,
|
||||
.isMultiColumn = true,
|
||||
.total = 100.0,
|
||||
.attributes = CPUMeter_attributes,
|
||||
.name = "RightCPUs2",
|
||||
@ -446,7 +484,9 @@ const MeterClass AllCPUs4Meter_class = {
|
||||
.delete = Meter_delete,
|
||||
.display = CPUMeter_display
|
||||
},
|
||||
.updateValues = AllCPUsMeter_updateValues,
|
||||
.defaultMode = CUSTOM_METERMODE,
|
||||
.isMultiColumn = true,
|
||||
.total = 100.0,
|
||||
.attributes = CPUMeter_attributes,
|
||||
.name = "AllCPUs4",
|
||||
@ -465,7 +505,9 @@ const MeterClass LeftCPUs4Meter_class = {
|
||||
.delete = Meter_delete,
|
||||
.display = CPUMeter_display
|
||||
},
|
||||
.updateValues = AllCPUsMeter_updateValues,
|
||||
.defaultMode = CUSTOM_METERMODE,
|
||||
.isMultiColumn = true,
|
||||
.total = 100.0,
|
||||
.attributes = CPUMeter_attributes,
|
||||
.name = "LeftCPUs4",
|
||||
@ -484,7 +526,9 @@ const MeterClass RightCPUs4Meter_class = {
|
||||
.delete = Meter_delete,
|
||||
.display = CPUMeter_display
|
||||
},
|
||||
.updateValues = AllCPUsMeter_updateValues,
|
||||
.defaultMode = CUSTOM_METERMODE,
|
||||
.isMultiColumn = true,
|
||||
.total = 100.0,
|
||||
.attributes = CPUMeter_attributes,
|
||||
.name = "RightCPUs4",
|
||||
@ -503,7 +547,9 @@ const MeterClass AllCPUs8Meter_class = {
|
||||
.delete = Meter_delete,
|
||||
.display = CPUMeter_display
|
||||
},
|
||||
.updateValues = AllCPUsMeter_updateValues,
|
||||
.defaultMode = CUSTOM_METERMODE,
|
||||
.isMultiColumn = true,
|
||||
.total = 100.0,
|
||||
.attributes = CPUMeter_attributes,
|
||||
.name = "AllCPUs8",
|
||||
@ -522,7 +568,9 @@ const MeterClass LeftCPUs8Meter_class = {
|
||||
.delete = Meter_delete,
|
||||
.display = CPUMeter_display
|
||||
},
|
||||
.updateValues = AllCPUsMeter_updateValues,
|
||||
.defaultMode = CUSTOM_METERMODE,
|
||||
.isMultiColumn = true,
|
||||
.total = 100.0,
|
||||
.attributes = CPUMeter_attributes,
|
||||
.name = "LeftCPUs8",
|
||||
@ -541,7 +589,9 @@ const MeterClass RightCPUs8Meter_class = {
|
||||
.delete = Meter_delete,
|
||||
.display = CPUMeter_display
|
||||
},
|
||||
.updateValues = AllCPUsMeter_updateValues,
|
||||
.defaultMode = CUSTOM_METERMODE,
|
||||
.isMultiColumn = true,
|
||||
.total = 100.0,
|
||||
.attributes = CPUMeter_attributes,
|
||||
.name = "RightCPUs8",
|
||||
|
@ -3,12 +3,13 @@
|
||||
/*
|
||||
htop - CPUMeter.h
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
#include "Meter.h"
|
||||
|
||||
|
||||
typedef enum {
|
||||
CPU_METER_NICE = 0,
|
||||
CPU_METER_NORMAL = 1,
|
||||
|
74
CRT.h
74
CRT.h
@ -3,7 +3,7 @@
|
||||
/*
|
||||
htop - CRT.h
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
@ -13,19 +13,23 @@ in the source distribution for its full text.
|
||||
|
||||
#include "Macros.h"
|
||||
#include "ProvideCurses.h"
|
||||
#include "Settings.h"
|
||||
|
||||
|
||||
typedef enum TreeStr_ {
|
||||
typedef enum TreeStr_
|
||||
{
|
||||
TREE_STR_VERT,
|
||||
TREE_STR_RTEE,
|
||||
TREE_STR_BEND,
|
||||
TREE_STR_TEND,
|
||||
TREE_STR_OPEN,
|
||||
TREE_STR_SHUT,
|
||||
TREE_STR_ASC,
|
||||
TREE_STR_DESC,
|
||||
LAST_TREE_STR
|
||||
} TreeStr;
|
||||
|
||||
typedef enum ColorScheme_ {
|
||||
typedef enum ColorScheme_
|
||||
{
|
||||
COLORSCHEME_DEFAULT,
|
||||
COLORSCHEME_MONOCHROME,
|
||||
COLORSCHEME_BLACKONWHITE,
|
||||
@ -36,7 +40,8 @@ typedef enum ColorScheme_ {
|
||||
LAST_COLORSCHEME
|
||||
} ColorScheme;
|
||||
|
||||
typedef enum ColorElements_ {
|
||||
typedef enum ColorElements_
|
||||
{
|
||||
RESET_COLOR,
|
||||
DEFAULT_COLOR,
|
||||
FUNCTION_BAR,
|
||||
@ -50,6 +55,7 @@ typedef enum ColorElements_ {
|
||||
PANEL_SELECTION_FOLLOW,
|
||||
PANEL_SELECTION_UNFOCUS,
|
||||
LARGE_NUMBER,
|
||||
METER_SHADOW,
|
||||
METER_TEXT,
|
||||
METER_VALUE,
|
||||
METER_VALUE_ERROR,
|
||||
@ -57,18 +63,22 @@ typedef enum ColorElements_ {
|
||||
METER_VALUE_IOWRITE,
|
||||
METER_VALUE_NOTICE,
|
||||
METER_VALUE_OK,
|
||||
METER_VALUE_WARN,
|
||||
LED_COLOR,
|
||||
UPTIME,
|
||||
TEMP,
|
||||
FREQ,
|
||||
BATTERY,
|
||||
TASKS_RUNNING,
|
||||
SWAP,
|
||||
SWAP_CACHE,
|
||||
PROCESS,
|
||||
PROCESS_SHADOW,
|
||||
PROCESS_TAG,
|
||||
PROCESS_MEGABYTES,
|
||||
PROCESS_GIGABYTES,
|
||||
PROCESS_TREE,
|
||||
PROCESS_R_STATE,
|
||||
PROCESS_RUN_STATE,
|
||||
PROCESS_D_STATE,
|
||||
PROCESS_BASENAME,
|
||||
PROCESS_HIGH_PRIORITY,
|
||||
@ -87,6 +97,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,
|
||||
@ -98,6 +113,7 @@ typedef enum ColorElements_ {
|
||||
DATE,
|
||||
DATETIME,
|
||||
HELP_BOLD,
|
||||
HELP_SHADOW,
|
||||
HOSTNAME,
|
||||
CPU_NICE,
|
||||
CPU_NICE_TEXT,
|
||||
@ -108,6 +124,11 @@ typedef enum ColorElements_ {
|
||||
CPU_SOFTIRQ,
|
||||
CPU_STEAL,
|
||||
CPU_GUEST,
|
||||
PANEL_EDIT,
|
||||
SCREENS_OTH_BORDER,
|
||||
SCREENS_OTH_TEXT,
|
||||
SCREENS_CUR_BORDER,
|
||||
SCREENS_CUR_TEXT,
|
||||
PRESSURE_STALL_TEN,
|
||||
PRESSURE_STALL_SIXTY,
|
||||
PRESSURE_STALL_THREEHUNDRED,
|
||||
@ -119,16 +140,33 @@ 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;
|
||||
|
||||
void CRT_fatalError(const char *note) ATTR_NORETURN;
|
||||
|
||||
#ifdef NDEBUG
|
||||
#define CRT_debug(...)
|
||||
#else
|
||||
void CRT_debug_impl(const char *file, size_t lineno, const char *func, const char *fmt, ...) ATTR_FORMAT(printf, 4, 5);
|
||||
#define CRT_debug(...) CRT_debug_impl(__FILE__, __LINE__, __func__, __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
void CRT_handleSIGSEGV(int signal) ATTR_NORETURN;
|
||||
|
||||
#define KEY_WHEELUP KEY_F(20)
|
||||
#define KEY_WHEELDOWN KEY_F(21)
|
||||
#define KEY_RECLICK KEY_F(22)
|
||||
#define KEY_WHEELUP KEY_F(30)
|
||||
#define KEY_WHEELDOWN KEY_F(31)
|
||||
#define KEY_RECLICK KEY_F(32)
|
||||
#define KEY_SHIFT_TAB KEY_F(33)
|
||||
#define KEY_ALT(x) (KEY_F(64 - 26) + ((x) - 'A'))
|
||||
|
||||
extern const char *CRT_degreeSign;
|
||||
@ -151,24 +189,14 @@ extern int CRT_scrollWheelVAmount;
|
||||
|
||||
extern ColorScheme CRT_colorScheme;
|
||||
|
||||
#ifdef HAVE_SETUID_ENABLED
|
||||
void CRT_setMouse(bool 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);
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
htop - CategoriesPanel.c
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
@ -14,14 +14,19 @@ in the source distribution for its full text.
|
||||
#include "AvailableColumnsPanel.h"
|
||||
#include "AvailableMetersPanel.h"
|
||||
#include "ColorsPanel.h"
|
||||
#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 "ScreensPanel.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,38 @@ 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);
|
||||
static void CategoriesPanel_makeScreensPage(CategoriesPanel* this) {
|
||||
Panel* screens = (Panel*) ScreensPanel_new(this->settings);
|
||||
Panel* columns = (Panel*) ((ScreensPanel*)screens)->columns;
|
||||
Panel* availableColumns = (Panel*) AvailableColumnsPanel_new(columns, this->settings->dynamicColumns);
|
||||
ScreenManager_add(this->scr, screens, 20);
|
||||
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 = "Screens", .ctor = CategoriesPanel_makeScreensPage },
|
||||
{ .name = "Colors", .ctor = CategoriesPanel_makeColorsPage },
|
||||
};
|
||||
|
||||
static HandlerResult CategoriesPanel_eventHandler(Panel* super, int ch) {
|
||||
CategoriesPanel* this = (CategoriesPanel*) super;
|
||||
|
||||
@ -98,19 +134,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;
|
||||
@ -134,10 +159,11 @@ CategoriesPanel* CategoriesPanel_new(ScreenManager* scr, Settings* settings, Hea
|
||||
this->settings = settings;
|
||||
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));
|
||||
Panel_setHeader(super, "Categories");
|
||||
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;
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
/*
|
||||
htop - CategoriesPanel.h
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
@ -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);
|
||||
|
262
ChangeLog
262
ChangeLog
@ -1,3 +1,265 @@
|
||||
What's new in version 3.2.1
|
||||
|
||||
* Fix setting to show all branches collapsed by default
|
||||
* Restore functionality of stripExeFromCmdline setting
|
||||
* Fix some command line display settings not being honored without restart
|
||||
* Display single digit precision for CPU% greater than 99.9%
|
||||
* On Linux, FreeBSD and PCP consider only shrinkable ZFS ARC as cache
|
||||
* On Linux, increase field width of CPUD% and SWAPD% columns
|
||||
* Colorize process state characters in help screen
|
||||
* Use mousemask(3X) to enable and disable mouse control
|
||||
* Fix heap buffer overflow in Vector_compact
|
||||
* On Solaris, fix a process time scaling error
|
||||
* On Solaris, fix the build
|
||||
* On NetBSD, OpenBSD and Solaris ensure env buffer size is sufficient
|
||||
* On Linux, resolve processes exiting interfering with sampling
|
||||
* Fix ProcessList quadratic removal when scanning processes
|
||||
* Under LXC, limit CPU count to that given by /proc/cpuinfo
|
||||
* Improve container detection for LXC
|
||||
* Some minor documentation fixes
|
||||
|
||||
What's new in version 3.2.0
|
||||
|
||||
* Support for displaying multiple tabs in the user interface
|
||||
* Allow multiple filter and search terms (logical OR, separate by "|")
|
||||
* Set correct default sorting direction (defaultSortDesc)
|
||||
* Improve performance for process lookup and update
|
||||
* Rework the IOMeters initial display
|
||||
* Removed duplicate sections on COMM and EXE
|
||||
* Highlight process UNINTERRUPTIBLE_WAIT state (D)
|
||||
* Show only integer value when CPU% more than 99.9%
|
||||
* Handle rounding ambiguity between 99.9 and 100.0%
|
||||
* No longer leaves empty the last column in header
|
||||
* Fix header layout and meters reset if a header column is empty
|
||||
* Fix PID and UID column widths off-by-one error
|
||||
* On Linux, read generic sysfs batteries
|
||||
* On Linux, do not collect LRS per thread (it is process-wide)
|
||||
* On Linux, dynamically adjust the SECATTR and CGROUP column widths
|
||||
* On Linux, fix a crash in LXD
|
||||
* On FreeBSD, add support for showing process emulation
|
||||
* On Darwin, lazily set process TTY name
|
||||
* Always set SIGCHLD to default handling
|
||||
* Avoid zombie processes on signal races
|
||||
* Ensure last line is cleared when SIGINT is received
|
||||
* Instead of SIGTERM, pre-select the last sent signal
|
||||
* Internal Hashtable performance and sizing improvements
|
||||
* Add heuristics for guessing LXC or Docker from /proc/1/mounts
|
||||
* Force elapsed time display to zero if process started in the future
|
||||
* Avoid extremely large year values when printing time
|
||||
* Fix division by zero when calculating IO rates
|
||||
* Fix out of boundary writes in XUtils
|
||||
* Fix custom thread name display issue
|
||||
* Use AC_CANONICAL_HOST, not AC_CANONICAL_TARGET in configure.ac
|
||||
* Support libunwind of LLVM
|
||||
|
||||
What's new in version 3.1.2
|
||||
|
||||
* Bugfix for crash when storing modified settings at exit
|
||||
* Generate xz-compressed source tarball (with configure) using github actions
|
||||
* Allow -u UID with numerical value as argument
|
||||
* Added documentation for obsolete/state libraries/program files highlighting
|
||||
* Some obsolete/stale library highlighting refinements
|
||||
* Column width issues resolved
|
||||
* Dynamic UID column sizing improved
|
||||
* Discard stale information from Disk and Network I/O meters
|
||||
* Refined Linux kernel thread detection
|
||||
* Reworked process state handling
|
||||
* New CCGROUP column showing abbreviated cgroup name
|
||||
* New OFFSET column in the list of open files screen
|
||||
|
||||
What's new in version 3.1.1
|
||||
|
||||
* Update license headers to explicitly say GPLv2+
|
||||
* Document minimum version for libcap (thanks to James Brown)
|
||||
* Fix mouse wheel collision with autogroups nice adjustment
|
||||
* Adjust Makefile.am macro definitions for older automake versions
|
||||
* Ensure consistent reporting of MemoryMeter 'used' memory
|
||||
* Report hugepage memory as real and used memory (as before)
|
||||
* Handle procExeDeleted, usesDeletedLib without mergedCommandline mode
|
||||
* Validate meter configuration before proceeding beyond htoprc parsing
|
||||
* Properly release memory on partially read configuration
|
||||
* Handle interrupted sampling from within libpcp PDU transfers
|
||||
* On Linux, provide O_PATH value if not defined
|
||||
* On Linux, always compute procExeDeleted if already set
|
||||
* Workaround for Rosetta 2 on Darwin (thanks to Alexander Momchilov)
|
||||
* Fix FreeBSD cmdline memory leak in Process_updateCmdline, and
|
||||
* Plug a Disk I/O meter memory leak on FreeBSD (thanks to Ximalas)
|
||||
|
||||
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 a version
|
||||
identifying the minimum version of the configuration parser needed to
|
||||
fully understand the configuration file format.
|
||||
Old configuration file formats are automatically upgraded when
|
||||
saving the config file (htoprc).
|
||||
* 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
|
||||
* BUGFIX: Improve white text in the Light Terminal colour scheme
|
||||
(both of the above thanks to V)
|
||||
* Enable the function bar on the main screen to be hidden (see Setup -> Display options)
|
||||
* BUGFIX: Reduce layout issues esp. around printing wide characters (not complete yet)
|
||||
* BUGFIX: Make the follow function exit cleanly after followed process died
|
||||
* Solaris: make Process callbacks static
|
||||
* Update help and man page for improved -t / -s options
|
||||
* Drop usage of formatted error messages from <err.h>
|
||||
* Show arrow indicating order of sorted process column
|
||||
* Lots of plumbing around the internal Hashtable, hardening and code cleanups
|
||||
* LibSensors: add support for Ryzen CPUs
|
||||
(thanks to Matej Dian)
|
||||
* BUGFIX: Fix CPU percentage on M1 silicon Macs
|
||||
(thanks to Luke Groeninger)
|
||||
* LoadMeter: dynamically adjust color and total of bar
|
||||
* Find libsensors.so.4 for Fedora and friends
|
||||
* Add support to display CPU frequencies on Solarish platforms
|
||||
(thanks to Dominik Hassler)
|
||||
* Enable going back to previous search matches (Shift-F3)
|
||||
* Added keybind 'N' for sorting by PID (drops 'n'/'N' as not used before much)
|
||||
(thanks to Jake Mannens)
|
||||
|
||||
What's new in version 3.0.4
|
||||
|
||||
* Separate tree and list sort orders
|
||||
|
13
ClockMeter.c
13
ClockMeter.c
@ -1,7 +1,7 @@
|
||||
/*
|
||||
htop - ClockMeter.c
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
@ -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 = {
|
||||
|
@ -3,12 +3,13 @@
|
||||
/*
|
||||
htop - ClockMeter.h
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
#include "Meter.h"
|
||||
|
||||
|
||||
extern const MeterClass ClockMeter_class;
|
||||
|
||||
#endif
|
||||
|
@ -1,23 +1,22 @@
|
||||
/*
|
||||
htop - ColorsPanel.c
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
#include "ColorsPanel.h"
|
||||
|
||||
#include <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++)
|
||||
@ -68,6 +68,7 @@ static HandlerResult ColorsPanel_eventHandler(Panel* super, int ch) {
|
||||
|
||||
this->settings->colorScheme = mark;
|
||||
this->settings->changed = true;
|
||||
this->settings->lastUpdate++;
|
||||
|
||||
CRT_setColors(mark);
|
||||
clear();
|
||||
@ -86,14 +87,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);
|
||||
|
||||
|
@ -3,23 +3,22 @@
|
||||
/*
|
||||
htop - ColorsPanel.h
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
#include "Panel.h"
|
||||
#include "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
|
||||
|
@ -1,17 +1,20 @@
|
||||
/*
|
||||
htop - ColumnsPanel.c
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
#include "ColumnsPanel.h"
|
||||
|
||||
#include <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,35 +118,61 @@ const PanelClass ColumnsPanel_class = {
|
||||
.eventHandler = ColumnsPanel_eventHandler
|
||||
};
|
||||
|
||||
ColumnsPanel* ColumnsPanel_new(Settings* settings) {
|
||||
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));
|
||||
}
|
||||
|
||||
void ColumnsPanel_fill(ColumnsPanel* this, ScreenSettings* ss, Hashtable* columns) {
|
||||
Panel* super = (Panel*) this;
|
||||
Panel_prune(super);
|
||||
for (const ProcessField* fields = ss->fields; *fields; fields++)
|
||||
ColumnsPanel_add(super, *fields, columns);
|
||||
this->ss = ss;
|
||||
}
|
||||
|
||||
ColumnsPanel* ColumnsPanel_new(ScreenSettings* ss, Hashtable* columns, bool* changed) {
|
||||
ColumnsPanel* this = AllocThis(ColumnsPanel);
|
||||
Panel* super = (Panel*) this;
|
||||
FunctionBar* fuBar = FunctionBar_new(ColumnsFunctions, NULL, NULL);
|
||||
Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar);
|
||||
|
||||
this->settings = settings;
|
||||
this->ss = ss;
|
||||
this->changed = changed;
|
||||
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));
|
||||
}
|
||||
}
|
||||
ColumnsPanel_fill(this, ss, columns);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
void ColumnsPanel_update(Panel* super) {
|
||||
ColumnsPanel* this = (ColumnsPanel*) super;
|
||||
int size = Panel_size(super);
|
||||
this->settings->changed = true;
|
||||
this->settings->fields = xRealloc(this->settings->fields, sizeof(ProcessField) * (size + 1));
|
||||
this->settings->flags = 0;
|
||||
*(this->changed) = true;
|
||||
this->ss->fields = xRealloc(this->ss->fields, sizeof(ProcessField) * (size + 1));
|
||||
this->ss->flags = 0;
|
||||
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;
|
||||
this->ss->fields[i] = key;
|
||||
if (key < LAST_PROCESSFIELD)
|
||||
this->ss->flags |= Process_fields[key].flags;
|
||||
}
|
||||
this->settings->fields[size] = 0;
|
||||
this->ss->fields[size] = 0;
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
/*
|
||||
htop - ColumnsPanel.h
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
@ -12,16 +12,20 @@ in the source distribution for its full text.
|
||||
#include "Panel.h"
|
||||
#include "Settings.h"
|
||||
|
||||
|
||||
typedef struct ColumnsPanel_ {
|
||||
Panel super;
|
||||
ScreenSettings* ss;
|
||||
bool* changed;
|
||||
|
||||
Settings* settings;
|
||||
bool moving;
|
||||
} ColumnsPanel;
|
||||
|
||||
extern const PanelClass ColumnsPanel_class;
|
||||
|
||||
ColumnsPanel* ColumnsPanel_new(Settings* settings);
|
||||
ColumnsPanel* ColumnsPanel_new(ScreenSettings* ss, Hashtable* columns, bool* changed);
|
||||
|
||||
void ColumnsPanel_fill(ColumnsPanel* this, ScreenSettings* ss, Hashtable* columns);
|
||||
|
||||
void ColumnsPanel_update(Panel* super);
|
||||
|
||||
|
409
CommandLine.c
Normal file
409
CommandLine.c
Normal file
@ -0,0 +1,409 @@
|
||||
/*
|
||||
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 <ctype.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", name);
|
||||
#ifdef HAVE_GETMOUSE
|
||||
printf("-M --no-mouse Disable the mouse\n");
|
||||
#endif
|
||||
printf("-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");
|
||||
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 CommandLineStatus parseArguments(const char* program, int argc, char** argv, CommandLineSettings* flags) {
|
||||
|
||||
*flags = (CommandLineSettings) {
|
||||
.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);
|
||||
return STATUS_OK_EXIT;
|
||||
case 'V':
|
||||
printVersionFlag(program);
|
||||
return STATUS_OK_EXIT;
|
||||
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);
|
||||
}
|
||||
return STATUS_OK_EXIT;
|
||||
}
|
||||
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);
|
||||
return STATUS_ERROR_EXIT;
|
||||
}
|
||||
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);
|
||||
return STATUS_ERROR_EXIT;
|
||||
}
|
||||
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))) {
|
||||
for (const char *itr = username; *itr; ++itr)
|
||||
if (!isdigit((unsigned char)*itr)) {
|
||||
fprintf(stderr, "Error: invalid user \"%s\".\n", username);
|
||||
return STATUS_ERROR_EXIT;
|
||||
}
|
||||
flags->userId = atol(username);
|
||||
}
|
||||
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);
|
||||
return STATUS_ERROR_EXIT;
|
||||
}
|
||||
}
|
||||
flags->highlightChanges = true;
|
||||
break;
|
||||
}
|
||||
case 128:
|
||||
flags->readonly = true;
|
||||
break;
|
||||
|
||||
default: {
|
||||
CommandLineStatus status;
|
||||
if ((status = Platform_getLongOption(opt, argc, argv)) != STATUS_OK)
|
||||
return status;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return STATUS_OK;
|
||||
}
|
||||
|
||||
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, "");
|
||||
|
||||
CommandLineStatus status = STATUS_OK;
|
||||
CommandLineSettings flags = { 0 };
|
||||
|
||||
if ((status = parseArguments(name, argc, argv, &flags)) != STATUS_OK)
|
||||
return status != STATUS_OK_EXIT ? 1 : 0;
|
||||
|
||||
if (flags.readonly)
|
||||
Settings_enableReadonly();
|
||||
|
||||
if (!Platform_init())
|
||||
return 1;
|
||||
|
||||
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->ss->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->ss->treeView = false;
|
||||
}
|
||||
ScreenSettings_setSortKey(settings->ss, flags.sortKey);
|
||||
}
|
||||
|
||||
CRT_init(settings, flags.allowUnicode);
|
||||
|
||||
MainPanel* panel = MainPanel_new();
|
||||
ProcessList_setPanel(pl, (Panel*) panel);
|
||||
|
||||
MainPanel_updateLabels(panel, settings->ss->treeView, flags.commFilter);
|
||||
|
||||
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->ss->allBranchesCollapsed)
|
||||
ProcessList_collapseAllBranches(pl);
|
||||
|
||||
ScreenManager_run(scr, NULL, NULL, NULL);
|
||||
|
||||
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);
|
||||
DynamicColumns_delete(dc);
|
||||
DynamicMeters_delete(dm);
|
||||
|
||||
return 0;
|
||||
}
|
19
CommandLine.h
Normal file
19
CommandLine.h
Normal file
@ -0,0 +1,19 @@
|
||||
#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.
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
STATUS_OK,
|
||||
STATUS_ERROR_EXIT,
|
||||
STATUS_OK_EXIT
|
||||
} CommandLineStatus;
|
||||
|
||||
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) {
|
||||
@ -60,7 +60,7 @@ const InfoScreenClass CommandScreen_class = {
|
||||
|
||||
CommandScreen* CommandScreen_new(Process* process) {
|
||||
CommandScreen* this = AllocThis(CommandScreen);
|
||||
return (CommandScreen*) InfoScreen_init(&this->super, process, NULL, LINES - 3, " ");
|
||||
return (CommandScreen*) InfoScreen_init(&this->super, process, NULL, LINES - 2, " ");
|
||||
}
|
||||
|
||||
void CommandScreen_delete(Object* this) {
|
||||
|
38
Compat.c
38
Compat.c
@ -1,7 +1,7 @@
|
||||
/*
|
||||
htop - Compat.c
|
||||
(C) 2020 htop dev team
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
@ -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
|
||||
|
||||
}
|
||||
|
4
Compat.h
4
Compat.h
@ -3,7 +3,7 @@
|
||||
/*
|
||||
htop - Compat.h
|
||||
(C) 2020 htop dev team
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
@ -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 */
|
||||
|
16
DateMeter.c
16
DateMeter.c
@ -1,7 +1,7 @@
|
||||
/*
|
||||
htop - DateMeter.c
|
||||
(C) 2004-2020 Hisham H. Muhammad, Michael Schönitzer
|
||||
Released under the GNU GPL, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
@ -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 = {
|
||||
|
@ -3,12 +3,13 @@
|
||||
/*
|
||||
htop - DateMeter.h
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
Released under the GNU GPL, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
#include "Meter.h"
|
||||
|
||||
|
||||
extern const MeterClass DateMeter_class;
|
||||
|
||||
#endif
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
htop - DateTimeMeter.c
|
||||
(C) 2004-2020 Hisham H. Muhammad, Michael Schönitzer
|
||||
Released under the GNU GPL, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
@ -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 = {
|
||||
|
@ -3,12 +3,13 @@
|
||||
/*
|
||||
htop - DateTimeMeter.h
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
Released under the GNU GPL, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
#include "Meter.h"
|
||||
|
||||
|
||||
extern const MeterClass DateTimeMeter_class;
|
||||
|
||||
#endif
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
htop - DiskIOMeter.c
|
||||
(C) 2020 htop dev team
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
@ -9,12 +9,13 @@ 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 "Meter.h"
|
||||
#include "Object.h"
|
||||
#include "Platform.h"
|
||||
#include "ProcessList.h"
|
||||
#include "RichString.h"
|
||||
#include "XUtils.h"
|
||||
|
||||
@ -25,78 +26,108 @@ static const int DiskIOMeter_attributes[] = {
|
||||
METER_VALUE_IOWRITE,
|
||||
};
|
||||
|
||||
static bool hasData = false;
|
||||
static unsigned long int cached_read_diff = 0;
|
||||
static unsigned long int cached_write_diff = 0;
|
||||
static double cached_utilisation_diff = 0.0;
|
||||
static MeterRateStatus status = RATESTATUS_INIT;
|
||||
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 */
|
||||
/* update only every 500ms to have a sane span for rate calculation */
|
||||
if (passedTimeInMs > 500) {
|
||||
static unsigned long int cached_read_total = 0;
|
||||
static unsigned long int cached_write_total = 0;
|
||||
static unsigned long int cached_msTimeSpend_total = 0;
|
||||
|
||||
cached_last_update = timeInMilliSeconds;
|
||||
static uint64_t cached_read_total;
|
||||
static uint64_t cached_write_total;
|
||||
static uint64_t cached_msTimeSpend_total;
|
||||
uint64_t diff;
|
||||
|
||||
DiskIOData data;
|
||||
if (!Platform_getDiskIO(&data)) {
|
||||
status = RATESTATUS_NODATA;
|
||||
} else if (cached_last_update == 0) {
|
||||
status = RATESTATUS_INIT;
|
||||
} else if (passedTimeInMs > 30000) {
|
||||
status = RATESTATUS_STALE;
|
||||
} else {
|
||||
status = RATESTATUS_DATA;
|
||||
}
|
||||
|
||||
hasData = Platform_getDiskIO(&data);
|
||||
if (!hasData) {
|
||||
this->values[0] = 0;
|
||||
xSnprintf(buffer, len, "no data");
|
||||
cached_last_update = pl->realtimeMs;
|
||||
|
||||
if (status == RATESTATUS_NODATA) {
|
||||
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;
|
||||
}
|
||||
cached_msTimeSpend_total = data.totalMsTimeSpend;
|
||||
}
|
||||
|
||||
if (status == RATESTATUS_INIT) {
|
||||
xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "init");
|
||||
return;
|
||||
}
|
||||
if (status == RATESTATUS_STALE) {
|
||||
xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "stale");
|
||||
return;
|
||||
}
|
||||
|
||||
this->values[0] = cached_utilisation_diff;
|
||||
this->total = MAXIMUM(this->values[0], 100.0); /* fix total after (initial) spike */
|
||||
|
||||
char bufferRead[12], bufferWrite[12];
|
||||
Meter_humanUnit(bufferRead, cached_read_diff, sizeof(bufferRead));
|
||||
Meter_humanUnit(bufferWrite, cached_write_diff, sizeof(bufferWrite));
|
||||
snprintf(buffer, len, "%sB %sB %.1f%%", bufferRead, bufferWrite, cached_utilisation_diff);
|
||||
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) {
|
||||
if (!hasData) {
|
||||
static void DiskIOMeter_display(ATTR_UNUSED const Object* cast, RichString* out) {
|
||||
switch (status) {
|
||||
case RATESTATUS_NODATA:
|
||||
RichString_writeAscii(out, CRT_colors[METER_VALUE_ERROR], "no data");
|
||||
return;
|
||||
case RATESTATUS_INIT:
|
||||
RichString_writeAscii(out, CRT_colors[METER_VALUE], "initializing...");
|
||||
return;
|
||||
case RATESTATUS_STALE:
|
||||
RichString_writeAscii(out, CRT_colors[METER_VALUE_WARN], "stale data");
|
||||
return;
|
||||
case RATESTATUS_DATA:
|
||||
break;
|
||||
}
|
||||
|
||||
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 +142,7 @@ const MeterClass DiskIOMeter_class = {
|
||||
.super = {
|
||||
.extends = Class(Meter),
|
||||
.delete = Meter_delete,
|
||||
.display = DIskIOMeter_display
|
||||
.display = DiskIOMeter_display
|
||||
},
|
||||
.updateValues = DiskIOMeter_updateValues,
|
||||
.defaultMode = TEXT_METERMODE,
|
||||
|
@ -3,16 +3,17 @@
|
||||
/*
|
||||
htop - DiskIOMeter.h
|
||||
(C) 2020 htop dev team
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
#include "Meter.h"
|
||||
|
||||
|
||||
typedef struct DiskIOData_ {
|
||||
unsigned long int totalBytesRead;
|
||||
unsigned long int totalBytesWritten;
|
||||
unsigned long int totalMsTimeSpend;
|
||||
uint64_t totalBytesRead;
|
||||
uint64_t totalBytesWritten;
|
||||
uint64_t totalMsTimeSpend;
|
||||
} DiskIOData;
|
||||
|
||||
extern const MeterClass DiskIOMeter_class;
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
htop - DisplayOptionsPanel.c
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
@ -18,6 +18,7 @@ in the source distribution for its full text.
|
||||
#include "Object.h"
|
||||
#include "OptionItem.h"
|
||||
#include "ProvideCurses.h"
|
||||
#include "ScreensPanel.h"
|
||||
|
||||
|
||||
static const char* const DisplayOptionsFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL};
|
||||
@ -43,6 +44,8 @@ static HandlerResult DisplayOptionsPanel_eventHandler(Panel* super, int ch) {
|
||||
case KEY_RECLICK:
|
||||
case ' ':
|
||||
switch (OptionItem_kind(selected)) {
|
||||
case OPTION_ITEM_TEXT:
|
||||
break;
|
||||
case OPTION_ITEM_CHECK:
|
||||
CheckItem_toggle((CheckItem*)selected);
|
||||
result = HANDLED;
|
||||
@ -69,11 +72,13 @@ static HandlerResult DisplayOptionsPanel_eventHandler(Panel* super, int ch) {
|
||||
|
||||
if (result == HANDLED) {
|
||||
this->settings->changed = true;
|
||||
this->settings->lastUpdate++;
|
||||
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;
|
||||
}
|
||||
@ -96,8 +101,18 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager*
|
||||
this->scr = scr;
|
||||
|
||||
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)));
|
||||
|
||||
#define TABMSG "For current screen tab: \0"
|
||||
char tabheader[sizeof(TABMSG) + SCREEN_NAME_LEN + 1] = TABMSG;
|
||||
strncat(tabheader, settings->ss->name, SCREEN_NAME_LEN);
|
||||
Panel_add(super, (Object*) TextItem_new(tabheader));
|
||||
#undef TABMSG
|
||||
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("Tree view", &(settings->ss->treeView)));
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("- Tree view is always sorted by PID (htop 2 behavior)", &(settings->ss->treeViewAlwaysByPID)));
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("- Tree view is collapsed by default", &(settings->ss->allBranchesCollapsed)));
|
||||
Panel_add(super, (Object*) TextItem_new("Global options:"));
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("Show tabs for screens", &(settings->screenTabs)));
|
||||
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 +120,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 (red) / libraries (yellow)", &(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 +132,25 @@ 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("Hide main function bar (0 - off, 1 - on ESC until next input, 2 - permanently)", &(settings->hideFunctionBar), 0, 0, 2));
|
||||
#ifdef HAVE_LIBHWLOC
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("Show topology when selecting affinity by default", &(settings->topologyAffinity)));
|
||||
#endif
|
||||
|
@ -3,7 +3,7 @@
|
||||
/*
|
||||
htop - DisplayOptionsPanel.h
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
@ -11,6 +11,7 @@ in the source distribution for its full text.
|
||||
#include "ScreenManager.h"
|
||||
#include "Settings.h"
|
||||
|
||||
|
||||
typedef struct DisplayOptionsPanel_ {
|
||||
Panel super;
|
||||
|
||||
|
66
DynamicColumn.c
Normal file
66
DynamicColumn.c
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
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();
|
||||
}
|
||||
|
||||
void DynamicColumns_delete(Hashtable* dynamics) {
|
||||
if (dynamics) {
|
||||
Platform_dynamicColumnsDone(dynamics);
|
||||
Hashtable_delete(dynamics);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
34
DynamicColumn.h
Normal file
34
DynamicColumn.h
Normal file
@ -0,0 +1,34 @@
|
||||
#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);
|
||||
|
||||
void DynamicColumns_delete(Hashtable* dynamics);
|
||||
|
||||
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
|
131
DynamicMeter.c
Normal file
131
DynamicMeter.c
Normal file
@ -0,0 +1,131 @@
|
||||
/*
|
||||
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();
|
||||
}
|
||||
|
||||
void DynamicMeters_delete(Hashtable* dynamics) {
|
||||
if (dynamics) {
|
||||
Platform_dynamicMetersDone(dynamics);
|
||||
Hashtable_delete(dynamics);
|
||||
}
|
||||
}
|
||||
|
||||
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 = "",
|
||||
};
|
28
DynamicMeter.h
Normal file
28
DynamicMeter.h
Normal file
@ -0,0 +1,28 @@
|
||||
#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);
|
||||
|
||||
void DynamicMeters_delete(Hashtable* dynamics);
|
||||
|
||||
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"
|
||||
@ -17,7 +16,7 @@
|
||||
EnvScreen* EnvScreen_new(Process* process) {
|
||||
EnvScreen* this = xMalloc(sizeof(EnvScreen));
|
||||
Object_setClass(this, Class(EnvScreen));
|
||||
return (EnvScreen*) InfoScreen_init(&this->super, process, NULL, LINES - 3, " ");
|
||||
return (EnvScreen*) InfoScreen_init(&this->super, process, NULL, LINES - 2, " ");
|
||||
}
|
||||
|
||||
void EnvScreen_delete(Object* this) {
|
||||
@ -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;
|
||||
|
29
FreqMeter.c
Normal file
29
FreqMeter.c
Normal file
@ -0,0 +1,29 @@
|
||||
#include "FreqMeter.h"
|
||||
|
||||
#include "CRT.h"
|
||||
#include "Object.h"
|
||||
#include "Platform.h"
|
||||
#include "XUtils.h"
|
||||
|
||||
static const int FreqMeter_attributes[] = {
|
||||
FREQ};
|
||||
|
||||
static void FreqMeter_updateValues(Meter *this)
|
||||
{
|
||||
float freq = Platform_getFreq();
|
||||
|
||||
xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%.1lf GHz", freq);
|
||||
}
|
||||
|
||||
const MeterClass FreqMeter_class = {
|
||||
.super = {
|
||||
.extends = Class(Meter),
|
||||
.delete = Meter_delete},
|
||||
.updateValues = FreqMeter_updateValues,
|
||||
.defaultMode = TEXT_METERMODE,
|
||||
.maxItems = 1,
|
||||
.total = 100.0,
|
||||
.attributes = FreqMeter_attributes,
|
||||
.name = "Freq",
|
||||
.uiName = "CPU Frequency",
|
||||
.caption = "CPU/Frequency: "};
|
8
FreqMeter.h
Normal file
8
FreqMeter.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef HEADER_FreqMeter
|
||||
#define HEADER_FreqMeter
|
||||
|
||||
#include "Meter.h"
|
||||
|
||||
extern const MeterClass FreqMeter_class;
|
||||
|
||||
#endif
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
htop - FunctionBar.c
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
@ -88,11 +88,12 @@ void FunctionBar_setLabel(FunctionBar* this, int event, const char* text) {
|
||||
}
|
||||
}
|
||||
|
||||
void FunctionBar_draw(const FunctionBar* this) {
|
||||
FunctionBar_drawExtra(this, NULL, -1, false);
|
||||
int FunctionBar_draw(const FunctionBar* this) {
|
||||
return FunctionBar_drawExtra(this, NULL, -1, false);
|
||||
}
|
||||
|
||||
void FunctionBar_drawExtra(const FunctionBar* this, const char* buffer, int attr, bool setCursor) {
|
||||
int FunctionBar_drawExtra(const FunctionBar* this, const char* buffer, int attr, bool setCursor) {
|
||||
int cursorX = 0;
|
||||
attrset(CRT_colors[FUNCTION_BAR]);
|
||||
mvhline(LINES - 1, 0, ' ', COLS);
|
||||
int x = 0;
|
||||
@ -113,18 +114,20 @@ void FunctionBar_drawExtra(const FunctionBar* this, const char* buffer, int attr
|
||||
}
|
||||
mvaddstr(LINES - 1, x, buffer);
|
||||
x += strlen(buffer);
|
||||
cursorX = x;
|
||||
}
|
||||
|
||||
attrset(CRT_colors[RESET_COLOR]);
|
||||
|
||||
if (setCursor) {
|
||||
CRT_cursorX = x;
|
||||
curs_set(1);
|
||||
} else {
|
||||
curs_set(0);
|
||||
}
|
||||
|
||||
currentLen = x;
|
||||
|
||||
return cursorX;
|
||||
}
|
||||
|
||||
void FunctionBar_append(const char* buffer, int attr) {
|
||||
|
@ -3,12 +3,13 @@
|
||||
/*
|
||||
htop - FunctionBar.h
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
|
||||
typedef struct FunctionBar_ {
|
||||
int size;
|
||||
char** functions;
|
||||
@ -28,9 +29,9 @@ void FunctionBar_delete(FunctionBar* this);
|
||||
|
||||
void FunctionBar_setLabel(FunctionBar* this, int event, const char* text);
|
||||
|
||||
void FunctionBar_draw(const FunctionBar* this);
|
||||
int FunctionBar_draw(const FunctionBar* this);
|
||||
|
||||
void FunctionBar_drawExtra(const FunctionBar* this, const char* buffer, int attr, bool setCursor);
|
||||
int FunctionBar_drawExtra(const FunctionBar* this, const char* buffer, int attr, bool setCursor);
|
||||
|
||||
void FunctionBar_append(const char* buffer, int attr);
|
||||
|
||||
|
111
Hashtable.c
111
Hashtable.c
@ -1,7 +1,7 @@
|
||||
/*
|
||||
htop - Hashtable.c
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
@ -11,44 +11,62 @@ in the source distribution for its full text.
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "CRT.h"
|
||||
#include "Macros.h"
|
||||
#include "XUtils.h"
|
||||
|
||||
#ifndef NDEBUG
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct HashtableItem_ {
|
||||
ht_key_t key;
|
||||
size_t probe;
|
||||
void* value;
|
||||
} HashtableItem;
|
||||
|
||||
struct Hashtable_ {
|
||||
size_t size;
|
||||
HashtableItem* buckets;
|
||||
size_t items;
|
||||
bool owner;
|
||||
};
|
||||
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
||||
static void Hashtable_dump(const Hashtable* this) {
|
||||
fprintf(stderr, "Hashtable %p: size=%u items=%u owner=%s\n",
|
||||
fprintf(stderr, "Hashtable %p: size=%zu items=%zu owner=%s\n",
|
||||
(const void*)this,
|
||||
this->size,
|
||||
this->items,
|
||||
this->owner ? "yes" : "no");
|
||||
|
||||
unsigned int items = 0;
|
||||
for (unsigned int i = 0; i < this->size; i++) {
|
||||
fprintf(stderr, " item %5u: key = %5u probe = %2u value = %p\n",
|
||||
size_t items = 0;
|
||||
for (size_t i = 0; i < this->size; i++) {
|
||||
fprintf(stderr, " item %5zu: key = %5u probe = %2zu value = %p\n",
|
||||
i,
|
||||
this->buckets[i].key,
|
||||
this->buckets[i].probe,
|
||||
this->buckets[i].value ? (const void*)this->buckets[i].value : "(nil)");
|
||||
this->buckets[i].value);
|
||||
|
||||
if (this->buckets[i].value)
|
||||
items++;
|
||||
}
|
||||
|
||||
fprintf(stderr, "Hashtable %p: items=%u counted=%u\n",
|
||||
fprintf(stderr, "Hashtable %p: items=%zu counted=%zu\n",
|
||||
(const void*)this,
|
||||
this->items,
|
||||
items);
|
||||
}
|
||||
|
||||
static bool Hashtable_isConsistent(const Hashtable* this) {
|
||||
unsigned int items = 0;
|
||||
for (unsigned int i = 0; i < this->size; i++) {
|
||||
size_t items = 0;
|
||||
for (size_t i = 0; i < this->size; i++) {
|
||||
if (this->buckets[i].value)
|
||||
items++;
|
||||
}
|
||||
@ -58,9 +76,9 @@ static bool Hashtable_isConsistent(const Hashtable* this) {
|
||||
return res;
|
||||
}
|
||||
|
||||
unsigned int Hashtable_count(const Hashtable* this) {
|
||||
unsigned int items = 0;
|
||||
for (unsigned int i = 0; i < this->size; i++) {
|
||||
size_t Hashtable_count(const Hashtable* this) {
|
||||
size_t items = 0;
|
||||
for (size_t i = 0; i < this->size; i++) {
|
||||
if (this->buckets[i].value)
|
||||
items++;
|
||||
}
|
||||
@ -72,7 +90,7 @@ unsigned int Hashtable_count(const Hashtable* this) {
|
||||
|
||||
/* https://oeis.org/A014234 */
|
||||
static const uint64_t OEISprimes[] = {
|
||||
2, 3, 7, 13, 31, 61, 127, 251, 509, 1021, 2039, 4093, 8191,
|
||||
7, 13, 31, 61, 127, 251, 509, 1021, 2039, 4093, 8191,
|
||||
16381, 32749, 65521, 131071, 262139, 524287, 1048573,
|
||||
2097143, 4194301, 8388593, 16777213, 33554393,
|
||||
67108859, 134217689, 268435399, 536870909, 1073741789,
|
||||
@ -80,18 +98,17 @@ static const uint64_t OEISprimes[] = {
|
||||
34359738337, 68719476731, 137438953447
|
||||
};
|
||||
|
||||
static uint64_t nextPrime(unsigned int n) {
|
||||
assert(n <= OEISprimes[ARRAYSIZE(OEISprimes) - 1]);
|
||||
|
||||
for (unsigned int i = 0; i < ARRAYSIZE(OEISprimes); i++) {
|
||||
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])
|
||||
return OEISprimes[i];
|
||||
}
|
||||
|
||||
return OEISprimes[ARRAYSIZE(OEISprimes) - 1];
|
||||
CRT_fatalError("Hashtable: no prime found");
|
||||
}
|
||||
|
||||
Hashtable* Hashtable_new(unsigned int size, bool owner) {
|
||||
Hashtable* Hashtable_new(size_t size, bool owner) {
|
||||
Hashtable* this;
|
||||
|
||||
this = xMalloc(sizeof(Hashtable));
|
||||
@ -115,7 +132,7 @@ void Hashtable_clear(Hashtable* this) {
|
||||
assert(Hashtable_isConsistent(this));
|
||||
|
||||
if (this->owner)
|
||||
for (unsigned int i = 0; i < this->size; i++)
|
||||
for (size_t i = 0; i < this->size; i++)
|
||||
free(this->buckets[i].value);
|
||||
|
||||
memset(this->buckets, 0, this->size * sizeof(HashtableItem));
|
||||
@ -124,11 +141,11 @@ void Hashtable_clear(Hashtable* this) {
|
||||
assert(Hashtable_isConsistent(this));
|
||||
}
|
||||
|
||||
static void insert(Hashtable* this, hkey_t key, void* value) {
|
||||
unsigned int index = key % this->size;
|
||||
unsigned int probe = 0;
|
||||
static void insert(Hashtable* this, ht_key_t key, void* value) {
|
||||
size_t index = key % this->size;
|
||||
size_t probe = 0;
|
||||
#ifndef NDEBUG
|
||||
unsigned int origIndex = index;
|
||||
size_t origIndex = index;
|
||||
#endif
|
||||
|
||||
for (;;) {
|
||||
@ -167,22 +184,26 @@ static void insert(Hashtable* this, hkey_t key, void* value) {
|
||||
}
|
||||
}
|
||||
|
||||
void Hashtable_setSize(Hashtable* this, unsigned int size) {
|
||||
void Hashtable_setSize(Hashtable* this, size_t size) {
|
||||
|
||||
assert(Hashtable_isConsistent(this));
|
||||
|
||||
if (size <= this->items)
|
||||
return;
|
||||
|
||||
HashtableItem* oldBuckets = this->buckets;
|
||||
unsigned int oldSize = this->size;
|
||||
size_t newSize = nextPrime(size);
|
||||
if (newSize == this->size)
|
||||
return;
|
||||
|
||||
this->size = nextPrime(size);
|
||||
HashtableItem* oldBuckets = this->buckets;
|
||||
size_t oldSize = this->size;
|
||||
|
||||
this->size = newSize;
|
||||
this->buckets = (HashtableItem*) xCalloc(this->size, sizeof(HashtableItem));
|
||||
this->items = 0;
|
||||
|
||||
/* rehash */
|
||||
for (unsigned int i = 0; i < oldSize; i++) {
|
||||
for (size_t i = 0; i < oldSize; i++) {
|
||||
if (!oldBuckets[i].value)
|
||||
continue;
|
||||
|
||||
@ -194,15 +215,19 @@ void Hashtable_setSize(Hashtable* this, unsigned int size) {
|
||||
assert(Hashtable_isConsistent(this));
|
||||
}
|
||||
|
||||
void Hashtable_put(Hashtable* this, hkey_t key, void* value) {
|
||||
void Hashtable_put(Hashtable* this, ht_key_t key, void* value) {
|
||||
|
||||
assert(Hashtable_isConsistent(this));
|
||||
assert(this->size > 0);
|
||||
assert(value);
|
||||
|
||||
/* grow on load-factor > 0.7 */
|
||||
if (10 * this->items > 7 * this->size)
|
||||
if (10 * this->items > 7 * this->size) {
|
||||
if (SIZE_MAX / 2 < this->size)
|
||||
CRT_fatalError("Hashtable: size overflow");
|
||||
|
||||
Hashtable_setSize(this, 2 * this->size);
|
||||
}
|
||||
|
||||
insert(this, key, value);
|
||||
|
||||
@ -211,11 +236,11 @@ void Hashtable_put(Hashtable* this, hkey_t key, void* value) {
|
||||
assert(this->size > this->items);
|
||||
}
|
||||
|
||||
void* Hashtable_remove(Hashtable* this, hkey_t key) {
|
||||
unsigned int index = key % this->size;
|
||||
unsigned int probe = 0;
|
||||
void* Hashtable_remove(Hashtable* this, ht_key_t key) {
|
||||
size_t index = key % this->size;
|
||||
size_t probe = 0;
|
||||
#ifndef NDEBUG
|
||||
unsigned int origIndex = index;
|
||||
size_t origIndex = index;
|
||||
#endif
|
||||
|
||||
assert(Hashtable_isConsistent(this));
|
||||
@ -230,7 +255,7 @@ void* Hashtable_remove(Hashtable* this, hkey_t key) {
|
||||
res = this->buckets[index].value;
|
||||
}
|
||||
|
||||
unsigned int next = (index + 1) % this->size;
|
||||
size_t next = (index + 1) % this->size;
|
||||
|
||||
while (this->buckets[next].value && this->buckets[next].probe > 0) {
|
||||
this->buckets[index] = this->buckets[next];
|
||||
@ -261,17 +286,17 @@ void* Hashtable_remove(Hashtable* this, hkey_t key) {
|
||||
|
||||
/* shrink on load-factor < 0.125 */
|
||||
if (8 * this->items < this->size)
|
||||
Hashtable_setSize(this, this->size / 2);
|
||||
Hashtable_setSize(this, this->size / 3); /* account for nextPrime rounding up */
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void* Hashtable_get(Hashtable* this, hkey_t key) {
|
||||
unsigned int index = key % this->size;
|
||||
unsigned int probe = 0;
|
||||
void* Hashtable_get(Hashtable* this, ht_key_t key) {
|
||||
size_t index = key % this->size;
|
||||
size_t probe = 0;
|
||||
void* res = NULL;
|
||||
#ifndef NDEBUG
|
||||
unsigned int origIndex = index;
|
||||
size_t origIndex = index;
|
||||
#endif
|
||||
|
||||
assert(Hashtable_isConsistent(this));
|
||||
@ -296,7 +321,7 @@ void* Hashtable_get(Hashtable* this, hkey_t key) {
|
||||
|
||||
void Hashtable_foreach(Hashtable* this, Hashtable_PairFunction f, void* userData) {
|
||||
assert(Hashtable_isConsistent(this));
|
||||
for (unsigned int i = 0; i < this->size; i++) {
|
||||
for (size_t i = 0; i < this->size; i++) {
|
||||
HashtableItem* walk = &this->buckets[i];
|
||||
if (walk->value)
|
||||
f(walk->key, walk->value, userData);
|
||||
|
32
Hashtable.h
32
Hashtable.h
@ -3,49 +3,39 @@
|
||||
/*
|
||||
htop - Hashtable.h
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
|
||||
typedef unsigned int hkey_t;
|
||||
typedef unsigned int ht_key_t;
|
||||
|
||||
typedef void(*Hashtable_PairFunction)(hkey_t key, void* value, void* userdata);
|
||||
typedef void(*Hashtable_PairFunction)(ht_key_t key, void* value, void* userdata);
|
||||
|
||||
typedef struct HashtableItem_ {
|
||||
hkey_t key;
|
||||
unsigned int probe;
|
||||
void* value;
|
||||
} HashtableItem;
|
||||
|
||||
typedef struct Hashtable_ {
|
||||
unsigned int size;
|
||||
HashtableItem* buckets;
|
||||
unsigned int items;
|
||||
bool owner;
|
||||
} Hashtable;
|
||||
typedef struct Hashtable_ Hashtable;
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
||||
unsigned int Hashtable_count(const Hashtable* this);
|
||||
size_t Hashtable_count(const Hashtable* this);
|
||||
|
||||
#endif /* NDEBUG */
|
||||
|
||||
Hashtable* Hashtable_new(unsigned int size, bool owner);
|
||||
Hashtable* Hashtable_new(size_t size, bool owner);
|
||||
|
||||
void Hashtable_delete(Hashtable* this);
|
||||
|
||||
void Hashtable_clear(Hashtable* this);
|
||||
|
||||
void Hashtable_setSize(Hashtable* this, unsigned int size);
|
||||
void Hashtable_setSize(Hashtable* this, size_t size);
|
||||
|
||||
void Hashtable_put(Hashtable* this, hkey_t key, void* value);
|
||||
void Hashtable_put(Hashtable* this, ht_key_t key, void* value);
|
||||
|
||||
void* Hashtable_remove(Hashtable* this, hkey_t key);
|
||||
void* Hashtable_remove(Hashtable* this, ht_key_t key);
|
||||
|
||||
void* Hashtable_get(Hashtable* this, hkey_t key);
|
||||
void* Hashtable_get(Hashtable* this, ht_key_t key);
|
||||
|
||||
void Hashtable_foreach(Hashtable* this, Hashtable_PairFunction f, void* userData);
|
||||
|
||||
|
256
Header.c
256
Header.c
@ -1,18 +1,23 @@
|
||||
/*
|
||||
htop - Header.c
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
#include "Header.h"
|
||||
|
||||
#include <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,120 @@ void Header_delete(Header* this) {
|
||||
Header_forEachColumn(this, i) {
|
||||
Vector_delete(this->columns[i]);
|
||||
}
|
||||
|
||||
free(this->columns);
|
||||
free(this);
|
||||
}
|
||||
|
||||
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 dynamic[32] = {0};
|
||||
if (sscanf(paren, "(%30s)", dynamic)) { // DynamicMeter
|
||||
char* end;
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
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 + 1, 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 +166,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 +176,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,39 +188,107 @@ 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 numCols = HeaderLayout_getColumns(this->headerLayout);
|
||||
const int width = COLS - 2 * pad - (numCols - 1);
|
||||
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;
|
||||
|
||||
/* Let meters in text mode expand to the right on empty neighbors;
|
||||
except for multi column meters. */
|
||||
if (meter->mode == TEXT_METERMODE && !Meter_isMultiColumn(meter)) {
|
||||
for (int j = 1; j < meter->columnWidthCount; j++) {
|
||||
actualWidth++; /* separator column */
|
||||
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);
|
||||
x++; /* separator column */
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
if (this->settings->screenTabs) {
|
||||
maxHeight++;
|
||||
}
|
||||
this->height = maxHeight;
|
||||
this->pad = pad;
|
||||
return maxHeight;
|
||||
|
24
Header.h
24
Header.h
@ -3,48 +3,46 @@
|
||||
/*
|
||||
htop - Header.h
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
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, H_fEC_numColumns_ = HeaderLayout_getColumns((this_)->headerLayout); (i_) < H_fEC_numColumns_; ++(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
|
||||
|
78
HeaderLayout.h
Normal file
78
HeaderLayout.h
Normal file
@ -0,0 +1,78 @@
|
||||
#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_INVALID = -1,
|
||||
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 */
|
88
HeaderOptionsPanel.c
Normal file
88
HeaderOptionsPanel.c
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
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;
|
||||
this->settings->lastUpdate++;
|
||||
|
||||
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, scr->header->headerLayout), 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 */
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
htop - HostnameMeter.c
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
@ -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 = {
|
||||
|
@ -3,12 +3,13 @@
|
||||
/*
|
||||
htop - HostnameMeter.h
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
#include "Meter.h"
|
||||
|
||||
|
||||
extern const MeterClass HostnameMeter_class;
|
||||
|
||||
#endif
|
||||
|
82
IncSet.c
82
IncSet.c
@ -1,7 +1,7 @@
|
||||
/*
|
||||
htop - IncSet.c
|
||||
(C) 2005-2012 Hisham H. Muhammad
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
@ -29,9 +29,16 @@ void IncSet_reset(IncSet* this, IncType type) {
|
||||
IncMode_reset(&this->modes[type]);
|
||||
}
|
||||
|
||||
static const char* const searchFunctions[] = {"Next ", "Cancel ", " Search: ", NULL};
|
||||
static const char* const searchKeys[] = {"F3", "Esc", " "};
|
||||
static const int searchEvents[] = {KEY_F(3), 27, ERR};
|
||||
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};
|
||||
|
||||
static inline void IncMode_initSearch(IncMode* search) {
|
||||
memset(search, 0, sizeof(IncMode));
|
||||
@ -70,15 +77,15 @@ 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;
|
||||
const char* incFilter = this->modes[INC_FILTER].buffer;
|
||||
for (int i = 0; i < Vector_size(lines); i++) {
|
||||
ListItem* line = (ListItem*)Vector_get(lines, i);
|
||||
if (String_contains_i(line->value, incFilter)) {
|
||||
if (String_contains_i(line->value, incFilter, true)) {
|
||||
Panel_add(panel, (Object*)line);
|
||||
if (selected == (Object*)line) {
|
||||
Panel_setSelected(panel, n);
|
||||
@ -98,10 +105,10 @@ static void updateWeakPanel(IncSet* this, Panel* panel, Vector* lines) {
|
||||
}
|
||||
}
|
||||
|
||||
static bool search(IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelValue) {
|
||||
static bool search(const IncSet* this, 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)) {
|
||||
if (String_contains_i(getPanelValue(panel, i), this->active->buffer, true)) {
|
||||
Panel_setSelected(panel, i);
|
||||
return true;
|
||||
}
|
||||
@ -110,7 +117,22 @@ 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) {
|
||||
void IncSet_activate(IncSet* this, IncType type, Panel* panel) {
|
||||
this->active = &(this->modes[type]);
|
||||
panel->currentBar = this->active->bar;
|
||||
panel->cursorOn = true;
|
||||
this->panel = panel;
|
||||
IncSet_drawBar(this, CRT_colors[FUNCTION_BAR]);
|
||||
}
|
||||
|
||||
static void IncSet_deactivate(IncSet* this, Panel* panel) {
|
||||
this->active = NULL;
|
||||
Panel_setDefaultBar(panel);
|
||||
panel->cursorOn = false;
|
||||
FunctionBar_draw(this->defaultBar);
|
||||
}
|
||||
|
||||
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;
|
||||
@ -126,21 +148,13 @@ static bool IncMode_find(IncMode* mode, Panel* panel, IncMode_GetPanelValue getP
|
||||
return false;
|
||||
}
|
||||
|
||||
if (String_contains_i(getPanelValue(panel, i), mode->buffer)) {
|
||||
if (String_contains_i(getPanelValue(panel, i), mode->buffer, true)) {
|
||||
Panel_setSelected(panel, i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool IncSet_next(IncSet* this, IncType type, Panel* panel, IncMode_GetPanelValue getPanelValue) {
|
||||
return IncMode_find(&this->modes[type], panel, getPanelValue, 1);
|
||||
}
|
||||
|
||||
bool IncSet_prev(IncSet* this, IncType type, Panel* panel, IncMode_GetPanelValue getPanelValue) {
|
||||
return IncMode_find(&this->modes[type], panel, getPanelValue, -1);
|
||||
}
|
||||
|
||||
bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue getPanelValue, Vector* lines) {
|
||||
if (ch == ERR)
|
||||
return true;
|
||||
@ -149,15 +163,15 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue
|
||||
int size = Panel_size(panel);
|
||||
bool filterChanged = false;
|
||||
bool doSearch = true;
|
||||
if (ch == KEY_F(3)) {
|
||||
if (ch == KEY_F(3) || ch == KEY_F(15)) {
|
||||
if (size == 0)
|
||||
return true;
|
||||
|
||||
IncMode_find(mode, panel, getPanelValue, 1);
|
||||
IncMode_find(mode, panel, getPanelValue, ch == KEY_F(3) ? 1 : -1);
|
||||
doSearch = false;
|
||||
} else if (0 < ch && ch < 255 && isprint((unsigned char)ch)) {
|
||||
if (mode->index < INCMODE_MAX) {
|
||||
mode->buffer[mode->index] = ch;
|
||||
mode->buffer[mode->index] = (char) ch;
|
||||
mode->index++;
|
||||
mode->buffer[mode->index] = 0;
|
||||
if (mode->isFilter) {
|
||||
@ -167,7 +181,7 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if ((ch == KEY_BACKSPACE || ch == 127)) {
|
||||
} else if (ch == KEY_BACKSPACE || ch == 127) {
|
||||
if (mode->index > 0) {
|
||||
mode->index--;
|
||||
mode->buffer[mode->index] = 0;
|
||||
@ -182,7 +196,7 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue
|
||||
doSearch = false;
|
||||
}
|
||||
} else if (ch == KEY_RESIZE) {
|
||||
Panel_resize(panel, COLS, LINES - panel->y - 1);
|
||||
doSearch = (mode->index > 0);
|
||||
} else {
|
||||
if (mode->isFilter) {
|
||||
filterChanged = true;
|
||||
@ -195,12 +209,11 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue
|
||||
IncMode_reset(mode);
|
||||
}
|
||||
}
|
||||
this->active = NULL;
|
||||
Panel_setDefaultBar(panel);
|
||||
IncSet_deactivate(this, panel);
|
||||
doSearch = false;
|
||||
}
|
||||
if (doSearch) {
|
||||
this->found = search(mode, panel, getPanelValue);
|
||||
this->found = search(this, panel, getPanelValue);
|
||||
}
|
||||
if (filterChanged && lines) {
|
||||
updateWeakPanel(this, panel, lines);
|
||||
@ -209,18 +222,17 @@ 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 : "";
|
||||
}
|
||||
|
||||
void IncSet_activate(IncSet* this, IncType type, Panel* panel) {
|
||||
this->active = &(this->modes[type]);
|
||||
panel->currentBar = this->active->bar;
|
||||
}
|
||||
|
||||
void IncSet_drawBar(const IncSet* this) {
|
||||
void IncSet_drawBar(const IncSet* this, int attr) {
|
||||
if (this->active) {
|
||||
FunctionBar_drawExtra(this->active->bar, this->active->buffer, (this->active->isFilter || this->found) ? -1 : CRT_colors[FAILED_SEARCH], true);
|
||||
if (!this->active->isFilter && !this->found)
|
||||
attr = CRT_colors[FAILED_SEARCH];
|
||||
int cursorX = FunctionBar_drawExtra(this->active->bar, this->active->buffer, attr, true);
|
||||
this->panel->cursorY = LINES - 1;
|
||||
this->panel->cursorX = cursorX;
|
||||
} else {
|
||||
FunctionBar_draw(this->defaultBar);
|
||||
}
|
||||
|
12
IncSet.h
12
IncSet.h
@ -3,7 +3,7 @@
|
||||
/*
|
||||
htop - IncSet.h
|
||||
(C) 2005-2012 Hisham H. Muhammad
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
@ -14,6 +14,7 @@ in the source distribution for its full text.
|
||||
#include "Panel.h"
|
||||
#include "Vector.h"
|
||||
|
||||
|
||||
#define INCMODE_MAX 40
|
||||
|
||||
typedef enum {
|
||||
@ -31,6 +32,7 @@ typedef struct IncMode_ {
|
||||
typedef struct IncSet_ {
|
||||
IncMode modes[2];
|
||||
IncMode* active;
|
||||
Panel* panel;
|
||||
FunctionBar* defaultBar;
|
||||
bool filtering;
|
||||
bool found;
|
||||
@ -40,6 +42,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);
|
||||
@ -48,17 +52,13 @@ IncSet* IncSet_new(FunctionBar* bar);
|
||||
|
||||
void IncSet_delete(IncSet* this);
|
||||
|
||||
bool IncSet_next(IncSet* this, IncType type, Panel* panel, IncMode_GetPanelValue getPanelValue);
|
||||
|
||||
bool IncSet_prev(IncSet* this, IncType type, Panel* panel, IncMode_GetPanelValue getPanelValue);
|
||||
|
||||
bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue getPanelValue, Vector* lines);
|
||||
|
||||
const char* IncSet_getListItemValue(Panel* panel, int i);
|
||||
|
||||
void IncSet_activate(IncSet* this, IncType type, Panel* panel);
|
||||
|
||||
void IncSet_drawBar(const IncSet* this);
|
||||
void IncSet_drawBar(const IncSet* this, int attr);
|
||||
|
||||
int IncSet_synthesizeEvent(IncSet* this, int x);
|
||||
|
||||
|
46
InfoScreen.c
46
InfoScreen.c
@ -4,7 +4,6 @@
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "CRT.h"
|
||||
@ -26,9 +25,9 @@ InfoScreen* InfoScreen_init(InfoScreen* this, const Process* process, FunctionBa
|
||||
if (!bar) {
|
||||
bar = FunctionBar_new(InfoScreenFunctions, InfoScreenKeys, InfoScreenEvents);
|
||||
}
|
||||
this->display = Panel_new(0, 1, COLS, height, false, Class(ListItem), bar);
|
||||
this->display = Panel_new(0, 1, COLS, height, Class(ListItem), false, bar);
|
||||
this->inc = IncSet_new(bar);
|
||||
this->lines = Vector_new(this->display->items->type, true, DEFAULT_SIZE);
|
||||
this->lines = Vector_new(Vector_type(this->display->items), true, DEFAULT_SIZE);
|
||||
Panel_setHeader(this->display, panelHeader);
|
||||
return this;
|
||||
}
|
||||
@ -54,17 +53,17 @@ void InfoScreen_drawTitled(InfoScreen* this, const char* fmt, ...) {
|
||||
|
||||
attrset(CRT_colors[METER_TEXT]);
|
||||
mvhline(0, 0, ' ', COLS);
|
||||
mvwprintw(stdscr, 0, 0, title);
|
||||
mvaddstr(0, 0, title);
|
||||
attrset(CRT_colors[DEFAULT_COLOR]);
|
||||
Panel_draw(this->display, true, true, true);
|
||||
Panel_draw(this->display, true, true, true, false);
|
||||
|
||||
IncSet_drawBar(this->inc);
|
||||
IncSet_drawBar(this->inc, CRT_colors[FUNCTION_BAR]);
|
||||
}
|
||||
|
||||
void InfoScreen_addLine(InfoScreen* this, const char* line) {
|
||||
Vector_add(this->lines, (Object*) ListItem_new(line, 0));
|
||||
const char* incFilter = IncSet_filter(this->inc);
|
||||
if (!incFilter || String_contains_i(line, incFilter)) {
|
||||
if (!incFilter || String_contains_i(line, incFilter, true)) {
|
||||
Panel_add(this->display, Vector_get(this->lines, Vector_size(this->lines) - 1));
|
||||
}
|
||||
}
|
||||
@ -73,7 +72,7 @@ void InfoScreen_appendLine(InfoScreen* this, const char* line) {
|
||||
ListItem* last = (ListItem*)Vector_get(this->lines, Vector_size(this->lines) - 1);
|
||||
ListItem_append(last, line);
|
||||
const char* incFilter = IncSet_filter(this->inc);
|
||||
if (incFilter && Panel_get(this->display, Panel_size(this->display) - 1) != (Object*)last && String_contains_i(line, incFilter)) {
|
||||
if (incFilter && Panel_get(this->display, Panel_size(this->display) - 1) != (Object*)last && String_contains_i(line, incFilter, true)) {
|
||||
Panel_add(this->display, (Object*)last);
|
||||
}
|
||||
}
|
||||
@ -89,14 +88,10 @@ void InfoScreen_run(InfoScreen* this) {
|
||||
bool looping = true;
|
||||
while (looping) {
|
||||
|
||||
Panel_draw(panel, false, true, true);
|
||||
IncSet_drawBar(this->inc);
|
||||
Panel_draw(panel, false, true, true, false);
|
||||
IncSet_drawBar(this->inc, CRT_colors[FUNCTION_BAR]);
|
||||
|
||||
if (this->inc->active) {
|
||||
(void) move(LINES - 1, CRT_cursorX);
|
||||
}
|
||||
set_escdelay(25);
|
||||
int ch = getch();
|
||||
int ch = Panel_getCh(panel);
|
||||
|
||||
if (ch == ERR) {
|
||||
if (As_InfoScreen(this)->onErr) {
|
||||
@ -105,18 +100,29 @@ void InfoScreen_run(InfoScreen* this) {
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_GETMOUSE
|
||||
if (ch == KEY_MOUSE) {
|
||||
MEVENT mevent;
|
||||
int ok = getmouse(&mevent);
|
||||
if (ok == OK) {
|
||||
if (mevent.bstate & BUTTON1_RELEASED) {
|
||||
if (mevent.y >= panel->y && mevent.y < LINES - 1) {
|
||||
Panel_setSelected(panel, mevent.y - panel->y + panel->scrollV);
|
||||
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 +142,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 +160,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;
|
||||
|
18
ListItem.c
18
ListItem.c
@ -1,7 +1,7 @@
|
||||
/*
|
||||
htop - ListItem.c
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
@ -18,13 +18,13 @@ in the source distribution for its full text.
|
||||
#include "XUtils.h"
|
||||
|
||||
|
||||
static void ListItem_delete(Object* cast) {
|
||||
void ListItem_delete(Object* cast) {
|
||||
ListItem* this = (ListItem*)cast;
|
||||
free(this->value);
|
||||
free(this);
|
||||
}
|
||||
|
||||
static void ListItem_display(const Object* cast, RichString* out) {
|
||||
void ListItem_display(const Object* cast, RichString* out) {
|
||||
const ListItem* const this = (const ListItem*)cast;
|
||||
assert (this != NULL);
|
||||
|
||||
@ -34,17 +34,19 @@ static void ListItem_display(const Object* cast, RichString* out) {
|
||||
CRT_utf8 ? "↕ " :
|
||||
#endif
|
||||
"+ ");
|
||||
} else {
|
||||
RichString_prune(out);
|
||||
}
|
||||
RichString_appendWide(out, CRT_colors[DEFAULT_COLOR], this->value);
|
||||
}
|
||||
|
||||
ListItem* ListItem_new(const char* value, int key) {
|
||||
ListItem* this = AllocThis(ListItem);
|
||||
void ListItem_init(ListItem* this, const char* value, int key) {
|
||||
this->value = xStrdup(value);
|
||||
this->key = key;
|
||||
this->moving = false;
|
||||
}
|
||||
|
||||
ListItem* ListItem_new(const char* value, int key) {
|
||||
ListItem* this = AllocThis(ListItem);
|
||||
ListItem_init(this, value, key);
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -57,7 +59,7 @@ void ListItem_append(ListItem* this, const char* text) {
|
||||
this->value[newLen] = '\0';
|
||||
}
|
||||
|
||||
static long ListItem_compare(const void* cast1, const void* cast2) {
|
||||
int ListItem_compare(const void* cast1, const void* cast2) {
|
||||
const ListItem* obj1 = (const ListItem*) cast1;
|
||||
const ListItem* obj2 = (const ListItem*) cast2;
|
||||
return strcmp(obj1->value, obj2->value);
|
||||
|
11
ListItem.h
11
ListItem.h
@ -3,7 +3,7 @@
|
||||
/*
|
||||
htop - ListItem.h
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
@ -11,6 +11,7 @@ in the source distribution for its full text.
|
||||
|
||||
#include "Object.h"
|
||||
|
||||
|
||||
typedef struct ListItem_ {
|
||||
Object super;
|
||||
char* value;
|
||||
@ -20,10 +21,18 @@ typedef struct ListItem_ {
|
||||
|
||||
extern const ObjectClass ListItem_class;
|
||||
|
||||
void ListItem_delete(Object* cast);
|
||||
|
||||
void ListItem_display(const Object* cast, RichString* out);
|
||||
|
||||
void ListItem_init(ListItem* this, const char* value, int key);
|
||||
|
||||
ListItem* ListItem_new(const char* value, int key);
|
||||
|
||||
void ListItem_append(ListItem* this, const char* text);
|
||||
|
||||
int ListItem_compare(const void* cast1, const void* cast2);
|
||||
|
||||
static inline const char* ListItem_getRef(const ListItem* this) {
|
||||
return this->value;
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
htop - LoadAverageMeter.c
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -24,36 +25,78 @@ static const int LoadMeter_attributes[] = {
|
||||
LOAD
|
||||
};
|
||||
|
||||
static void LoadAverageMeter_updateValues(Meter* this, char* buffer, size_t size) {
|
||||
static const int OK_attributes[] = {
|
||||
METER_VALUE_OK
|
||||
};
|
||||
|
||||
static const int Medium_attributes[] = {
|
||||
METER_VALUE_WARN
|
||||
};
|
||||
|
||||
static const int High_attributes[] = {
|
||||
METER_VALUE_ERROR
|
||||
};
|
||||
|
||||
static void LoadAverageMeter_updateValues(Meter* this) {
|
||||
Platform_getLoadAverage(&this->values[0], &this->values[1], &this->values[2]);
|
||||
xSnprintf(buffer, size, "%.2f/%.2f/%.2f", this->values[0], this->values[1], this->values[2]);
|
||||
|
||||
// only show bar for 1min value
|
||||
this->curItems = 1;
|
||||
|
||||
// change bar color and total based on value
|
||||
if (this->values[0] < 1.0) {
|
||||
this->curAttributes = OK_attributes;
|
||||
this->total = 1.0;
|
||||
} else if (this->values[0] < this->pl->activeCPUs) {
|
||||
this->curAttributes = Medium_attributes;
|
||||
this->total = this->pl->activeCPUs;
|
||||
} else {
|
||||
this->curAttributes = High_attributes;
|
||||
this->total = 2 * this->pl->activeCPUs;
|
||||
}
|
||||
|
||||
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);
|
||||
if (this->values[0] > this->total) {
|
||||
this->total = this->values[0];
|
||||
|
||||
// change bar color and total based on value
|
||||
if (this->values[0] < 1.0) {
|
||||
this->curAttributes = OK_attributes;
|
||||
this->total = 1.0;
|
||||
} else if (this->values[0] < this->pl->activeCPUs) {
|
||||
this->curAttributes = Medium_attributes;
|
||||
this->total = this->pl->activeCPUs;
|
||||
} else {
|
||||
this->curAttributes = High_attributes;
|
||||
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 = {
|
||||
|
@ -3,12 +3,13 @@
|
||||
/*
|
||||
htop - LoadAverageMeter.h
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
#include "Meter.h"
|
||||
|
||||
|
||||
extern const MeterClass LoadAverageMeter_class;
|
||||
|
||||
extern const MeterClass LoadMeter_class;
|
||||
|
25
Macros.h
25
Macros.h
@ -27,12 +27,17 @@
|
||||
#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
|
||||
|
||||
#define ATTR_FORMAT(type, index, check) __attribute__((format (type, index, check)))
|
||||
#define ATTR_NONNULL __attribute__((nonnull))
|
||||
#define ATTR_NORETURN __attribute__((noreturn))
|
||||
#define ATTR_UNUSED __attribute__((unused))
|
||||
#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
|
||||
|
83
MainPanel.c
83
MainPanel.c
@ -2,7 +2,7 @@
|
||||
htop - ColumnsPanel.c
|
||||
(C) 2004-2015 Hisham H. Muhammad
|
||||
(C) 2020 Red Hat, Inc. All Rights Reserved.
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
@ -22,17 +22,19 @@ in the source distribution for its full text.
|
||||
|
||||
|
||||
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) {
|
||||
void MainPanel_updateLabels(MainPanel* this, bool list, bool filter) {
|
||||
FunctionBar* bar = MainPanel_getFunctionBar(this);
|
||||
FunctionBar_setLabel(bar, KEY_F(5), mode ? "List " : "Tree ");
|
||||
FunctionBar_setLabel(bar, KEY_F(5), list ? "List " : "Tree ");
|
||||
FunctionBar_setLabel(bar, KEY_F(4), filter ? "FILTER" : "Filter");
|
||||
}
|
||||
|
||||
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;
|
||||
@ -44,6 +46,11 @@ void MainPanel_pidSearch(MainPanel* this, int ch) {
|
||||
}
|
||||
}
|
||||
|
||||
static const char* MainPanel_getValue(Panel* this, int i) {
|
||||
const Process* p = (const Process*) Panel_get(this, i);
|
||||
return Process_getCommand(p);
|
||||
}
|
||||
|
||||
static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
|
||||
MainPanel* this = (MainPanel*) super;
|
||||
|
||||
@ -51,28 +58,45 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
|
||||
|
||||
Htop_Reaction reaction = HTOP_OK;
|
||||
|
||||
if (ch != ERR && ch != KEY_RESIZE)
|
||||
/* Let supervising ScreenManager handle resize */
|
||||
if (ch == KEY_RESIZE)
|
||||
return IGNORED;
|
||||
|
||||
/* reset on every normal key */
|
||||
bool needReset = ch != ERR;
|
||||
#ifdef HAVE_GETMOUSE
|
||||
/* except mouse events while mouse support is disabled */
|
||||
if (!(ch != KEY_MOUSE || this->state->settings->enableMouse))
|
||||
needReset = false;
|
||||
#endif
|
||||
if (needReset)
|
||||
this->state->hideProcessSelection = false;
|
||||
|
||||
Settings* settings = this->state->settings;
|
||||
ScreenSettings* ss = settings->ss;
|
||||
|
||||
if (EVENT_IS_HEADER_CLICK(ch)) {
|
||||
int x = EVENT_HEADER_CLICK_GET_X(ch);
|
||||
const ProcessList* pl = this->state->pl;
|
||||
Settings* settings = this->state->settings;
|
||||
int hx = super->scrollH + x + 1;
|
||||
ProcessField field = ProcessList_keyAt(pl, hx);
|
||||
if (settings->treeView && settings->treeViewAlwaysByPID) {
|
||||
settings->treeView = false;
|
||||
settings->direction = 1;
|
||||
if (ss->treeView && ss->treeViewAlwaysByPID) {
|
||||
ss->treeView = false;
|
||||
ss->direction = 1;
|
||||
reaction |= Action_setSortKey(settings, field);
|
||||
} else if (field == Settings_getActiveSortKey(settings)) {
|
||||
Settings_invertSortOrder(settings);
|
||||
} else if (field == ScreenSettings_getActiveSortKey(ss)) {
|
||||
ScreenSettings_invertSortOrder(ss);
|
||||
} else {
|
||||
reaction |= Action_setSortKey(settings, field);
|
||||
}
|
||||
reaction |= HTOP_RECALCULATE | HTOP_REDRAW_BAR | HTOP_SAVE_SETTINGS;
|
||||
result = HANDLED;
|
||||
} else if (EVENT_IS_SCREEN_TAB_CLICK(ch)) {
|
||||
int x = EVENT_SCREEN_TAB_GET_X(ch);
|
||||
reaction |= Action_setScreenTab(settings, x);
|
||||
result = HANDLED;
|
||||
} else if (ch != ERR && this->inc->active) {
|
||||
bool filterChanged = IncSet_handleKey(this->inc, ch, super, (IncMode_GetPanelValue) MainPanel_getValue, NULL);
|
||||
bool filterChanged = IncSet_handleKey(this->inc, ch, super, MainPanel_getValue, NULL);
|
||||
if (filterChanged) {
|
||||
this->state->pl->incFilter = IncSet_filter(this->inc);
|
||||
reaction = HTOP_REFRESH | HTOP_REDRAW_BAR;
|
||||
@ -99,10 +123,13 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
|
||||
}
|
||||
|
||||
if (reaction & HTOP_REDRAW_BAR) {
|
||||
MainPanel_updateTreeFunctions(this, this->state->settings->treeView);
|
||||
MainPanel_updateLabels(this, settings->ss->treeView, this->state->pl->incFilter);
|
||||
}
|
||||
if (reaction & HTOP_RESIZE) {
|
||||
result |= RESIZE;
|
||||
}
|
||||
if (reaction & HTOP_UPDATE_PANELHDR) {
|
||||
ProcessList_printHeader(this->state->pl, Panel_getHeader(super));
|
||||
result |= REDRAW;
|
||||
}
|
||||
if (reaction & HTOP_REFRESH) {
|
||||
result |= REFRESH;
|
||||
@ -124,18 +151,13 @@ 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;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char* MainPanel_getValue(MainPanel* this, int i) {
|
||||
Process* p = (Process*) Panel_get((Panel*)this, i);
|
||||
return Process_getCommand(p);
|
||||
}
|
||||
|
||||
bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, Arg arg, bool* wasAnyTagged) {
|
||||
Panel* super = (Panel*) this;
|
||||
bool ok = true;
|
||||
@ -160,26 +182,37 @@ bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, Ar
|
||||
return ok;
|
||||
}
|
||||
|
||||
static void MainPanel_drawFunctionBar(Panel* super) {
|
||||
static void MainPanel_drawFunctionBar(Panel* super, bool hideFunctionBar) {
|
||||
MainPanel* this = (MainPanel*) super;
|
||||
IncSet_drawBar(this->inc);
|
||||
|
||||
// Do not hide active search and filter bar.
|
||||
if (hideFunctionBar && !this->inc->active)
|
||||
return;
|
||||
|
||||
IncSet_drawBar(this->inc, CRT_colors[FUNCTION_BAR]);
|
||||
if (this->state->pauseProcessUpdate) {
|
||||
FunctionBar_append("PAUSED", CRT_colors[PAUSED]);
|
||||
}
|
||||
}
|
||||
|
||||
static void MainPanel_printHeader(Panel* super) {
|
||||
MainPanel* this = (MainPanel*) super;
|
||||
ProcessList_printHeader(this->state->pl, &super->header);
|
||||
}
|
||||
|
||||
const PanelClass MainPanel_class = {
|
||||
.super = {
|
||||
.extends = Class(Panel),
|
||||
.delete = MainPanel_delete
|
||||
},
|
||||
.eventHandler = MainPanel_eventHandler,
|
||||
.drawFunctionBar = MainPanel_drawFunctionBar
|
||||
.drawFunctionBar = MainPanel_drawFunctionBar,
|
||||
.printHeader = MainPanel_printHeader
|
||||
};
|
||||
|
||||
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));
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
htop - ColumnsPanel.h
|
||||
(C) 2004-2015 Hisham H. Muhammad
|
||||
(C) 2020 Red Hat, Inc. All Rights Reserved.
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
@ -32,14 +32,11 @@ typedef bool(*MainPanel_ForeachProcessFn)(Process*, Arg);
|
||||
|
||||
#define MainPanel_getFunctionBar(this_) (((Panel*)(this_))->defaultBar)
|
||||
|
||||
void MainPanel_updateTreeFunctions(MainPanel* this, bool mode);
|
||||
|
||||
void MainPanel_pidSearch(MainPanel* this, int ch);
|
||||
// update the Label Keys in the MainPanel bar, list: list / tree mode, filter: filter (inc) active / inactive
|
||||
void MainPanel_updateLabels(MainPanel* this, bool list, bool filter);
|
||||
|
||||
int MainPanel_selectedPid(MainPanel* this);
|
||||
|
||||
const char* MainPanel_getValue(MainPanel* this, int i);
|
||||
|
||||
bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, Arg arg, bool* wasAnyTagged);
|
||||
|
||||
extern const PanelClass MainPanel_class;
|
||||
|
244
Makefile.am
244
Makefile.am
@ -1,10 +1,22 @@
|
||||
AUTOMAKE_OPTIONS = subdir-objects
|
||||
|
||||
if !HTOP_PCP
|
||||
bin_PROGRAMS = htop
|
||||
myhtopplatprogram = htop.c
|
||||
else
|
||||
bin_PROGRAMS = pcp-htop
|
||||
myhtopplatprogram = pcp-htop.c
|
||||
endif
|
||||
|
||||
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 +24,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 +38,7 @@ myhtopsources = \
|
||||
ClockMeter.c \
|
||||
ColorsPanel.c \
|
||||
ColumnsPanel.c \
|
||||
CommandLine.c \
|
||||
CommandScreen.c \
|
||||
Compat.c \
|
||||
CPUMeter.c \
|
||||
@ -34,18 +47,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 \
|
||||
@ -58,12 +74,16 @@ myhtopsources = \
|
||||
ProcessLocksScreen.c \
|
||||
RichString.c \
|
||||
ScreenManager.c \
|
||||
ScreensPanel.c \
|
||||
Settings.c \
|
||||
SignalsPanel.c \
|
||||
SwapMeter.c \
|
||||
SysArchMeter.c \
|
||||
TasksMeter.c \
|
||||
TraceScreen.c \
|
||||
UptimeMeter.c \
|
||||
FreqMeter.c \
|
||||
TempMeter.c \
|
||||
UsersTable.c \
|
||||
Vector.c \
|
||||
XUtils.c
|
||||
@ -81,16 +101,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 +124,7 @@ myhtopheaders = \
|
||||
Macros.h \
|
||||
MainPanel.h \
|
||||
MemoryMeter.h \
|
||||
MemorySwapMeter.h \
|
||||
Meter.h \
|
||||
MetersPanel.h \
|
||||
NetworkIOMeter.h \
|
||||
@ -112,12 +138,16 @@ myhtopheaders = \
|
||||
ProvideCurses.h \
|
||||
RichString.h \
|
||||
ScreenManager.h \
|
||||
ScreensPanel.h \
|
||||
Settings.h \
|
||||
SignalsPanel.h \
|
||||
SwapMeter.h \
|
||||
SysArchMeter.h \
|
||||
TasksMeter.h \
|
||||
TraceScreen.h \
|
||||
UptimeMeter.h \
|
||||
FreqMeter.h \
|
||||
TempMeter.h \
|
||||
UsersTable.h \
|
||||
Vector.h \
|
||||
XUtils.h
|
||||
@ -126,6 +156,11 @@ myhtopheaders = \
|
||||
# -----
|
||||
|
||||
linux_platform_headers = \
|
||||
generic/gettime.h \
|
||||
generic/hostname.h \
|
||||
generic/uname.h \
|
||||
linux/CGroupUtils.h \
|
||||
linux/HugePageMeter.h \
|
||||
linux/IOPriority.h \
|
||||
linux/IOPriorityPanel.h \
|
||||
linux/LibSensors.h \
|
||||
@ -142,9 +177,12 @@ 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/CGroupUtils.c \
|
||||
linux/HugePageMeter.c \
|
||||
linux/IOPriorityPanel.c \
|
||||
linux/LibSensors.c \
|
||||
linux/LinuxProcess.c \
|
||||
@ -155,10 +193,12 @@ myhtopplatsources = \
|
||||
linux/SystemdMeter.c \
|
||||
linux/ZramMeter.c \
|
||||
zfs/ZfsArcMeter.c \
|
||||
zfs/ZfsArcStats.c \
|
||||
zfs/ZfsCompressedArcMeter.c
|
||||
|
||||
if HTOP_LINUX
|
||||
AM_LDFLAGS += -rdynamic
|
||||
myhtopplatheaders = $(linux_platform_headers)
|
||||
myhtopplatsources = $(linux_platform_sources)
|
||||
endif
|
||||
|
||||
# FreeBSD
|
||||
@ -169,17 +209,28 @@ 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
|
||||
|
||||
myhtopplatheaders = $(freebsd_platform_headers)
|
||||
myhtopplatsources = $(freebsd_platform_sources)
|
||||
endif
|
||||
|
||||
# DragonFlyBSD
|
||||
@ -189,31 +240,72 @@ 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
|
||||
|
||||
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
|
||||
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
|
||||
myhtopplatheaders = $(openbsd_platform_headers)
|
||||
myhtopplatsources = $(openbsd_platform_sources)
|
||||
endif
|
||||
|
||||
# Darwin
|
||||
@ -223,60 +315,124 @@ darwin_platform_headers = \
|
||||
darwin/DarwinProcess.h \
|
||||
darwin/DarwinProcessList.h \
|
||||
darwin/Platform.h \
|
||||
darwin/PlatformHelpers.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/PlatformHelpers.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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
myhtopplatheaders = $(pcp_platform_headers)
|
||||
myhtopplatsources = $(pcp_platform_sources)
|
||||
pcp_htop_SOURCES = $(myhtopplatprogram) $(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
|
||||
myhtopplatsources = $(unsupported_platform_sources)
|
||||
myhtopplatheaders = $(unsupported_platform_headers)
|
||||
endif
|
||||
|
||||
# ----
|
||||
|
||||
htop_SOURCES = $(myhtopheaders) $(myhtopplatheaders) $(myhtopsources) $(myhtopplatsources)
|
||||
htop_SOURCES = $(myhtopplatprogram) $(myhtopheaders) $(myhtopplatheaders) $(myhtopsources) $(myhtopplatsources)
|
||||
nodist_htop_SOURCES = config.h
|
||||
|
||||
target:
|
||||
@ -286,10 +442,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
|
||||
|
@ -1,12 +1,15 @@
|
||||
/*
|
||||
htop - MemoryMeter.c
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
#include "MemoryMeter.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "CRT.h"
|
||||
#include "Object.h"
|
||||
#include "Platform.h"
|
||||
@ -16,13 +19,23 @@ 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);
|
||||
|
||||
/* Do not print available memory in bar mode */
|
||||
this->curItems = 4;
|
||||
|
||||
written = Meter_humanUnit(buffer, this->values[0], size);
|
||||
METER_BUFFER_CHECK(buffer, size, written);
|
||||
|
||||
@ -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);
|
||||
|
||||
/* 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",
|
||||
|
@ -3,12 +3,13 @@
|
||||
/*
|
||||
htop - MemoryMeter.h
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
#include "Meter.h"
|
||||
|
||||
|
||||
extern const MeterClass MemoryMeter_class;
|
||||
|
||||
#endif
|
||||
|
107
MemorySwapMeter.c
Normal file
107
MemorySwapMeter.c
Normal file
@ -0,0 +1,107 @@
|
||||
/*
|
||||
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;
|
||||
|
||||
/* Use the same width for each sub meter to align with CPU meter */
|
||||
const int colwidth = w / 2;
|
||||
const int diff = w - colwidth * 2;
|
||||
|
||||
assert(data->memoryMeter->draw);
|
||||
data->memoryMeter->draw(data->memoryMeter, x, y, colwidth);
|
||||
assert(data->swapMeter->draw);
|
||||
data->swapMeter->draw(data->swapMeter, x + colwidth + diff, y, colwidth);
|
||||
}
|
||||
|
||||
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,
|
||||
.isMultiColumn = true,
|
||||
.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
|
130
Meter.c
130
Meter.c
@ -1,7 +1,7 @@
|
||||
/*
|
||||
htop - Meter.c
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
@ -32,13 +32,14 @@ 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;
|
||||
this->param = param;
|
||||
this->pl = pl;
|
||||
this->curItems = type->maxItems;
|
||||
this->curAttributes = NULL;
|
||||
this->values = type->maxItems ? xCalloc(type->maxItems, sizeof(double)) : NULL;
|
||||
this->total = type->total;
|
||||
this->caption = xStrdup(type->caption);
|
||||
@ -92,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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -131,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;
|
||||
@ -154,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);
|
||||
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);
|
||||
RichString_printoffnVal(out, y, x, 0, w - 1);
|
||||
RichString_end(out);
|
||||
Meter_displayBuffer(this, &out);
|
||||
RichString_printoffnVal(out, y, x, 0, w);
|
||||
RichString_delete(&out);
|
||||
}
|
||||
|
||||
/* ---------- BarMeterMode ---------- */
|
||||
@ -178,21 +175,19 @@ 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));
|
||||
|
||||
w -= 2;
|
||||
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;
|
||||
attrset(CRT_colors[BAR_BORDER]);
|
||||
mvaddch(y, x, '[');
|
||||
w--;
|
||||
mvaddch(y, x + MAXIMUM(w, 0), ']');
|
||||
w--;
|
||||
attrset(CRT_colors[RESET_COLOR]);
|
||||
|
||||
w--;
|
||||
x++;
|
||||
|
||||
if (w < 1)
|
||||
@ -201,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
|
||||
@ -216,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);
|
||||
@ -252,17 +247,18 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
|
||||
// ...then print the buffer.
|
||||
offset = 0;
|
||||
for (uint8_t i = 0; i < this->curItems; i++) {
|
||||
RichString_setAttrn(&bar, CRT_colors[Meter_attributes(this)[i]], startPos + offset, startPos + offset + blockSizes[i] - 1);
|
||||
int attr = this->curAttributes ? this->curAttributes[i] : Meter_attributes(this)[i];
|
||||
RichString_setAttrn(&bar, CRT_colors[attr], startPos + offset, blockSizes[i]);
|
||||
RichString_printoffnVal(bar, y, x + offset, startPos + offset, MINIMUM(blockSizes[i], w - offset));
|
||||
offset += blockSizes[i];
|
||||
offset = CLAMP(offset, 0, w);
|
||||
}
|
||||
if (offset < w) {
|
||||
RichString_setAttrn(&bar, CRT_colors[BAR_SHADOW], startPos + offset, startPos + w - 1);
|
||||
RichString_setAttrn(&bar, CRT_colors[BAR_SHADOW], startPos + offset, w - offset);
|
||||
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]);
|
||||
@ -290,17 +286,17 @@ static const char* const GraphMeterMode_dotsAscii[] = {
|
||||
/*20*/":", /*21*/":", /*22*/":"
|
||||
};
|
||||
|
||||
static const char* const* GraphMeterMode_dots;
|
||||
static int GraphMeterMode_pixPerRow;
|
||||
|
||||
static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
|
||||
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;
|
||||
#ifdef HAVE_LIBNCURSESW
|
||||
if (CRT_utf8) {
|
||||
GraphMeterMode_dots = GraphMeterMode_dotsUtf8;
|
||||
@ -312,41 +308,38 @@ 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;
|
||||
}
|
||||
|
||||
int i = nValues - (w * 2) + 2, k = 0;
|
||||
int i = nValues - (w * 2), k = 0;
|
||||
if (i < 0) {
|
||||
k = -i / 2;
|
||||
i = 0;
|
||||
}
|
||||
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++) {
|
||||
@ -387,8 +380,6 @@ static void LEDMeterMode_drawDigit(int x, int y, int 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;
|
||||
@ -396,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
|
||||
@ -408,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 = {
|
||||
@ -460,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[] = {
|
||||
|
41
Meter.h
41
Meter.h
@ -3,13 +3,14 @@
|
||||
/*
|
||||
htop - Meter.h
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
#include "config.h" // IWYU pragma: keep
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <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;
|
||||
@ -67,6 +73,7 @@ typedef struct MeterClass_ {
|
||||
const char* const caption; /* prefix in the actual header */
|
||||
const char* const description; /* optional meter description in header setup menu */
|
||||
const uint8_t maxItems;
|
||||
const bool isMultiColumn; /* whether the meter draws multiple sub-columns (defaults to false) */
|
||||
} MeterClass;
|
||||
|
||||
#define As_Meter(this_) ((const MeterClass*)((this_)->super.klass))
|
||||
@ -77,16 +84,20 @@ 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
|
||||
#define Meter_uiName(this_) As_Meter(this_)->uiName
|
||||
#define Meter_isMultiColumn(this_) As_Meter(this_)->isMultiColumn
|
||||
|
||||
typedef struct GraphData_ {
|
||||
struct timeval time;
|
||||
double values[METER_BUFFER_LEN];
|
||||
double values[METER_GRAPHDATA_SIZE];
|
||||
} GraphData;
|
||||
|
||||
struct Meter_ {
|
||||
@ -95,11 +106,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;
|
||||
@ -120,9 +134,16 @@ typedef enum {
|
||||
LAST_METERMODE
|
||||
} MeterModeId;
|
||||
|
||||
typedef enum {
|
||||
RATESTATUS_DATA,
|
||||
RATESTATUS_INIT,
|
||||
RATESTATUS_NODATA,
|
||||
RATESTATUS_STALE
|
||||
} MeterRateStatus;
|
||||
|
||||
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);
|
||||
|
||||
@ -132,7 +153,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[];
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
htop - MetersPanel.c
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
@ -184,9 +184,9 @@ static HandlerResult MetersPanel_eventHandler(Panel* super, int ch) {
|
||||
if (result == HANDLED || sideMove) {
|
||||
Header* header = this->scr->header;
|
||||
this->settings->changed = true;
|
||||
this->settings->lastUpdate++;
|
||||
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 +216,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;
|
||||
|
@ -3,7 +3,7 @@
|
||||
/*
|
||||
htop - MetersPanel.h
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
|
125
NetworkIOMeter.c
125
NetworkIOMeter.c
@ -1,13 +1,15 @@
|
||||
#include "NetworkIOMeter.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <sys/time.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "CRT.h"
|
||||
#include "Macros.h"
|
||||
#include "Meter.h"
|
||||
#include "Object.h"
|
||||
#include "Platform.h"
|
||||
#include "Process.h"
|
||||
#include "ProcessList.h"
|
||||
#include "RichString.h"
|
||||
#include "XUtils.h"
|
||||
|
||||
@ -17,82 +19,119 @@ static const int NetworkIOMeter_attributes[] = {
|
||||
METER_VALUE_IOWRITE,
|
||||
};
|
||||
|
||||
static bool hasData = false;
|
||||
static MeterRateStatus status = RATESTATUS_INIT;
|
||||
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 unsigned long int cached_rxb_diff = 0;
|
||||
static unsigned long int cached_rxp_diff = 0;
|
||||
static unsigned long int cached_txb_diff = 0;
|
||||
static unsigned long int cached_txp_diff = 0;
|
||||
static void NetworkIOMeter_updateValues(Meter* this) {
|
||||
const ProcessList* pl = this->pl;
|
||||
static uint64_t cached_last_update = 0;
|
||||
|
||||
static void NetworkIOMeter_updateValues(ATTR_UNUSED Meter* this, char* buffer, size_t len) {
|
||||
static unsigned long long int cached_last_update = 0;
|
||||
uint64_t passedTimeInMs = pl->realtimeMs - cached_last_update;
|
||||
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
unsigned long long int timeInMilliSeconds = (unsigned long long int)tv.tv_sec * 1000 + (unsigned long long int)tv.tv_usec / 1000;
|
||||
unsigned long long int passedTimeInMs = timeInMilliSeconds - cached_last_update;
|
||||
|
||||
/* update only every 500ms */
|
||||
/* update only every 500ms to have a sane span for rate calculation */
|
||||
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;
|
||||
NetworkIOData data;
|
||||
if (!Platform_getNetworkIO(&data)) {
|
||||
status = RATESTATUS_NODATA;
|
||||
} else if (cached_last_update == 0) {
|
||||
status = RATESTATUS_INIT;
|
||||
} else if (passedTimeInMs > 30000) {
|
||||
status = RATESTATUS_STALE;
|
||||
} else {
|
||||
status = RATESTATUS_DATA;
|
||||
}
|
||||
|
||||
unsigned long int bytesReceived, packetsReceived, bytesTransmitted, packetsTransmitted;
|
||||
cached_last_update = pl->realtimeMs;
|
||||
|
||||
hasData = Platform_getNetworkIO(&bytesReceived, &packetsReceived, &bytesTransmitted, &packetsTransmitted);
|
||||
if (!hasData) {
|
||||
xSnprintf(buffer, len, "no data");
|
||||
if (status == RATESTATUS_NODATA) {
|
||||
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;
|
||||
}
|
||||
|
||||
if (status == RATESTATUS_INIT) {
|
||||
xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "init");
|
||||
return;
|
||||
}
|
||||
if (status == RATESTATUS_STALE) {
|
||||
xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "stale");
|
||||
return;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (!hasData) {
|
||||
switch (status) {
|
||||
case RATESTATUS_NODATA:
|
||||
RichString_writeAscii(out, CRT_colors[METER_VALUE_ERROR], "no data");
|
||||
return;
|
||||
case RATESTATUS_INIT:
|
||||
RichString_writeAscii(out, CRT_colors[METER_VALUE], "initializing...");
|
||||
return;
|
||||
case RATESTATUS_STALE:
|
||||
RichString_writeAscii(out, CRT_colors[METER_VALUE_WARN], "stale data");
|
||||
return;
|
||||
case RATESTATUS_DATA:
|
||||
break;
|
||||
}
|
||||
|
||||
char buffer[64];
|
||||
int len;
|
||||
|
||||
RichString_writeAscii(out, CRT_colors[METER_TEXT], "rx: ");
|
||||
Meter_humanUnit(buffer, cached_rxb_diff, sizeof(buffer));
|
||||
@ -104,8 +143,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 +155,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 */
|
||||
|
6
Object.c
6
Object.c
@ -2,7 +2,7 @@
|
||||
htop - Object.c
|
||||
(C) 2004-2012 Hisham H. Muhammad
|
||||
(C) 2020 Red Hat, Inc. All Rights Reserved.
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
@ -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 */
|
||||
|
13
Object.h
13
Object.h
@ -4,27 +4,24 @@
|
||||
htop - Object.h
|
||||
(C) 2004-2012 Hisham H. Muhammad
|
||||
(C) 2020 Red Hat, Inc. All Rights Reserved.
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
#include "config.h" // IWYU pragma: keep
|
||||
|
||||
#include <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;
|
||||
|
||||
typedef void(*Object_Display)(const Object*, RichString*);
|
||||
typedef long(*Object_Compare)(const void*, const void*);
|
||||
typedef int(*Object_Compare)(const void*, const void*);
|
||||
typedef void(*Object_Delete)(Object*);
|
||||
|
||||
#define Object_getClass(obj_) ((const Object*)(obj_))->klass
|
||||
@ -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
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
htop - OpenFilesScreen.c
|
||||
(C) 2005-2006 Hisham H. Muhammad
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
@ -9,13 +9,16 @@ in the source distribution for its full text.
|
||||
|
||||
#include "OpenFilesScreen.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "Macros.h"
|
||||
#include "Panel.h"
|
||||
@ -25,7 +28,7 @@ in the source distribution for its full text.
|
||||
|
||||
|
||||
typedef struct OpenFiles_Data_ {
|
||||
char* data[7];
|
||||
char* data[8];
|
||||
} OpenFiles_Data;
|
||||
|
||||
typedef struct OpenFiles_ProcessData_ {
|
||||
@ -55,6 +58,8 @@ static size_t getIndexForType(char type) {
|
||||
return 5;
|
||||
case 't':
|
||||
return 6;
|
||||
case 'o':
|
||||
return 7;
|
||||
}
|
||||
|
||||
/* should never reach here */
|
||||
@ -74,7 +79,7 @@ OpenFilesScreen* OpenFilesScreen_new(const Process* process) {
|
||||
} else {
|
||||
this->pid = process->pid;
|
||||
}
|
||||
return (OpenFilesScreen*) InfoScreen_init(&this->super, process, NULL, LINES - 3, " FD TYPE MODE DEVICE SIZE NODE NAME");
|
||||
return (OpenFilesScreen*) InfoScreen_init(&this->super, process, NULL, LINES - 2, " FD TYPE MODE DEVICE SIZE OFFSET NODE NAME");
|
||||
}
|
||||
|
||||
void OpenFilesScreen_delete(Object* this) {
|
||||
@ -115,13 +120,16 @@ static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) {
|
||||
close(fdnull);
|
||||
char buffer[32] = {0};
|
||||
xSnprintf(buffer, sizeof(buffer), "%d", pid);
|
||||
execlp("lsof", "lsof", "-P", "-p", buffer, "-F", NULL);
|
||||
// Use of NULL in variadic functions must have a pointer cast.
|
||||
// The NULL constant is not required by standard to have a pointer type.
|
||||
execlp("lsof", "lsof", "-P", "-o", "-p", buffer, "-F", (char *)NULL);
|
||||
exit(127);
|
||||
}
|
||||
close(fdpair[1]);
|
||||
|
||||
OpenFiles_Data* item = &(pdata->data);
|
||||
OpenFiles_FileData* fdata = NULL;
|
||||
bool lsofIncludesFileSize = false;
|
||||
|
||||
FILE* fd = fdopen(fdpair[0], "r");
|
||||
if (!fd) {
|
||||
@ -155,8 +163,17 @@ static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) {
|
||||
case 't': /* file's type */
|
||||
{
|
||||
size_t index = getIndexForType(cmd);
|
||||
free(item->data[index]);
|
||||
item->data[index] = xStrdup(line + 1);
|
||||
free_and_xStrdup(&item->data[index], line + 1);
|
||||
break;
|
||||
}
|
||||
case 'o': /* file's offset */
|
||||
{
|
||||
size_t index = getIndexForType(cmd);
|
||||
if (String_startsWith(line + 1, "0t")) {
|
||||
free_and_xStrdup(&item->data[index], line + 3);
|
||||
} else {
|
||||
free_and_xStrdup(&item->data[index], line + 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'c': /* process command name */
|
||||
@ -166,7 +183,6 @@ static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) {
|
||||
case 'k': /* link count */
|
||||
case 'l': /* file's lock status */
|
||||
case 'L': /* process login name */
|
||||
case 'o': /* file's offset */
|
||||
case 'p': /* process ID */
|
||||
case 'P': /* protocol name */
|
||||
case 'R': /* parent process ID */
|
||||
@ -175,12 +191,17 @@ static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) {
|
||||
/* ignore */
|
||||
break;
|
||||
}
|
||||
|
||||
if (cmd == 's')
|
||||
lsofIncludesFileSize = true;
|
||||
|
||||
free(line);
|
||||
}
|
||||
fclose(fd);
|
||||
|
||||
int wstatus;
|
||||
if (waitpid(child, &wstatus, 0) == -1) {
|
||||
while (waitpid(child, &wstatus, 0) == -1)
|
||||
if (errno != EINTR) {
|
||||
pdata->error = 1;
|
||||
return pdata;
|
||||
}
|
||||
@ -191,6 +212,25 @@ static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) {
|
||||
pdata->error = WEXITSTATUS(wstatus);
|
||||
}
|
||||
|
||||
/* We got all information we need; no post-processing needed */
|
||||
if (lsofIncludesFileSize)
|
||||
return pdata;
|
||||
|
||||
/* On linux, `lsof -o -F` omits SIZE, so add it back. */
|
||||
/* On macOS, `lsof -o -F` includes SIZE, so this block isn't needed. If no open files have a filesize, this will still run, unfortunately. */
|
||||
size_t fileSizeIndex = getIndexForType('s');
|
||||
for (fdata = pdata->files; fdata != NULL; fdata = fdata->next) {
|
||||
item = &fdata->data;
|
||||
const char* filename = getDataForType(item, 'n');
|
||||
|
||||
struct stat st;
|
||||
if (stat(filename, &st) == 0) {
|
||||
char fileSizeBuf[21]; /* 20 (long long) + 1 (NULL) */
|
||||
xSnprintf(fileSizeBuf, sizeof(fileSizeBuf), "%"PRIu64, st.st_size); /* st.st_size is long long on macOS, long on linux */
|
||||
free_and_xStrdup(&item->data[fileSizeIndex], fileSizeBuf);
|
||||
}
|
||||
}
|
||||
|
||||
return pdata;
|
||||
}
|
||||
|
||||
@ -213,14 +253,15 @@ static void OpenFilesScreen_scan(InfoScreen* this) {
|
||||
while (fdata) {
|
||||
OpenFiles_Data* data = &fdata->data;
|
||||
size_t lenN = strlen(getDataForType(data, 'n'));
|
||||
size_t sizeEntry = 5 + 7 + 4 + 10 + 10 + 10 + lenN + 7 /*spaces*/ + 1 /*null*/;
|
||||
size_t sizeEntry = 5 + 7 + 4 + 10 + 10 + 10 + 10 + lenN + 8 /*spaces*/ + 1 /*null*/;
|
||||
char entry[sizeEntry];
|
||||
xSnprintf(entry, sizeof(entry), "%5.5s %-7.7s %-4.4s %-10.10s %10.10s %10.10s %s",
|
||||
xSnprintf(entry, sizeof(entry), "%5.5s %-7.7s %-4.4s %-10.10s %10.10s %10.10s %10.10s %s",
|
||||
getDataForType(data, 'f'),
|
||||
getDataForType(data, 't'),
|
||||
getDataForType(data, 'a'),
|
||||
getDataForType(data, 'D'),
|
||||
getDataForType(data, 's'),
|
||||
getDataForType(data, 'o'),
|
||||
getDataForType(data, 'i'),
|
||||
getDataForType(data, 'n'));
|
||||
InfoScreen_addLine(this, entry);
|
||||
|
@ -3,7 +3,7 @@
|
||||
/*
|
||||
htop - OpenFilesScreen.h
|
||||
(C) 2005-2006 Hisham H. Muhammad
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
@ -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;
|
||||
|
28
OptionItem.c
28
OptionItem.c
@ -1,7 +1,7 @@
|
||||
/*
|
||||
htop - OptionItem.c
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
@ -25,6 +25,13 @@ static void OptionItem_delete(Object* cast) {
|
||||
free(this);
|
||||
}
|
||||
|
||||
static void TextItem_display(const Object* cast, RichString* out) {
|
||||
const TextItem* this = (const TextItem*)cast;
|
||||
assert (this != NULL);
|
||||
|
||||
RichString_appendWide(out, CRT_colors[HELP_BOLD], this->super.text);
|
||||
}
|
||||
|
||||
static void CheckItem_display(const Object* cast, RichString* out) {
|
||||
const CheckItem* this = (const CheckItem*)cast;
|
||||
assert (this != NULL);
|
||||
@ -53,7 +60,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], " ");
|
||||
@ -68,6 +75,16 @@ const OptionItemClass OptionItem_class = {
|
||||
}
|
||||
};
|
||||
|
||||
const OptionItemClass TextItem_class = {
|
||||
.super = {
|
||||
.extends = Class(OptionItem),
|
||||
.delete = OptionItem_delete,
|
||||
.display = TextItem_display
|
||||
},
|
||||
.kind = OPTION_ITEM_TEXT
|
||||
};
|
||||
|
||||
|
||||
const OptionItemClass CheckItem_class = {
|
||||
.super = {
|
||||
.extends = Class(OptionItem),
|
||||
@ -77,6 +94,7 @@ const OptionItemClass CheckItem_class = {
|
||||
.kind = OPTION_ITEM_CHECK
|
||||
};
|
||||
|
||||
|
||||
const OptionItemClass NumberItem_class = {
|
||||
.super = {
|
||||
.extends = Class(OptionItem),
|
||||
@ -86,6 +104,12 @@ const OptionItemClass NumberItem_class = {
|
||||
.kind = OPTION_ITEM_NUMBER
|
||||
};
|
||||
|
||||
TextItem* TextItem_new(const char* text) {
|
||||
TextItem* this = AllocThis(TextItem);
|
||||
this->super.text = xStrdup(text);
|
||||
return this;
|
||||
}
|
||||
|
||||
CheckItem* CheckItem_newByRef(const char* text, bool* ref) {
|
||||
CheckItem* this = AllocThis(CheckItem);
|
||||
this->super.text = xStrdup(text);
|
||||
|
12
OptionItem.h
12
OptionItem.h
@ -3,7 +3,7 @@
|
||||
/*
|
||||
htop - OptionItem.h
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
@ -13,6 +13,7 @@ in the source distribution for its full text.
|
||||
|
||||
|
||||
enum OptionItemType {
|
||||
OPTION_ITEM_TEXT,
|
||||
OPTION_ITEM_CHECK,
|
||||
OPTION_ITEM_NUMBER,
|
||||
};
|
||||
@ -32,6 +33,12 @@ typedef struct OptionItem_ {
|
||||
char* text;
|
||||
} OptionItem;
|
||||
|
||||
typedef struct TextItem_ {
|
||||
OptionItem super;
|
||||
|
||||
char* text;
|
||||
} TextItem;
|
||||
|
||||
typedef struct CheckItem_ {
|
||||
OptionItem super;
|
||||
|
||||
@ -51,9 +58,12 @@ typedef struct NumberItem_ {
|
||||
} NumberItem;
|
||||
|
||||
extern const OptionItemClass OptionItem_class;
|
||||
extern const OptionItemClass TextItem_class;
|
||||
extern const OptionItemClass CheckItem_class;
|
||||
extern const OptionItemClass NumberItem_class;
|
||||
|
||||
TextItem* TextItem_new(const char* text);
|
||||
|
||||
CheckItem* CheckItem_newByRef(const char* text, bool* ref);
|
||||
CheckItem* CheckItem_newByVal(const char* text, bool value);
|
||||
bool CheckItem_get(const CheckItem* this);
|
||||
|
120
Panel.c
120
Panel.c
@ -1,7 +1,7 @@
|
||||
/*
|
||||
htop - Panel.c
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
@ -30,7 +30,7 @@ const PanelClass Panel_class = {
|
||||
.eventHandler = Panel_selectByTyping,
|
||||
};
|
||||
|
||||
Panel* Panel_new(int x, int y, int w, int h, bool owner, const ObjectClass* type, FunctionBar* fuBar) {
|
||||
Panel* Panel_new(int x, int y, int w, int h, const ObjectClass* type, bool owner, FunctionBar* fuBar) {
|
||||
Panel* this;
|
||||
this = xMalloc(sizeof(Panel));
|
||||
Object_setClass(this, Class(Panel));
|
||||
@ -49,6 +49,8 @@ void Panel_init(Panel* this, int x, int y, int w, int h, const ObjectClass* type
|
||||
this->y = y;
|
||||
this->w = w;
|
||||
this->h = h;
|
||||
this->cursorX = 0;
|
||||
this->cursorY = 0;
|
||||
this->eventHandlerState = NULL;
|
||||
this->items = Vector_new(type, owner, DEFAULT_SIZE);
|
||||
this->scrollV = 0;
|
||||
@ -57,6 +59,7 @@ void Panel_init(Panel* this, int x, int y, int w, int h, const ObjectClass* type
|
||||
this->oldSelected = 0;
|
||||
this->selectedLen = 0;
|
||||
this->needsRedraw = true;
|
||||
this->cursorOn = false;
|
||||
this->wasFocus = false;
|
||||
RichString_beginAllocated(this->header);
|
||||
this->defaultBar = fuBar;
|
||||
@ -69,20 +72,18 @@ 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_setCursorToSelection(Panel* this) {
|
||||
this->cursorY = this->y + this->selected - this->scrollV + 1;
|
||||
this->cursorX = this->x + this->selectedLen - this->scrollH;
|
||||
}
|
||||
|
||||
void Panel_setSelectionColor(Panel* this, ColorElements colorId) {
|
||||
this->selectionColorId = colorId;
|
||||
}
|
||||
|
||||
RichString* Panel_getHeader(Panel* this) {
|
||||
assert (this != NULL);
|
||||
|
||||
this->needsRedraw = true;
|
||||
return &(this->header);
|
||||
}
|
||||
|
||||
inline void Panel_setHeader(Panel* this, const char* header) {
|
||||
RichString_writeWide(&(this->header), CRT_colors[PANEL_HEADER_FOCUS], header);
|
||||
this->needsRedraw = true;
|
||||
@ -99,10 +100,6 @@ void Panel_move(Panel* this, int x, int y) {
|
||||
void Panel_resize(Panel* this, int w, int h) {
|
||||
assert (this != NULL);
|
||||
|
||||
if (RichString_sizeVal(this->header) > 0) {
|
||||
h--;
|
||||
}
|
||||
|
||||
this->w = w;
|
||||
this->h = h;
|
||||
this->needsRedraw = true;
|
||||
@ -183,13 +180,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);
|
||||
@ -219,7 +216,7 @@ void Panel_splice(Panel* this, Vector* from) {
|
||||
this->needsRedraw = true;
|
||||
}
|
||||
|
||||
void Panel_draw(Panel* this, bool force_redraw, bool focus, bool highlightSelected) {
|
||||
void Panel_draw(Panel* this, bool force_redraw, bool focus, bool highlightSelected, bool hideFunctionBar) {
|
||||
assert (this != NULL);
|
||||
|
||||
int size = Vector_size(this->items);
|
||||
@ -228,28 +225,37 @@ void Panel_draw(Panel* this, bool force_redraw, bool focus, bool highlightSelect
|
||||
int x = this->x;
|
||||
int h = this->h;
|
||||
|
||||
int headerLen = RichString_sizeVal(this->header);
|
||||
if (headerLen > 0) {
|
||||
int attr = focus
|
||||
if (hideFunctionBar)
|
||||
h++;
|
||||
|
||||
const int header_attr = focus
|
||||
? CRT_colors[PANEL_HEADER_FOCUS]
|
||||
: CRT_colors[PANEL_HEADER_UNFOCUS];
|
||||
attrset(attr);
|
||||
if (force_redraw) {
|
||||
if (Panel_printHeaderFn(this))
|
||||
Panel_printHeader(this);
|
||||
else
|
||||
RichString_setAttr(&this->header, header_attr);
|
||||
}
|
||||
int headerLen = RichString_sizeVal(this->header);
|
||||
if (headerLen > 0) {
|
||||
attrset(header_attr);
|
||||
mvhline(y, x, ' ', this->w);
|
||||
if (scrollH < headerLen) {
|
||||
RichString_setAttr(&this->header, attr);
|
||||
RichString_printoffnVal(this->header, y, x, scrollH,
|
||||
MINIMUM(headerLen - scrollH, this->w));
|
||||
}
|
||||
attrset(CRT_colors[RESET_COLOR]);
|
||||
y++;
|
||||
h--;
|
||||
}
|
||||
|
||||
// ensure scroll area is on screen
|
||||
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
|
||||
@ -271,7 +277,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);
|
||||
@ -289,7 +295,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) {
|
||||
@ -298,11 +304,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);
|
||||
@ -318,30 +324,37 @@ 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)) {
|
||||
if (Panel_drawFunctionBarFn(this))
|
||||
Panel_drawFunctionBar(this);
|
||||
else
|
||||
Panel_drawFunctionBar(this, hideFunctionBar);
|
||||
else if (!hideFunctionBar)
|
||||
FunctionBar_draw(this->currentBar);
|
||||
}
|
||||
|
||||
this->oldSelected = this->selected;
|
||||
this->wasFocus = focus;
|
||||
this->needsRedraw = false;
|
||||
move(0, 0);
|
||||
}
|
||||
|
||||
static int Panel_headerHeight(const Panel* this) {
|
||||
return RichString_sizeVal(this->header) > 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
bool Panel_onKey(Panel* this, int key) {
|
||||
assert (this != NULL);
|
||||
|
||||
int size = Vector_size(this->items);
|
||||
const int size = Vector_size(this->items);
|
||||
|
||||
#define CLAMP_INDEX(var, delta, min, max) \
|
||||
CLAMP((var) + (delta), (min), MAXIMUM(0, (max)))
|
||||
#define PANEL_SCROLL(amount) \
|
||||
do { \
|
||||
this->selected += (amount); \
|
||||
this->scrollV = CLAMP(this->scrollV + (amount), 0, MAXIMUM(0, (size - this->h - Panel_headerHeight(this)))); \
|
||||
this->needsRedraw = true; \
|
||||
} while (0)
|
||||
|
||||
switch (key) {
|
||||
case KEY_DOWN:
|
||||
@ -375,27 +388,19 @@ bool Panel_onKey(Panel* this, int key) {
|
||||
break;
|
||||
|
||||
case KEY_PPAGE:
|
||||
this->selected -= (this->h - 1);
|
||||
this->scrollV = CLAMP_INDEX(this->scrollV, -(this->h - 1), 0, size - this->h);
|
||||
this->needsRedraw = true;
|
||||
PANEL_SCROLL(-(this->h - Panel_headerHeight(this)));
|
||||
break;
|
||||
|
||||
case KEY_NPAGE:
|
||||
this->selected += (this->h - 1);
|
||||
this->scrollV = CLAMP_INDEX(this->scrollV, +(this->h - 1), 0, size - this->h);
|
||||
this->needsRedraw = true;
|
||||
PANEL_SCROLL(+(this->h - Panel_headerHeight(this)));
|
||||
break;
|
||||
|
||||
case KEY_WHEELUP:
|
||||
this->selected -= CRT_scrollWheelVAmount;
|
||||
this->scrollV = CLAMP_INDEX(this->scrollV, -CRT_scrollWheelVAmount, 0, size - this->h);
|
||||
this->needsRedraw = true;
|
||||
PANEL_SCROLL(-CRT_scrollWheelVAmount);
|
||||
break;
|
||||
|
||||
case KEY_WHEELDOWN:
|
||||
this->selected += CRT_scrollWheelVAmount;
|
||||
this->scrollV = CLAMP_INDEX(this->scrollV, +CRT_scrollWheelVAmount, 0, size - this->h);
|
||||
this->needsRedraw = true;
|
||||
PANEL_SCROLL(+CRT_scrollWheelVAmount);
|
||||
break;
|
||||
|
||||
case KEY_HOME:
|
||||
@ -420,7 +425,7 @@ bool Panel_onKey(Panel* this, int key) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#undef CLAMP_INDEX
|
||||
#undef PANEL_SCROLL
|
||||
|
||||
// ensure selection within bounds
|
||||
if (this->selected < 0 || size == 0) {
|
||||
@ -455,7 +460,7 @@ HandlerResult Panel_selectByTyping(Panel* this, int ch) {
|
||||
}
|
||||
|
||||
if (len < 99) {
|
||||
buffer[len] = ch;
|
||||
buffer[len] = (char) ch;
|
||||
buffer[len + 1] = '\0';
|
||||
}
|
||||
|
||||
@ -472,7 +477,7 @@ HandlerResult Panel_selectByTyping(Panel* this, int ch) {
|
||||
|
||||
// if current word did not match,
|
||||
// retry considering the character the start of a new word.
|
||||
buffer[0] = ch;
|
||||
buffer[0] = (char) ch;
|
||||
buffer[1] = '\0';
|
||||
}
|
||||
|
||||
@ -487,3 +492,16 @@ HandlerResult Panel_selectByTyping(Panel* this, int ch) {
|
||||
|
||||
return IGNORED;
|
||||
}
|
||||
|
||||
int Panel_getCh(Panel* this) {
|
||||
if (this->cursorOn) {
|
||||
move(this->cursorY, this->cursorX);
|
||||
curs_set(1);
|
||||
} else {
|
||||
curs_set(0);
|
||||
}
|
||||
#ifdef HAVE_SET_ESCDELAY
|
||||
set_escdelay(25);
|
||||
#endif
|
||||
return getch();
|
||||
}
|
||||
|
38
Panel.h
38
Panel.h
@ -3,10 +3,13 @@
|
||||
/*
|
||||
htop - Panel.h
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
#include "config.h" // IWYU pragma: keep
|
||||
|
||||
#include <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)
|
||||
@ -35,32 +39,42 @@ typedef enum HandlerResult_ {
|
||||
#define EVENT_IS_HEADER_CLICK(ev_) ((ev_) >= -10000 && (ev_) <= -9000)
|
||||
#define EVENT_HEADER_CLICK_GET_X(ev_) ((ev_) + 10000)
|
||||
|
||||
#define EVENT_SCREEN_TAB_CLICK(x_) (-20000 + (x_))
|
||||
#define EVENT_IS_SCREEN_TAB_CLICK(ev_) ((ev_) >= -20000 && (ev_) < -10000)
|
||||
#define EVENT_SCREEN_TAB_GET_X(ev_) ((ev_) + 20000)
|
||||
|
||||
typedef HandlerResult (*Panel_EventHandler)(Panel*, int);
|
||||
typedef void (*Panel_DrawFunctionBar)(Panel*);
|
||||
typedef void (*Panel_DrawFunctionBar)(Panel*, bool);
|
||||
typedef void (*Panel_PrintHeader)(Panel*);
|
||||
|
||||
typedef struct PanelClass_ {
|
||||
const ObjectClass super;
|
||||
const Panel_EventHandler eventHandler;
|
||||
const Panel_DrawFunctionBar drawFunctionBar;
|
||||
const Panel_PrintHeader printHeader;
|
||||
} PanelClass;
|
||||
|
||||
#define As_Panel(this_) ((const PanelClass*)((this_)->super.klass))
|
||||
#define Panel_eventHandlerFn(this_) As_Panel(this_)->eventHandler
|
||||
#define Panel_eventHandler(this_, ev_) (assert(As_Panel(this_)->eventHandler), As_Panel(this_)->eventHandler((Panel*)(this_), ev_))
|
||||
#define Panel_drawFunctionBarFn(this_) As_Panel(this_)->drawFunctionBar
|
||||
#define Panel_drawFunctionBar(this_) (assert(As_Panel(this_)->drawFunctionBar), As_Panel(this_)->drawFunctionBar((Panel*)(this_)))
|
||||
#define Panel_drawFunctionBar(this_, hideFB_) (assert(As_Panel(this_)->drawFunctionBar), As_Panel(this_)->drawFunctionBar((Panel*)(this_), hideFB_))
|
||||
#define Panel_printHeaderFn(this_) As_Panel(this_)->printHeader
|
||||
#define Panel_printHeader(this_) (assert(As_Panel(this_)->printHeader), As_Panel(this_)->printHeader((Panel*)(this_)))
|
||||
|
||||
struct Panel_ {
|
||||
Object super;
|
||||
int x, y, w, h;
|
||||
int cursorX, cursorY;
|
||||
Vector* items;
|
||||
int selected;
|
||||
int oldSelected;
|
||||
int selectedLen;
|
||||
void* eventHandlerState;
|
||||
int scrollV;
|
||||
short scrollH;
|
||||
int scrollH;
|
||||
bool needsRedraw;
|
||||
bool cursorOn;
|
||||
bool wasFocus;
|
||||
FunctionBar* currentBar;
|
||||
FunctionBar* defaultBar;
|
||||
@ -74,7 +88,7 @@ struct Panel_ {
|
||||
|
||||
extern const PanelClass Panel_class;
|
||||
|
||||
Panel* Panel_new(int x, int y, int w, int h, bool owner, const ObjectClass* type, FunctionBar* fuBar);
|
||||
Panel* Panel_new(int x, int y, int w, int h, const ObjectClass* type, bool owner, FunctionBar* fuBar);
|
||||
|
||||
void Panel_delete(Object* cast);
|
||||
|
||||
@ -82,9 +96,9 @@ void Panel_init(Panel* this, int x, int y, int w, int h, const ObjectClass* type
|
||||
|
||||
void Panel_done(Panel* this);
|
||||
|
||||
void Panel_setSelectionColor(Panel* this, ColorElements colorId);
|
||||
void Panel_setCursorToSelection(Panel* this);
|
||||
|
||||
RichString* Panel_getHeader(Panel* this);
|
||||
void Panel_setSelectionColor(Panel* this, ColorElements colorId);
|
||||
|
||||
void Panel_setHeader(Panel* this, const char* header);
|
||||
|
||||
@ -110,13 +124,13 @@ 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);
|
||||
|
||||
void Panel_draw(Panel* this, bool force_redraw, bool focus, bool highlightSelected);
|
||||
void Panel_draw(Panel* this, bool force_redraw, bool focus, bool highlightSelected, bool hideFunctionBar);
|
||||
|
||||
void Panel_splice(Panel* this, Vector* from);
|
||||
|
||||
@ -124,4 +138,6 @@ bool Panel_onKey(Panel* this, int key);
|
||||
|
||||
HandlerResult Panel_selectByTyping(Panel* this, int ch);
|
||||
|
||||
int Panel_getCh(Panel* this);
|
||||
|
||||
#endif
|
||||
|
296
Process.h
296
Process.h
@ -4,7 +4,7 @@
|
||||
htop - Process.h
|
||||
(C) 2004-2015 Hisham H. Muhammad
|
||||
(C) 2020 Red Hat, Inc. All Rights Reserved.
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
@ -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,104 +48,260 @@ 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;
|
||||
|
||||
/* Core process states (shared by platforms)
|
||||
* NOTE: The enum has an ordering that is important!
|
||||
* See processStateChar in process.c for ProcessSate -> letter mapping */
|
||||
typedef enum ProcessState_ {
|
||||
UNKNOWN = 1,
|
||||
RUNNABLE,
|
||||
RUNNING,
|
||||
QUEUED,
|
||||
WAITING,
|
||||
UNINTERRUPTIBLE_WAIT,
|
||||
BLOCKED,
|
||||
PAGING,
|
||||
STOPPED,
|
||||
TRACED,
|
||||
ZOMBIE,
|
||||
DEFUNCT,
|
||||
IDLE,
|
||||
SLEEPING
|
||||
} ProcessState;
|
||||
|
||||
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_ {
|
||||
uint64_t lastUpdate; /* Marker based on settings->lastUpdate to track when the rendering needs refreshing */
|
||||
char* str; /* merged Command string */
|
||||
size_t highlightCount; /* how many portions of cmdline to highlight */
|
||||
ProcessCmdlineHighlight highlights[8]; /* which portions of cmdline to highlight */
|
||||
} 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;
|
||||
|
||||
unsigned int tree_left;
|
||||
unsigned int tree_right;
|
||||
/* Process state enum field (platform dependent) */
|
||||
ProcessState 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_depth;
|
||||
unsigned int tree_index;
|
||||
|
||||
/* Has no known parent process */
|
||||
bool isRoot;
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/* Whether the column width is dynamically adjusted (the minimum width is determined by the title length) */
|
||||
bool autoWidth;
|
||||
} ProcessFieldData;
|
||||
|
||||
// Implemented in platform-specific code:
|
||||
void Process_writeField(const Process* this, RichString* str, ProcessField field);
|
||||
long Process_compare(const void* v1, const void* v2);
|
||||
int Process_compare(const void* v1, const void* v2);
|
||||
void Process_delete(Object* cast);
|
||||
bool Process_isThread(const Process* this);
|
||||
extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD];
|
||||
extern uint8_t Process_fieldWidths[LAST_PROCESSFIELD];
|
||||
#define PROCESS_MIN_PID_DIGITS 5
|
||||
#define PROCESS_MAX_PID_DIGITS 19
|
||||
#define PROCESS_MIN_UID_DIGITS 5
|
||||
#define PROCESS_MAX_UID_DIGITS 20
|
||||
extern int Process_pidDigits;
|
||||
extern int Process_uidDigits;
|
||||
|
||||
typedef Process* (*Process_New)(const struct Settings_*);
|
||||
typedef void (*Process_WriteField)(const Process*, RichString*, ProcessField);
|
||||
typedef long (*Process_CompareByKey)(const Process*, const Process*, ProcessField);
|
||||
typedef const char* (*Process_GetCommandStr)(const Process*);
|
||||
typedef int (*Process_CompareByKey)(const Process*, const Process*, ProcessField);
|
||||
|
||||
typedef struct ProcessClass_ {
|
||||
const ObjectClass super;
|
||||
const Process_WriteField writeField;
|
||||
const Process_CompareByKey compareByKey;
|
||||
const Process_GetCommandStr getCommandStr;
|
||||
} 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_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,30 +312,60 @@ 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);
|
||||
/* Sets the size of the UID column based on the passed UID */
|
||||
void Process_setUidColumnWidth(uid_t maxUid);
|
||||
|
||||
void Process_colorNumber(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_printTime(RichString* str, unsigned long long totalHundredths);
|
||||
/* Takes number in kilo bytes (base 1024). Prints 6 columns. */
|
||||
void Process_printKBytes(RichString* str, unsigned long long number, bool coloring);
|
||||
|
||||
/* 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_printPercentage(float val, char* buffer, int n, uint8_t width, int* attr);
|
||||
|
||||
void Process_display(const Object* cast, RichString* out);
|
||||
|
||||
@ -199,8 +387,28 @@ bool Process_changePriorityBy(Process* this, Arg delta);
|
||||
|
||||
bool Process_sendSignal(Process* this, Arg sgn);
|
||||
|
||||
long Process_pidCompare(const void* v1, const void* v2);
|
||||
static inline int Process_pidEqualCompare(const void* v1, const void* v2) {
|
||||
const pid_t p1 = ((const Process*)v1)->pid;
|
||||
const pid_t p2 = ((const Process*)v2)->pid;
|
||||
return p1 != p2; /* return zero when equal */
|
||||
}
|
||||
|
||||
long Process_compareByKey_Base(const Process* p1, const Process* p2, ProcessField key);
|
||||
int Process_compareByKey_Base(const Process* p1, const Process* p2, ProcessField key);
|
||||
|
||||
const char* Process_getCommand(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_getCommand */
|
||||
void Process_makeCommandStr(Process* this);
|
||||
|
||||
void Process_writeCommand(const Process* this, int attr, int baseAttr, RichString* str);
|
||||
|
||||
void Process_resetFieldWidths(void);
|
||||
void Process_updateFieldWidth(ProcessField key, size_t width);
|
||||
void Process_updateCPUFieldWidths(float percentage);
|
||||
|
||||
#endif
|
||||
|
568
ProcessList.c
568
ProcessList.c
@ -1,7 +1,7 @@
|
||||
/*
|
||||
htop - ProcessList.c
|
||||
(C) 2004,2005 Hisham H. Muhammad
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
@ -11,31 +11,36 @@ 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
|
||||
this->displayList = Vector_new(klass, false, DEFAULT_SIZE);
|
||||
|
||||
this->processTable = Hashtable_new(200, false);
|
||||
this->displayTreeSet = Hashtable_new(200, false);
|
||||
this->draftingTreeSet = Hashtable_new(200, false);
|
||||
this->needsSort = true;
|
||||
|
||||
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;
|
||||
@ -67,11 +72,9 @@ void ProcessList_done(ProcessList* this) {
|
||||
}
|
||||
#endif
|
||||
|
||||
Hashtable_delete(this->draftingTreeSet);
|
||||
Hashtable_delete(this->displayTreeSet);
|
||||
Hashtable_delete(this->processTable);
|
||||
|
||||
Vector_delete(this->processes2);
|
||||
Vector_delete(this->displayList);
|
||||
Vector_delete(this->processes);
|
||||
}
|
||||
|
||||
@ -79,31 +82,63 @@ 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, char* titleBuffer, size_t titleBufferSize) {
|
||||
const DynamicColumn* column = Hashtable_get(this->dynamicColumns, key);
|
||||
if (column == NULL)
|
||||
return "- ";
|
||||
int width = column->width;
|
||||
if (!width || abs(width) > DYNAMIC_MAX_COLUMN_WIDTH)
|
||||
width = DYNAMIC_DEFAULT_COLUMN_WIDTH;
|
||||
xSnprintf(titleBuffer, titleBufferSize, "%*s", width, column->heading);
|
||||
return titleBuffer;
|
||||
}
|
||||
|
||||
static const char* alignedProcessFieldTitle(const ProcessList* this, ProcessField field) {
|
||||
static char titleBuffer[UINT8_MAX + sizeof(" ")];
|
||||
assert(sizeof(titleBuffer) >= DYNAMIC_MAX_COLUMN_WIDTH + sizeof(" "));
|
||||
assert(sizeof(titleBuffer) >= PROCESS_MAX_PID_DIGITS + sizeof(" "));
|
||||
assert(sizeof(titleBuffer) >= PROCESS_MAX_UID_DIGITS + sizeof(" "));
|
||||
|
||||
if (field >= LAST_PROCESSFIELD)
|
||||
return alignedDynamicColumnTitle(this, field, titleBuffer, sizeof(titleBuffer));
|
||||
|
||||
const char* title = Process_fields[field].title;
|
||||
if (!title)
|
||||
return "- ";
|
||||
|
||||
if (!Process_fields[field].pidColumn)
|
||||
return title;
|
||||
|
||||
static char titleBuffer[PROCESS_MAX_PID_DIGITS + /* space */ 1 + /* null-terminator */ + 1];
|
||||
if (Process_fields[field].pidColumn) {
|
||||
xSnprintf(titleBuffer, sizeof(titleBuffer), "%*s ", Process_pidDigits, title);
|
||||
|
||||
return titleBuffer;
|
||||
}
|
||||
|
||||
void ProcessList_printHeader(ProcessList* this, RichString* header) {
|
||||
RichString_prune(header);
|
||||
if (field == ST_UID) {
|
||||
xSnprintf(titleBuffer, sizeof(titleBuffer), "%*s ", Process_uidDigits, title);
|
||||
return titleBuffer;
|
||||
}
|
||||
|
||||
if (Process_fields[field].autoWidth) {
|
||||
if (field == PERCENT_CPU)
|
||||
xSnprintf(titleBuffer, sizeof(titleBuffer), "%*s ", Process_fieldWidths[field], title);
|
||||
else
|
||||
xSnprintf(titleBuffer, sizeof(titleBuffer), "%-*.*s ", Process_fieldWidths[field], Process_fieldWidths[field], title);
|
||||
return titleBuffer;
|
||||
}
|
||||
|
||||
return title;
|
||||
}
|
||||
|
||||
void ProcessList_printHeader(const ProcessList* this, RichString* header) {
|
||||
RichString_rewind(header, RichString_size(header));
|
||||
|
||||
const Settings* settings = this->settings;
|
||||
const ProcessField* fields = settings->fields;
|
||||
const ScreenSettings* ss = settings->ss;
|
||||
const ProcessField* fields = ss->fields;
|
||||
|
||||
ProcessField key = Settings_getActiveSortKey(settings);
|
||||
ProcessField key = ScreenSettings_getActiveSortKey(ss);
|
||||
|
||||
for (int i = 0; fields[i]; i++) {
|
||||
int color;
|
||||
if (settings->treeView && settings->treeViewAlwaysByPID) {
|
||||
if (ss->treeView && ss->treeViewAlwaysByPID) {
|
||||
color = CRT_colors[PANEL_HEADER_FOCUS];
|
||||
} else if (key == fields[i]) {
|
||||
color = CRT_colors[PANEL_SELECTION_FOCUS];
|
||||
@ -111,7 +146,15 @@ 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) == ' ') {
|
||||
bool ascending = ScreenSettings_getActiveDirection(ss) == 1;
|
||||
RichString_rewind(header, 1); // rewind to override space
|
||||
RichString_appendnWide(header,
|
||||
CRT_colors[PANEL_SELECTION_FOCUS],
|
||||
CRT_treeStr[ascending ? TREE_STR_ASC : TREE_STR_DESC],
|
||||
1);
|
||||
}
|
||||
if (COMM == fields[i] && settings->showMergedCommand) {
|
||||
RichString_appendAscii(header, color, "(merged)");
|
||||
}
|
||||
@ -119,353 +162,185 @@ void ProcessList_printHeader(ProcessList* this, RichString* header) {
|
||||
}
|
||||
|
||||
void ProcessList_add(ProcessList* this, Process* p) {
|
||||
assert(Vector_indexOf(this->processes, p, Process_pidCompare) == -1);
|
||||
assert(Vector_indexOf(this->processes, p, Process_pidEqualCompare) == -1);
|
||||
assert(Hashtable_get(this->processTable, p->pid) == NULL);
|
||||
p->processList = this;
|
||||
|
||||
// highlighting processes found in first scan by first scan marked "far in the past"
|
||||
p->seenTs = this->scanTs;
|
||||
p->seenStampMs = this->monotonicMs;
|
||||
|
||||
Vector_add(this->processes, p);
|
||||
Hashtable_put(this->processTable, p->pid, p);
|
||||
|
||||
assert(Vector_indexOf(this->processes, p, Process_pidCompare) != -1);
|
||||
assert(Vector_indexOf(this->processes, p, Process_pidEqualCompare) != -1);
|
||||
assert(Hashtable_get(this->processTable, p->pid) != NULL);
|
||||
assert(Hashtable_count(this->processTable) == Vector_count(this->processes));
|
||||
assert(Vector_countEquals(this->processes, Hashtable_count(this->processTable)));
|
||||
}
|
||||
|
||||
void ProcessList_remove(ProcessList* this, Process* p) {
|
||||
assert(Vector_indexOf(this->processes, p, Process_pidCompare) != -1);
|
||||
assert(Hashtable_get(this->processTable, p->pid) != NULL);
|
||||
// ProcessList_removeIndex removes Process p from the list's map and soft deletes
|
||||
// it from its vector. Vector_compact *must* be called once the caller is done
|
||||
// removing items.
|
||||
// Should only be called from ProcessList_scan to avoid breaking dying process highlighting.
|
||||
static void ProcessList_removeIndex(ProcessList* this, const Process* p, int idx) {
|
||||
pid_t pid = p->pid;
|
||||
|
||||
Process* pp = Hashtable_remove(this->processTable, p->pid);
|
||||
assert(pp == p); (void)pp;
|
||||
assert(p == (Process*)Vector_get(this->processes, idx));
|
||||
assert(Hashtable_get(this->processTable, pid) != NULL);
|
||||
|
||||
unsigned int pid = p->pid;
|
||||
int idx = Vector_indexOf(this->processes, p, Process_pidCompare);
|
||||
assert(idx != -1);
|
||||
Hashtable_remove(this->processTable, pid);
|
||||
Vector_softRemove(this->processes, idx);
|
||||
|
||||
if (idx >= 0) {
|
||||
Vector_remove(this->processes, idx);
|
||||
if (this->following != -1 && this->following == pid) {
|
||||
this->following = -1;
|
||||
Panel_setSelectionColor(this->panel, PANEL_SELECTION_FOCUS);
|
||||
}
|
||||
|
||||
assert(Hashtable_get(this->processTable, pid) == NULL); (void)pid;
|
||||
assert(Hashtable_count(this->processTable) == Vector_count(this->processes));
|
||||
assert(Hashtable_get(this->processTable, pid) == NULL);
|
||||
assert(Vector_countEquals(this->processes, Hashtable_count(this->processTable)));
|
||||
}
|
||||
|
||||
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.
|
||||
//
|
||||
// Algorithm
|
||||
//
|
||||
// The algorithm is based on `depth-first search`,
|
||||
// even though `breadth-first search` approach may be more efficient on first glance,
|
||||
// after comparison it may be not, as it's not safe to go deeper without first updating the tree structure.
|
||||
// If it would be safe that approach would likely bring an advantage in performance.
|
||||
//
|
||||
// Each call of the function looks for a 'layer'. A 'layer' is a list of processes with the same depth.
|
||||
// First it sorts a list. Then it runs the function recursively for each element of the sorted list.
|
||||
// After that it updates the settings of processes.
|
||||
//
|
||||
// It relies on `leftBound` and `rightBound` as an optimization to cut the list size at the time it builds a 'layer'.
|
||||
//
|
||||
// It uses a temporary Hashtable `draftingTreeSet` because it's not safe to traverse a tree
|
||||
// and at the same time make changes in it.
|
||||
//
|
||||
static void ProcessList_updateTreeSetLayer(ProcessList* this, unsigned int leftBound, unsigned int rightBound, unsigned int deep, unsigned int left, unsigned int right, unsigned int* index, unsigned int* treeIndex, int indent) {
|
||||
|
||||
// It's guaranteed that layer_size is enough space
|
||||
// but most likely it needs less. Specifically on first iteration.
|
||||
int layerSize = (right - left) / 2;
|
||||
|
||||
// check if we reach `children` of `leaves`
|
||||
if (layerSize == 0)
|
||||
static void ProcessList_buildTreeBranch(ProcessList* this, pid_t pid, int level, int indent, bool show) {
|
||||
// On OpenBSD the kernel thread 'swapper' has pid 0.
|
||||
// Do not treat it as root of any tree.
|
||||
if (pid == 0)
|
||||
return;
|
||||
|
||||
Vector* layer = Vector_new(this->processes->type, false, layerSize);
|
||||
|
||||
// Find all processes on the same layer (process with the same `deep` value
|
||||
// and included in a range from `leftBound` to `rightBound`).
|
||||
//
|
||||
// This loop also keeps track of left_bound and right_bound of these processes
|
||||
// in order not to lose this information once the list is sorted.
|
||||
//
|
||||
// The variables left_bound and right_bound are different from what the values lhs and rhs represent.
|
||||
// While left_bound and right_bound define a range of processes to look at, the values given by lhs and rhs are indices into an array
|
||||
//
|
||||
// In the below example note how filtering a range of indices i is different from filtering for processes in the bounds left_bound < x < right_bound …
|
||||
//
|
||||
// The nested tree set is sorted by left value, which is guaranteed upon entry/exit of this function.
|
||||
//
|
||||
// i | l | r
|
||||
// 1 | 1 | 9
|
||||
// 2 | 2 | 8
|
||||
// 3 | 4 | 5
|
||||
// 4 | 6 | 7
|
||||
for (unsigned int i = leftBound; i < rightBound; i++) {
|
||||
Process* proc = (Process*)Hashtable_get(this->displayTreeSet, i);
|
||||
assert(proc);
|
||||
if (proc && proc->tree_depth == deep && proc->tree_left > left && proc->tree_right < right) {
|
||||
if (Vector_size(layer) > 0) {
|
||||
Process* previous_process = (Process*)Vector_get(layer, Vector_size(layer) - 1);
|
||||
|
||||
// Make a 'right_bound' of previous_process in a layer the current process's index.
|
||||
//
|
||||
// Use 'tree_depth' as a temporal variable.
|
||||
// It's safe to do as later 'tree_depth' will be renovated.
|
||||
previous_process->tree_depth = proc->tree_index;
|
||||
}
|
||||
|
||||
Vector_add(layer, proc);
|
||||
}
|
||||
}
|
||||
|
||||
// The loop above changes just up to process-1.
|
||||
// So the last process of the layer isn't updated by the above code.
|
||||
//
|
||||
// Thus, if present, set the `rightBound` to the last process on the layer
|
||||
if (Vector_size(layer) > 0) {
|
||||
Process* previous_process = (Process*)Vector_get(layer, Vector_size(layer) - 1);
|
||||
previous_process->tree_depth = rightBound;
|
||||
}
|
||||
|
||||
Vector_quickSort(layer);
|
||||
|
||||
int size = Vector_size(layer);
|
||||
for (int i = 0; i < size; i++) {
|
||||
Process* proc = (Process*)Vector_get(layer, i);
|
||||
|
||||
unsigned int idx = (*index)++;
|
||||
int newLeft = (*treeIndex)++;
|
||||
|
||||
int level = deep == 0 ? 0 : (int)deep - 1;
|
||||
int currentIndent = indent == -1 ? 0 : indent | (1 << level);
|
||||
int nextIndent = indent == -1 ? 0 : ((i < size - 1) ? currentIndent : indent);
|
||||
|
||||
unsigned int newLeftBound = proc->tree_index;
|
||||
unsigned int newRightBound = proc->tree_depth;
|
||||
ProcessList_updateTreeSetLayer(this, newLeftBound, newRightBound, deep + 1, proc->tree_left, proc->tree_right, index, treeIndex, nextIndent);
|
||||
|
||||
int newRight = (*treeIndex)++;
|
||||
|
||||
proc->tree_left = newLeft;
|
||||
proc->tree_right = newRight;
|
||||
proc->tree_index = idx;
|
||||
proc->tree_depth = deep;
|
||||
|
||||
if (indent == -1) {
|
||||
proc->indent = 0;
|
||||
} else if (i == size - 1) {
|
||||
proc->indent = -currentIndent;
|
||||
// The vector is sorted by parent PID, find the start of the range by bisection
|
||||
int vsize = Vector_size(this->processes);
|
||||
int l = 0;
|
||||
int r = vsize;
|
||||
while (l < r) {
|
||||
int c = (l + r) / 2;
|
||||
Process* process = (Process*)Vector_get(this->processes, c);
|
||||
pid_t ppid = process->isRoot ? 0 : Process_getParentPid(process);
|
||||
if (ppid < pid) {
|
||||
l = c + 1;
|
||||
} else {
|
||||
proc->indent = currentIndent;
|
||||
r = c;
|
||||
}
|
||||
}
|
||||
// Find the end to know the last line for indent handling purposes
|
||||
int lastShown = r;
|
||||
while (r < vsize) {
|
||||
Process* process = (Process*)Vector_get(this->processes, r);
|
||||
if (!Process_isChildOf(process, pid))
|
||||
break;
|
||||
if (process->show)
|
||||
lastShown = r;
|
||||
r++;
|
||||
}
|
||||
|
||||
Hashtable_put(this->draftingTreeSet, proc->tree_index, proc);
|
||||
|
||||
// It's not strictly necessary to do this, but doing so anyways
|
||||
// allows for checking the correctness of the inner workings.
|
||||
Hashtable_remove(this->displayTreeSet, newLeftBound);
|
||||
}
|
||||
|
||||
Vector_delete(layer);
|
||||
}
|
||||
|
||||
static void ProcessList_updateTreeSet(ProcessList* this) {
|
||||
unsigned int index = 0;
|
||||
unsigned int tree_index = 1;
|
||||
|
||||
const int vsize = Vector_size(this->processes);
|
||||
|
||||
assert(Hashtable_count(this->draftingTreeSet) == 0);
|
||||
assert((int)Hashtable_count(this->displayTreeSet) == vsize);
|
||||
|
||||
ProcessList_updateTreeSetLayer(this, 0, vsize, 0, 0, vsize * 2 + 1, &index, &tree_index, -1);
|
||||
|
||||
Hashtable* tmp = this->draftingTreeSet;
|
||||
this->draftingTreeSet = this->displayTreeSet;
|
||||
this->displayTreeSet = tmp;
|
||||
|
||||
assert(Hashtable_count(this->draftingTreeSet) == 0);
|
||||
assert((int)Hashtable_count(this->displayTreeSet) == vsize);
|
||||
}
|
||||
|
||||
static void ProcessList_buildTreeBranch(ProcessList* this, pid_t pid, int level, int indent, int direction, bool show, int* node_counter, int* node_index) {
|
||||
Vector* children = Vector_new(Class(Process), false, DEFAULT_SIZE);
|
||||
|
||||
for (int i = Vector_size(this->processes) - 1; i >= 0; i--) {
|
||||
for (int i = l; i < r; i++) {
|
||||
Process* process = (Process*)Vector_get(this->processes, i);
|
||||
if (process->show && Process_isChildOf(process, pid)) {
|
||||
process = (Process*)Vector_take(this->processes, i);
|
||||
Vector_add(children, process);
|
||||
}
|
||||
}
|
||||
|
||||
int size = Vector_size(children);
|
||||
for (int i = 0; i < size; i++) {
|
||||
int index = (*node_index)++;
|
||||
Process* process = (Process*)Vector_get(children, i);
|
||||
|
||||
int lft = (*node_counter)++;
|
||||
|
||||
if (!show) {
|
||||
process->show = false;
|
||||
}
|
||||
|
||||
int s = Vector_size(this->processes2);
|
||||
if (direction == 1) {
|
||||
Vector_add(this->processes2, process);
|
||||
} else {
|
||||
Vector_insert(this->processes2, 0, process);
|
||||
}
|
||||
|
||||
assert(Vector_size(this->processes2) == s + 1); (void)s;
|
||||
Vector_add(this->displayList, process);
|
||||
|
||||
int nextIndent = indent | (1 << level);
|
||||
ProcessList_buildTreeBranch(this, process->pid, level + 1, (i < size - 1) ? nextIndent : indent, direction, show ? process->showChildren : false, node_counter, node_index);
|
||||
if (i == size - 1) {
|
||||
ProcessList_buildTreeBranch(this, process->pid, level + 1, (i < lastShown) ? nextIndent : indent, process->show && process->showChildren);
|
||||
if (i == lastShown) {
|
||||
process->indent = -nextIndent;
|
||||
} else {
|
||||
process->indent = nextIndent;
|
||||
}
|
||||
|
||||
int rht = (*node_counter)++;
|
||||
|
||||
process->tree_left = lft;
|
||||
process->tree_right = rht;
|
||||
process->tree_depth = level + 1;
|
||||
process->tree_index = index;
|
||||
Hashtable_put(this->displayTreeSet, index, process);
|
||||
}
|
||||
Vector_delete(children);
|
||||
}
|
||||
|
||||
static long ProcessList_treeProcessCompare(const void* v1, const void* v2) {
|
||||
static int compareProcessByKnownParentThenNatural(const void* v1, const void* v2) {
|
||||
const Process* p1 = (const Process*)v1;
|
||||
const Process* p2 = (const Process*)v2;
|
||||
|
||||
return SPACESHIP_NUMBER(p1->tree_left, p2->tree_left);
|
||||
}
|
||||
int result = SPACESHIP_NUMBER(
|
||||
p1->isRoot ? 0 : Process_getParentPid(p1),
|
||||
p2->isRoot ? 0 : Process_getParentPid(p2)
|
||||
);
|
||||
|
||||
static long ProcessList_treeProcessCompareByPID(const void* v1, const void* v2) {
|
||||
const Process *p1 = (const Process*)v1;
|
||||
const Process *p2 = (const Process*)v2;
|
||||
if (result != 0)
|
||||
return result;
|
||||
|
||||
return SPACESHIP_NUMBER(p1->pid, p2->pid);
|
||||
return Process_compare(v1, v2);
|
||||
}
|
||||
|
||||
// Builds a sorted tree from scratch, without relying on previously gathered information
|
||||
static void ProcessList_buildTree(ProcessList* this) {
|
||||
int node_counter = 1;
|
||||
int node_index = 0;
|
||||
int direction = Settings_getActiveDirection(this->settings);
|
||||
Vector_prune(this->displayList);
|
||||
|
||||
// Sort by PID
|
||||
Vector_quickSortCustomCompare(this->processes, ProcessList_treeProcessCompareByPID);
|
||||
// Mark root processes
|
||||
int vsize = Vector_size(this->processes);
|
||||
|
||||
// Find all processes whose parent is not visible
|
||||
int size;
|
||||
while ((size = Vector_size(this->processes))) {
|
||||
int i;
|
||||
for (i = 0; i < size; i++) {
|
||||
for (int i = 0; i < vsize; i++) {
|
||||
Process* process = (Process*)Vector_get(this->processes, i);
|
||||
|
||||
// Immediately consume processes hidden from view
|
||||
if (!process->show) {
|
||||
process = (Process*)Vector_take(this->processes, i);
|
||||
process->indent = 0;
|
||||
process->tree_depth = 0;
|
||||
process->tree_left = node_counter++;
|
||||
process->tree_index = node_index++;
|
||||
Vector_add(this->processes2, process);
|
||||
ProcessList_buildTreeBranch(this, process->pid, 0, 0, direction, false, &node_counter, &node_index);
|
||||
process->tree_right = node_counter++;
|
||||
Hashtable_put(this->displayTreeSet, process->tree_index, process);
|
||||
break;
|
||||
}
|
||||
|
||||
pid_t ppid = Process_getParentPid(process);
|
||||
|
||||
// Bisect the process vector to find parent
|
||||
int l = 0;
|
||||
int r = size;
|
||||
process->isRoot = false;
|
||||
|
||||
// If PID corresponds with PPID (e.g. "kernel_task" (PID:0, PPID:0)
|
||||
// on Mac OS X 10.11.6) cancel bisecting and regard this process as
|
||||
// root.
|
||||
if (process->pid == ppid)
|
||||
r = 0;
|
||||
// on Mac OS X 10.11.6) regard this process as root.
|
||||
if (process->pid == ppid) {
|
||||
process->isRoot = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// On Linux both the init process (pid 1) and the root UMH kernel thread (pid 2)
|
||||
// use a ppid of 0. As that PID can't exist, we can skip searching for it.
|
||||
if (!ppid)
|
||||
r = 0;
|
||||
if (!ppid) {
|
||||
process->isRoot = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
while (l < r) {
|
||||
int c = (l + r) / 2;
|
||||
pid_t pid = ((Process*)Vector_get(this->processes, c))->pid;
|
||||
if (ppid == pid) {
|
||||
break;
|
||||
} else if (ppid < pid) {
|
||||
r = c;
|
||||
} else {
|
||||
l = c + 1;
|
||||
}
|
||||
// We don't know about its parent for whatever reason
|
||||
if (ProcessList_findProcess(this, ppid) == NULL)
|
||||
process->isRoot = true;
|
||||
}
|
||||
|
||||
// Sort by known parent PID (roots first), then PID
|
||||
Vector_quickSortCustomCompare(this->processes, compareProcessByKnownParentThenNatural);
|
||||
|
||||
// Find all processes whose parent is not visible
|
||||
for (int i = 0; i < vsize; i++) {
|
||||
Process* process = (Process*)Vector_get(this->processes, i);
|
||||
|
||||
// If parent not found, then construct the tree with this node as root
|
||||
if (l >= r) {
|
||||
process = (Process*)Vector_take(this->processes, i);
|
||||
if (process->isRoot) {
|
||||
process = (Process*)Vector_get(this->processes, i);
|
||||
process->indent = 0;
|
||||
process->tree_depth = 0;
|
||||
process->tree_left = node_counter++;
|
||||
process->tree_index = node_index++;
|
||||
Vector_add(this->processes2, process);
|
||||
Hashtable_put(this->displayTreeSet, process->tree_index, process);
|
||||
ProcessList_buildTreeBranch(this, process->pid, 0, 0, direction, process->showChildren, &node_counter, &node_index);
|
||||
process->tree_right = node_counter++;
|
||||
break;
|
||||
Vector_add(this->displayList, process);
|
||||
ProcessList_buildTreeBranch(this, process->pid, 0, 0, process->showChildren);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// There should be no loop in the process tree
|
||||
assert(i < size);
|
||||
}
|
||||
|
||||
// Swap listings around
|
||||
Vector* t = this->processes;
|
||||
this->processes = this->processes2;
|
||||
this->processes2 = t;
|
||||
this->needsSort = false;
|
||||
|
||||
// Check consistency of the built structures
|
||||
assert(Vector_size(this->processes) == vsize); (void)vsize;
|
||||
assert(Vector_size(this->processes2) == 0);
|
||||
assert(Vector_size(this->displayList) == vsize); (void)vsize;
|
||||
}
|
||||
|
||||
void ProcessList_sort(ProcessList* this) {
|
||||
if (this->settings->treeView) {
|
||||
ProcessList_updateTreeSet(this);
|
||||
Vector_quickSortCustomCompare(this->processes, ProcessList_treeProcessCompare);
|
||||
void ProcessList_updateDisplayList(ProcessList* this) {
|
||||
if (this->settings->ss->treeView) {
|
||||
if (this->needsSort)
|
||||
ProcessList_buildTree(this);
|
||||
} else {
|
||||
if (this->needsSort)
|
||||
Vector_insertionSort(this->processes);
|
||||
Vector_prune(this->displayList);
|
||||
int size = Vector_size(this->processes);
|
||||
for (int i = 0; i < size; i++)
|
||||
Vector_add(this->displayList, Vector_get(this->processes, i));
|
||||
}
|
||||
this->needsSort = false;
|
||||
}
|
||||
|
||||
ProcessField ProcessList_keyAt(const ProcessList* this, int at) {
|
||||
int x = 0;
|
||||
const ProcessField* fields = this->settings->fields;
|
||||
const ProcessField* fields = this->settings->ss->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;
|
||||
}
|
||||
@ -482,51 +357,94 @@ void ProcessList_expandTree(ProcessList* this) {
|
||||
}
|
||||
}
|
||||
|
||||
// Called on collapse-all toggle and on startup, possibly in non-tree mode
|
||||
void ProcessList_collapseAllBranches(ProcessList* this) {
|
||||
ProcessList_buildTree(this); // Update `tree_depth` fields of the processes
|
||||
this->needsSort = true; // ProcessList is sorted by parent now, force new sort
|
||||
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) {
|
||||
ProcessList_updateDisplayList(this);
|
||||
|
||||
const char* incFilter = this->incFilter;
|
||||
|
||||
int currPos = Panel_getSelectedIndex(this->panel);
|
||||
pid_t currPid = this->following != -1 ? this->following : 0;
|
||||
int currScrollV = this->panel->scrollV;
|
||||
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->displayList);
|
||||
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->displayList, i);
|
||||
|
||||
if ( (!p->show)
|
||||
|| (this->userId != (uid_t) -1 && (p->st_uid != this->userId))
|
||||
|| (incFilter && !(String_contains_i(Process_getCommand(p), incFilter)))
|
||||
|| (incFilter && !(String_contains_i(Process_getCommand(p), incFilter, true)))
|
||||
|| (this->pidMatchList && !Hashtable_get(this->pidMatchList, p->tgid)) )
|
||||
continue;
|
||||
|
||||
Panel_set(this->panel, idx, (Object*)p);
|
||||
if ((this->following == -1 && idx == currPos) || (this->following != -1 && p->pid == currPid)) {
|
||||
|
||||
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(Vector_indexOf(this->processes, proc, Process_pidEqualCompare) != -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);
|
||||
@ -546,48 +464,48 @@ void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate) {
|
||||
this->kernelThreads = 0;
|
||||
this->runningTasks = 0;
|
||||
|
||||
Process_resetFieldWidths();
|
||||
|
||||
// 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);
|
||||
|
||||
uid_t maxUid = 0;
|
||||
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);
|
||||
|
||||
// keep track of the highest UID for column scaling
|
||||
if (p->st_uid > maxUid)
|
||||
maxUid = p->st_uid;
|
||||
|
||||
if (p->tombStampMs > 0) {
|
||||
// remove tombed process
|
||||
if (this->scanTs >= p->tombTs) {
|
||||
ProcessList_remove(this, p);
|
||||
if (this->monotonicMs >= p->tombStampMs) {
|
||||
ProcessList_removeIndex(this, p, i);
|
||||
}
|
||||
} 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);
|
||||
ProcessList_removeIndex(this, p, i);
|
||||
}
|
||||
} else {
|
||||
p->updated = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (this->settings->treeView) {
|
||||
// Clear out the hashtable to avoid any left-over processes from previous build
|
||||
//
|
||||
// The sorting algorithm relies on the fact that
|
||||
// len(this->displayTreeSet) == len(this->processes)
|
||||
Hashtable_clear(this->displayTreeSet);
|
||||
// Compact the processes vector in case of any deletions
|
||||
Vector_compact(this->processes);
|
||||
|
||||
ProcessList_buildTree(this);
|
||||
}
|
||||
// Set UID column width based on max UID.
|
||||
Process_setUidColumnWidth(maxUid);
|
||||
}
|
||||
|
@ -3,13 +3,16 @@
|
||||
/*
|
||||
htop - ProcessList.h
|
||||
(C) 2004,2005 Hisham H. Muhammad
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
#include "config.h" // IWYU pragma: keep
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "Hashtable.h"
|
||||
@ -34,16 +37,26 @@ 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;
|
||||
|
||||
Vector* processes;
|
||||
Vector* processes2;
|
||||
Hashtable* processTable;
|
||||
Vector* processes; /* all known processes; sort order can vary and differ from display order */
|
||||
Vector* displayList; /* process tree flattened in display order (borrowed);
|
||||
updated in ProcessList_updateDisplayList when rebuilding panel */
|
||||
Hashtable* processTable; /* fast known process lookup by PID */
|
||||
UsersTable* usersTable;
|
||||
|
||||
Hashtable* displayTreeSet;
|
||||
Hashtable* draftingTreeSet;
|
||||
bool needsSort;
|
||||
|
||||
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;
|
||||
@ -56,55 +69,59 @@ 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_sort(ProcessList* this);
|
||||
void ProcessList_updateDisplayList(ProcessList* this);
|
||||
|
||||
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
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
htop - ProcessLocksScreen.c
|
||||
(C) 2020 htop dev team
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
@ -27,7 +27,7 @@ ProcessLocksScreen* ProcessLocksScreen_new(const Process* process) {
|
||||
this->pid = process->tgid;
|
||||
else
|
||||
this->pid = process->pid;
|
||||
return (ProcessLocksScreen*) InfoScreen_init(&this->super, process, NULL, LINES-3, " ID TYPE EXCLUSION READ/WRITE DEVICE:INODE START END FILENAME");
|
||||
return (ProcessLocksScreen*) InfoScreen_init(&this->super, process, NULL, LINES - 2, " ID TYPE EXCLUSION READ/WRITE DEVICE:INODE START END FILENAME");
|
||||
}
|
||||
|
||||
void ProcessLocksScreen_delete(Object* this) {
|
||||
|
@ -3,7 +3,7 @@
|
||||
/*
|
||||
htop - ProcessLocksScreen.h
|
||||
(C) 2020 htop dev team
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
/*
|
||||
htop - RichString.h
|
||||
(C) 2004,2011 Hisham H. Muhammad
|
||||
Released under the GNU GPL, see the COPYING file
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
@ -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>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user