mirror of
https://github.com/xzeldon/htop.git
synced 2025-07-15 21:44:36 +03:00
Compare commits
787 Commits
Author | SHA1 | Date | |
---|---|---|---|
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 | |||
0b989ee38c | |||
3fb0024fd3 | |||
dfb9b82607 | |||
fc7aead36b | |||
737cd6167a | |||
6502b02666 | |||
cdfd407e2e | |||
64c05a1ed5 | |||
a7612b0b7d | |||
3ec8f67ab2 | |||
10c6810bff | |||
068561351f | |||
9b8b380c32 | |||
a09ad6b8b4 | |||
9a86577cf2 | |||
8db8b9edac | |||
4a73e80338 | |||
5fa1c7040d | |||
3f9c63d5c0 | |||
358d20687f | |||
e3862aa67e | |||
7e7a53c415 | |||
6b100b0cf4 | |||
6e46fd6f1f | |||
22da57d621 | |||
c5e31ba4aa | |||
f878f302ca | |||
67ccd6b909 | |||
f614b8a19f | |||
c150e4bde9 | |||
9f68c8d341 | |||
89473cc9ae | |||
d872e36308 | |||
77db240b48 | |||
2327260ee8 | |||
e8c6994f40 | |||
3d1703f16f | |||
52fa4e7ee4 | |||
27b8d81ed2 | |||
26993d2d2b | |||
0401df8cbd | |||
0cb257586a | |||
1193c6e349 | |||
edd6130be7 | |||
107e3c8aa5 | |||
4eeeb63647 | |||
eb36385a6b | |||
79970f05f3 | |||
61b8e31b41 | |||
c9583c692d | |||
4507911cc3 | |||
b7836515e8 | |||
a3db2da4a7 | |||
cf982f2928 | |||
8d69a9a53e | |||
366b78edd9 | |||
f8a610e6e1 | |||
4b1a4a4ebd | |||
3655b6ca0b | |||
1506283aff | |||
4b877eb16a | |||
f32f0188cd | |||
e65cdf947c | |||
ab60f59ed8 | |||
8149823d56 | |||
12421f460a | |||
880eecabf5 | |||
738d31b903 | |||
28bc087d8a | |||
2700d99069 | |||
75e9f9a8d9 | |||
db5687a355 | |||
7b739b6292 | |||
ded9c5d363 | |||
2d231d77ca | |||
f6613db5cd | |||
4c44a70f96 | |||
157086e750 | |||
5506925b34 | |||
c6d9fa279b | |||
dcf7ad386c | |||
30bf212185 | |||
05969998c1 | |||
ead978bce6 | |||
4f88d38256 | |||
f03f48a0fb | |||
ad8aa2ce77 | |||
b92cfa7d7a | |||
57d9ecc551 | |||
ad764ff972 | |||
77ec86aff4 | |||
e1ce141bc3 | |||
d9224c66a4 | |||
3d15ba5197 | |||
7ba25aa3c4 | |||
22f8f8000c | |||
4c4ba9d949 | |||
8d1595a20e | |||
876194492f | |||
5f528b7455 | |||
641fd2c4ad | |||
f913680020 | |||
f0a9dfc37e | |||
1e9b184367 | |||
ba1549f99b | |||
f61e74a4af | |||
8029e9af04 | |||
ef0fc7129e | |||
bc16fa037f | |||
cc7f16bb8f |
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1 @@
|
||||
open_collective: htop
|
103
.github/workflows/ci.yml
vendored
103
.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,142 @@ 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
|
||||
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
|
||||
# Until Ubuntu catches up with pcp-5.2.3+, cannot use -werror due to:
|
||||
# passing argument 2 of ‘pmLookupName’ from incompatible pointer type
|
||||
run: ./configure --enable-pcp --enable-unicode
|
||||
- name: Build
|
||||
run: make -k
|
||||
|
||||
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
|
||||
env:
|
||||
CC: clang
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Dependencies
|
||||
run: brew install automake
|
||||
- name: Bootstrap
|
||||
run: ./autogen.sh
|
||||
- name: Configure
|
||||
run: ./configure --enable-werror
|
||||
- name: Build
|
||||
run: make -k
|
||||
- name: Distcheck
|
||||
run: make distcheck DISTCHECK_CONFIGURE_FLAGS="--enable-werror"
|
||||
|
||||
whitespace_check:
|
||||
runs-on: ubuntu-latest
|
||||
|
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/
|
||||
|
18
.travis.yml
18
.travis.yml
@ -6,21 +6,11 @@ compiler:
|
||||
|
||||
os:
|
||||
- freebsd
|
||||
- linux
|
||||
- osx
|
||||
|
||||
arch:
|
||||
- amd64
|
||||
- s390x
|
||||
|
||||
before_script:
|
||||
if [[ ${TRAVIS_CPU_ARCH} == 's390x' ]]; then
|
||||
sudo apt-get update && sudo apt-get install -y libncursesw5-dev ;
|
||||
fi
|
||||
|
||||
script:
|
||||
- ./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
|
||||
|
10
AUTHORS
10
AUTHORS
@ -1 +1,11 @@
|
||||
Originally authored by:
|
||||
Hisham H. Muhammad
|
||||
|
||||
Currently maintained by the htop dev team:
|
||||
Benny Baumann
|
||||
Christian Göttsche
|
||||
Daniel Lange
|
||||
Nathan Scott
|
||||
|
||||
For the full list of contributors see:
|
||||
git log --format="%aN" | sort -u
|
||||
|
417
Action.c
417
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,26 +35,25 @@ in the source distribution for its full text.
|
||||
#include "Vector.h"
|
||||
#include "XUtils.h"
|
||||
|
||||
#if (defined(HAVE_LIBHWLOC) || defined(HAVE_LINUX_AFFINITY))
|
||||
#if (defined(HAVE_LIBHWLOC) || defined(HAVE_AFFINITY))
|
||||
#include "Affinity.h"
|
||||
#include "AffinityPanel.h"
|
||||
#endif
|
||||
|
||||
|
||||
Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess) {
|
||||
Panel* panel = st->panel;
|
||||
MainPanel* mainPanel = st->mainPanel;
|
||||
Header* header = st->header;
|
||||
Settings* settings = st->settings;
|
||||
|
||||
int y = panel->y;
|
||||
ScreenManager* scr = ScreenManager_new(header, settings, st, false);
|
||||
int y = ((Panel*)mainPanel)->y;
|
||||
ScreenManager* scr = ScreenManager_new(header, st->settings, st, false);
|
||||
scr->allowFocusChange = false;
|
||||
ScreenManager_add(scr, list, x - 1);
|
||||
ScreenManager_add(scr, panel, -1);
|
||||
ScreenManager_add(scr, list, x);
|
||||
ScreenManager_add(scr, (Panel*)mainPanel, -1);
|
||||
Panel* panelFocus;
|
||||
int ch;
|
||||
bool unfollow = false;
|
||||
int pid = followProcess ? MainPanel_selectedPid((MainPanel*)panel) : -1;
|
||||
int pid = followProcess ? MainPanel_selectedPid(mainPanel) : -1;
|
||||
if (followProcess && header->pl->following == -1) {
|
||||
header->pl->following = pid;
|
||||
unfollow = true;
|
||||
@ -63,11 +63,11 @@ Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess)
|
||||
header->pl->following = -1;
|
||||
}
|
||||
ScreenManager_delete(scr);
|
||||
Panel_move(panel, 0, y);
|
||||
Panel_resize(panel, COLS, LINES - y - 1);
|
||||
Panel_move((Panel*)mainPanel, 0, y);
|
||||
Panel_resize((Panel*)mainPanel, COLS, LINES - y - 1);
|
||||
if (panelFocus == list && ch == 13) {
|
||||
if (followProcess) {
|
||||
Process* selected = (Process*)Panel_getSelected(panel);
|
||||
const Process* selected = (const Process*)Panel_getSelected((Panel*)mainPanel);
|
||||
if (selected && selected->pid == pid)
|
||||
return Panel_getSelected(list);
|
||||
|
||||
@ -84,12 +84,8 @@ Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess)
|
||||
|
||||
static void Action_runSetup(State* st) {
|
||||
ScreenManager* scr = ScreenManager_new(st->header, st->settings, st, true);
|
||||
CategoriesPanel* panelCategories = CategoriesPanel_new(scr, st->settings, st->header, st->pl);
|
||||
ScreenManager_add(scr, (Panel*) panelCategories, 16);
|
||||
CategoriesPanel_makeMetersPage(panelCategories);
|
||||
Panel* panelFocus;
|
||||
int ch;
|
||||
ScreenManager_run(scr, &panelFocus, &ch);
|
||||
CategoriesPanel_new(scr, st->settings, st->header, st->pl);
|
||||
ScreenManager_run(scr, NULL, NULL);
|
||||
ScreenManager_delete(scr);
|
||||
if (st->settings->changed) {
|
||||
Header_writeBackToSettings(st->header);
|
||||
@ -104,7 +100,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 +137,7 @@ static bool expandCollapse(Panel* panel) {
|
||||
}
|
||||
|
||||
static bool collapseIntoParent(Panel* panel) {
|
||||
Process* p = (Process*) Panel_getSelected(panel);
|
||||
const Process* p = (Process*) Panel_getSelected(panel);
|
||||
if (!p)
|
||||
return false;
|
||||
|
||||
@ -158,25 +154,35 @@ static bool collapseIntoParent(Panel* panel) {
|
||||
}
|
||||
|
||||
Htop_Reaction Action_setSortKey(Settings* settings, ProcessField sortKey) {
|
||||
settings->sortKey = sortKey;
|
||||
settings->direction = 1;
|
||||
Settings_setSortKey(settings, 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 ProcessField* fields = st->settings->fields;
|
||||
Hashtable* dynamicColumns = st->settings->dynamicColumns;
|
||||
for (int i = 0; fields[i]; i++) {
|
||||
char* name = String_trim(Process_fields[fields[i]].name);
|
||||
char* name = NULL;
|
||||
if (fields[i] >= LAST_PROCESSFIELD) {
|
||||
DynamicColumn* column = Hashtable_get(dynamicColumns, fields[i]);
|
||||
if (!column)
|
||||
continue;
|
||||
name = xStrdup(column->caption ? column->caption : column->name);
|
||||
} else {
|
||||
name = String_trim(Process_fields[fields[i]].name);
|
||||
}
|
||||
Panel_add(sortPanel, (Object*) ListItem_new(name, fields[i]));
|
||||
if (fields[i] == st->settings->sortKey)
|
||||
if (fields[i] == Settings_getActiveSortKey(st->settings))
|
||||
Panel_setSelected(sortPanel, i);
|
||||
|
||||
free(name);
|
||||
}
|
||||
ListItem* field = (ListItem*) Action_pickFromVector(st, sortPanel, 15, false);
|
||||
const ListItem* field = (const ListItem*) Action_pickFromVector(st, sortPanel, 14, false);
|
||||
if (field) {
|
||||
reaction |= Action_setSortKey(st->settings, field->key);
|
||||
}
|
||||
@ -188,12 +194,8 @@ static Htop_Reaction sortBy(State* st) {
|
||||
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) {
|
||||
@ -210,12 +212,12 @@ static Htop_Reaction actionSortByTime(State* st) {
|
||||
|
||||
static Htop_Reaction actionToggleKernelThreads(State* st) {
|
||||
st->settings->hideKernelThreads = !st->settings->hideKernelThreads;
|
||||
return HTOP_RECALCULATE | HTOP_SAVE_SETTINGS;
|
||||
return HTOP_RECALCULATE | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING;
|
||||
}
|
||||
|
||||
static Htop_Reaction actionToggleUserlandThreads(State* st) {
|
||||
st->settings->hideUserlandThreads = !st->settings->hideUserlandThreads;
|
||||
return HTOP_RECALCULATE | HTOP_SAVE_SETTINGS;
|
||||
return HTOP_RECALCULATE | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING;
|
||||
}
|
||||
|
||||
static Htop_Reaction actionToggleProgramPath(State* st) {
|
||||
@ -230,58 +232,59 @@ static Htop_Reaction actionToggleMergedCommand(State* st) {
|
||||
|
||||
static Htop_Reaction actionToggleTreeView(State* st) {
|
||||
st->settings->treeView = !st->settings->treeView;
|
||||
if (st->settings->treeView) {
|
||||
st->settings->direction = 1;
|
||||
}
|
||||
|
||||
if (!st->settings->allBranchesCollapsed)
|
||||
ProcessList_expandTree(st->pl);
|
||||
return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
|
||||
}
|
||||
|
||||
static Htop_Reaction actionExpandOrCollapseAllBranches(State* st) {
|
||||
st->settings->allBranchesCollapsed = !st->settings->allBranchesCollapsed;
|
||||
if (st->settings->allBranchesCollapsed)
|
||||
ProcessList_collapseAllBranches(st->pl);
|
||||
else
|
||||
ProcessList_expandTree(st->pl);
|
||||
return HTOP_REFRESH | HTOP_SAVE_SETTINGS;
|
||||
}
|
||||
|
||||
static Htop_Reaction actionIncFilter(State* st) {
|
||||
IncSet* inc = ((MainPanel*)st->panel)->inc;
|
||||
IncSet_activate(inc, INC_FILTER, st->panel);
|
||||
IncSet* inc = (st->mainPanel)->inc;
|
||||
IncSet_activate(inc, INC_FILTER, (Panel*)st->mainPanel);
|
||||
st->pl->incFilter = IncSet_filter(inc);
|
||||
return HTOP_REFRESH | HTOP_KEEP_FOLLOWING;
|
||||
}
|
||||
|
||||
static Htop_Reaction actionIncSearch(State* st) {
|
||||
IncSet_reset(((MainPanel*)st->panel)->inc, INC_SEARCH);
|
||||
IncSet_activate(((MainPanel*)st->panel)->inc, INC_SEARCH, st->panel);
|
||||
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);
|
||||
return HTOP_REFRESH | HTOP_SAVE_SETTINGS;
|
||||
}
|
||||
|
||||
static Htop_Reaction actionSetSortColumn(State* st) {
|
||||
return sortBy(st);
|
||||
if (st->pauseProcessUpdate)
|
||||
ProcessList_sort(st->pl);
|
||||
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;
|
||||
}
|
||||
|
||||
@ -289,7 +292,7 @@ static Htop_Reaction actionCollapseIntoParent(State* st) {
|
||||
if (!st->settings->treeView) {
|
||||
return HTOP_OK;
|
||||
}
|
||||
bool changed = collapseIntoParent(st->panel);
|
||||
bool changed = collapseIntoParent((Panel*)st->mainPanel);
|
||||
return changed ? HTOP_RECALCULATE : HTOP_OK;
|
||||
}
|
||||
|
||||
@ -302,13 +305,14 @@ static Htop_Reaction actionQuit(ATTR_UNUSED State* st) {
|
||||
}
|
||||
|
||||
static Htop_Reaction actionSetAffinity(State* st) {
|
||||
if (st->pl->cpuCount == 1)
|
||||
if (Settings_isReadonly())
|
||||
return HTOP_OK;
|
||||
|
||||
#if (defined(HAVE_LIBHWLOC) || defined(HAVE_LINUX_AFFINITY))
|
||||
Panel* panel = st->panel;
|
||||
if (st->pl->activeCPUs == 1)
|
||||
return HTOP_OK;
|
||||
|
||||
Process* p = (Process*) Panel_getSelected(panel);
|
||||
#if (defined(HAVE_LIBHWLOC) || defined(HAVE_AFFINITY))
|
||||
const Process* p = (const Process*) Panel_getSelected((Panel*)st->mainPanel);
|
||||
if (!p)
|
||||
return HTOP_OK;
|
||||
|
||||
@ -318,46 +322,49 @@ static Htop_Reaction actionSetAffinity(State* st) {
|
||||
|
||||
int width;
|
||||
Panel* affinityPanel = AffinityPanel_new(st->pl, affinity1, &width);
|
||||
width += 1; /* we add a gap between the panels */
|
||||
Affinity_delete(affinity1);
|
||||
|
||||
void* set = Action_pickFromVector(st, affinityPanel, width, true);
|
||||
const void* set = Action_pickFromVector(st, affinityPanel, width, true);
|
||||
if (set) {
|
||||
Affinity* affinity2 = AffinityPanel_getAffinity(affinityPanel, st->pl);
|
||||
bool ok = MainPanel_foreachProcess((MainPanel*)panel, Affinity_set, (Arg) { .v = affinity2 }, NULL);
|
||||
bool ok = MainPanel_foreachProcess(st->mainPanel, Affinity_set, (Arg) { .v = affinity2 }, NULL);
|
||||
if (!ok)
|
||||
beep();
|
||||
Affinity_delete(affinity2);
|
||||
}
|
||||
Object_delete(affinityPanel);
|
||||
#endif
|
||||
return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
|
||||
#else
|
||||
return HTOP_OK;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
static Htop_Reaction actionKill(State* st) {
|
||||
if (Settings_isReadonly())
|
||||
return HTOP_OK;
|
||||
|
||||
Panel* signalsPanel = SignalsPanel_new();
|
||||
ListItem* sgn = (ListItem*) Action_pickFromVector(st, signalsPanel, 15, true);
|
||||
if (sgn) {
|
||||
if (sgn->key != 0) {
|
||||
Panel_setHeader(st->panel, "Sending...");
|
||||
Panel_draw(st->panel, true, true);
|
||||
const ListItem* sgn = (ListItem*) Action_pickFromVector(st, signalsPanel, 14, true);
|
||||
if (sgn && sgn->key != 0) {
|
||||
Panel_setHeader((Panel*)st->mainPanel, "Sending...");
|
||||
Panel_draw((Panel*)st->mainPanel, false, true, true, State_hideFunctionBar(st));
|
||||
refresh();
|
||||
MainPanel_foreachProcess((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;
|
||||
@ -370,22 +377,21 @@ static Htop_Reaction actionFilterByUser(State* st) {
|
||||
}
|
||||
|
||||
Htop_Reaction Action_follow(State* st) {
|
||||
st->pl->following = MainPanel_selectedPid((MainPanel*)st->panel);
|
||||
Panel_setSelectionColor(st->panel, CRT_colors[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;
|
||||
|
||||
@ -398,8 +404,9 @@ static Htop_Reaction actionLsof(State* st) {
|
||||
}
|
||||
|
||||
static Htop_Reaction actionShowLocks(State* st) {
|
||||
Process* p = (Process*) Panel_getSelected(st->panel);
|
||||
if (!p) return HTOP_OK;
|
||||
const Process* p = (Process*) Panel_getSelected((Panel*)st->mainPanel);
|
||||
if (!p)
|
||||
return HTOP_OK;
|
||||
ProcessLocksScreen* pls = ProcessLocksScreen_new(p);
|
||||
InfoScreen_run((InfoScreen*)pls);
|
||||
ProcessLocksScreen_delete((Object*)pls);
|
||||
@ -409,7 +416,10 @@ static Htop_Reaction actionShowLocks(State* st) {
|
||||
}
|
||||
|
||||
static Htop_Reaction actionStrace(State* st) {
|
||||
Process* p = (Process*) Panel_getSelected(st->panel);
|
||||
if (Settings_isReadonly())
|
||||
return HTOP_OK;
|
||||
|
||||
const Process* p = (Process*) Panel_getSelected((Panel*)st->mainPanel);
|
||||
if (!p)
|
||||
return HTOP_OK;
|
||||
|
||||
@ -425,12 +435,12 @@ static Htop_Reaction actionStrace(State* st) {
|
||||
}
|
||||
|
||||
static Htop_Reaction actionTag(State* st) {
|
||||
Process* p = (Process*) Panel_getSelected(st->panel);
|
||||
Process* p = (Process*) Panel_getSelected((Panel*)st->mainPanel);
|
||||
if (!p)
|
||||
return HTOP_OK;
|
||||
|
||||
Process_toggleTag(p);
|
||||
Panel_onKey(st->panel, KEY_DOWN);
|
||||
Panel_onKey((Panel*)st->mainPanel, KEY_DOWN);
|
||||
return HTOP_OK;
|
||||
}
|
||||
|
||||
@ -446,49 +456,51 @@ static Htop_Reaction actionTogglePauseProcessUpdate(State* st) {
|
||||
|
||||
static const struct {
|
||||
const char* key;
|
||||
bool roInactive;
|
||||
const char* info;
|
||||
} helpLeft[] = {
|
||||
{ .key = " Arrows: ", .info = "scroll process list" },
|
||||
{ .key = " Digits: ", .info = "incremental PID search" },
|
||||
{ .key = " F3 /: ", .info = "incremental name search" },
|
||||
{ .key = " F4 \\: ",.info = "incremental name filtering" },
|
||||
{ .key = " F5 t: ", .info = "tree view" },
|
||||
{ .key = " p: ", .info = "toggle program path" },
|
||||
{ .key = " m: ", .info = "toggle merged command" },
|
||||
{ .key = " Z: ", .info = "pause/resume process updates" },
|
||||
{ .key = " u: ", .info = "show processes of a single user" },
|
||||
{ .key = " H: ", .info = "hide/show user process threads" },
|
||||
{ .key = " K: ", .info = "hide/show kernel threads" },
|
||||
{ .key = " F: ", .info = "cursor follows process" },
|
||||
{ .key = " F6 + -: ", .info = "expand/collapse tree" },
|
||||
{ .key = " P M T: ", .info = "sort by CPU%, MEM% or TIME" },
|
||||
{ .key = " I: ", .info = "invert sort order" },
|
||||
{ .key = " F6 > .: ", .info = "select sort column" },
|
||||
{ .key = " Arrows: ", .roInactive = false, .info = "scroll process list" },
|
||||
{ .key = " Digits: ", .roInactive = false, .info = "incremental PID search" },
|
||||
{ .key = " F3 /: ", .roInactive = false, .info = "incremental name search" },
|
||||
{ .key = " F4 \\: ", .roInactive = false, .info = "incremental name filtering" },
|
||||
{ .key = " F5 t: ", .roInactive = false, .info = "tree view" },
|
||||
{ .key = " p: ", .roInactive = false, .info = "toggle program path" },
|
||||
{ .key = " m: ", .roInactive = false, .info = "toggle merged command" },
|
||||
{ .key = " Z: ", .roInactive = false, .info = "pause/resume process updates" },
|
||||
{ .key = " u: ", .roInactive = false, .info = "show processes of a single user" },
|
||||
{ .key = " H: ", .roInactive = false, .info = "hide/show user process threads" },
|
||||
{ .key = " K: ", .roInactive = false, .info = "hide/show kernel threads" },
|
||||
{ .key = " F: ", .roInactive = false, .info = "cursor follows process" },
|
||||
{ .key = " + - *: ", .roInactive = false, .info = "expand/collapse tree/toggle all" },
|
||||
{ .key = "N P M T: ", .roInactive = false, .info = "sort by PID, CPU%, MEM% or TIME" },
|
||||
{ .key = " I: ", .roInactive = false, .info = "invert sort order" },
|
||||
{ .key = " F6 > .: ", .roInactive = false, .info = "select sort column" },
|
||||
{ .key = NULL, .info = NULL }
|
||||
};
|
||||
|
||||
static const struct {
|
||||
const char* key;
|
||||
bool roInactive;
|
||||
const char* info;
|
||||
} helpRight[] = {
|
||||
{ .key = " Space: ", .info = "tag process" },
|
||||
{ .key = " c: ", .info = "tag process and its children" },
|
||||
{ .key = " U: ", .info = "untag all processes" },
|
||||
{ .key = " F9 k: ", .info = "kill process/tagged processes" },
|
||||
{ .key = " F7 ]: ", .info = "higher priority (root only)" },
|
||||
{ .key = " F8 [: ", .info = "lower priority (+ nice)" },
|
||||
#if (defined(HAVE_LIBHWLOC) || defined(HAVE_LINUX_AFFINITY))
|
||||
{ .key = " a: ", .info = "set CPU affinity" },
|
||||
{ .key = " Space: ", .roInactive = false, .info = "tag process" },
|
||||
{ .key = " c: ", .roInactive = false, .info = "tag process and its children" },
|
||||
{ .key = " U: ", .roInactive = false, .info = "untag all processes" },
|
||||
{ .key = " F9 k: ", .roInactive = true, .info = "kill process/tagged processes" },
|
||||
{ .key = " F7 ]: ", .roInactive = true, .info = "higher priority (root only)" },
|
||||
{ .key = " F8 [: ", .roInactive = false, .info = "lower priority (+ nice)" },
|
||||
#if (defined(HAVE_LIBHWLOC) || defined(HAVE_AFFINITY))
|
||||
{ .key = " a: ", .roInactive = true, .info = "set CPU affinity" },
|
||||
#endif
|
||||
{ .key = " e: ", .info = "show process environment" },
|
||||
{ .key = " i: ", .info = "set IO priority" },
|
||||
{ .key = " l: ", .info = "list open files with lsof" },
|
||||
{ .key = " x: ", .info = "list file locks of process" },
|
||||
{ .key = " s: ", .info = "trace syscalls with strace" },
|
||||
{ .key = " w: ", .info = "wrap process command in multiple lines" },
|
||||
{ .key = " F2 C S: ", .info = "setup" },
|
||||
{ .key = " F1 h: ", .info = "show this help screen" },
|
||||
{ .key = " F10 q: ", .info = "quit" },
|
||||
{ .key = " e: ", .roInactive = false, .info = "show process environment" },
|
||||
{ .key = " i: ", .roInactive = true, .info = "set IO priority" },
|
||||
{ .key = " l: ", .roInactive = true, .info = "list open files with lsof" },
|
||||
{ .key = " x: ", .roInactive = false, .info = "list file locks of process" },
|
||||
{ .key = " s: ", .roInactive = true, .info = "trace syscalls with strace" },
|
||||
{ .key = " w: ", .roInactive = false, .info = "wrap process command in multiple lines" },
|
||||
{ .key = " F2 C S: ", .roInactive = false, .info = "setup" },
|
||||
{ .key = " F1 h ?: ", .roInactive = false, .info = "show this help screen" },
|
||||
{ .key = " F10 q: ", .roInactive = false, .info = "quit" },
|
||||
{ .key = NULL, .info = NULL }
|
||||
};
|
||||
|
||||
@ -498,8 +510,6 @@ static inline void addattrstr( int attr, const char* str) {
|
||||
}
|
||||
|
||||
static Htop_Reaction actionHelp(State* st) {
|
||||
Settings* settings = st->settings;
|
||||
|
||||
clear();
|
||||
attrset(CRT_colors[HELP_BOLD]);
|
||||
|
||||
@ -509,14 +519,14 @@ 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: ");
|
||||
|
||||
addattrstr(CRT_colors[BAR_BORDER], "[");
|
||||
if (settings->detailedCPUTime) {
|
||||
if (st->settings->detailedCPUTime) {
|
||||
addattrstr(CRT_colors[CPU_NICE_TEXT], "low"); addstr("/");
|
||||
addattrstr(CRT_colors[CPU_NORMAL], "normal"); addstr("/");
|
||||
addattrstr(CRT_colors[CPU_SYSTEM], "kernel"); addstr("/");
|
||||
@ -530,7 +540,7 @@ static Htop_Reaction actionHelp(State* st) {
|
||||
addattrstr(CRT_colors[CPU_NICE_TEXT], "low-priority"); addstr("/");
|
||||
addattrstr(CRT_colors[CPU_NORMAL], "normal"); addstr("/");
|
||||
addattrstr(CRT_colors[CPU_SYSTEM], "kernel"); addstr("/");
|
||||
addattrstr(CRT_colors[CPU_GUEST], "virtualiz");
|
||||
addattrstr(CRT_colors[CPU_GUEST], "virtualized");
|
||||
addattrstr(CRT_colors[BAR_SHADOW], " used%");
|
||||
}
|
||||
addattrstr(CRT_colors[BAR_BORDER], "]");
|
||||
@ -539,6 +549,7 @@ static Htop_Reaction actionHelp(State* st) {
|
||||
addattrstr(CRT_colors[BAR_BORDER], "[");
|
||||
addattrstr(CRT_colors[MEMORY_USED], "used"); addstr("/");
|
||||
addattrstr(CRT_colors[MEMORY_BUFFERS_TEXT], "buffers"); addstr("/");
|
||||
addattrstr(CRT_colors[MEMORY_SHARED], "shared"); addstr("/");
|
||||
addattrstr(CRT_colors[MEMORY_CACHE], "cache");
|
||||
addattrstr(CRT_colors[BAR_SHADOW], " used/total");
|
||||
addattrstr(CRT_colors[BAR_BORDER], "]");
|
||||
@ -546,7 +557,13 @@ static Htop_Reaction actionHelp(State* st) {
|
||||
mvaddstr(line++, 0, "Swap bar: ");
|
||||
addattrstr(CRT_colors[BAR_BORDER], "[");
|
||||
addattrstr(CRT_colors[SWAP], "used");
|
||||
#ifdef HTOP_LINUX
|
||||
addattrstr(CRT_colors[BAR_SHADOW], "/");
|
||||
addattrstr(CRT_colors[SWAP_CACHE], "cache");
|
||||
addattrstr(CRT_colors[BAR_SHADOW], " used/total");
|
||||
#else
|
||||
addattrstr(CRT_colors[BAR_SHADOW], " used/total");
|
||||
#endif
|
||||
addattrstr(CRT_colors[BAR_BORDER], "]");
|
||||
attrset(CRT_colors[DEFAULT_COLOR]);
|
||||
mvaddstr(line++, 0, "Type and layout of header meters are configurable in the setup screen.");
|
||||
@ -559,27 +576,29 @@ static Htop_Reaction actionHelp(State* st) {
|
||||
|
||||
line++;
|
||||
|
||||
const bool readonly = Settings_isReadonly();
|
||||
|
||||
int item;
|
||||
for (item = 0; helpLeft[item].key; item++) {
|
||||
attrset(CRT_colors[DEFAULT_COLOR]);
|
||||
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++;
|
||||
@ -591,28 +610,28 @@ static Htop_Reaction actionHelp(State* st) {
|
||||
CRT_readKey();
|
||||
clear();
|
||||
|
||||
return HTOP_RECALCULATE | HTOP_REDRAW_BAR;
|
||||
return HTOP_RECALCULATE | HTOP_REDRAW_BAR | HTOP_KEEP_FOLLOWING;
|
||||
}
|
||||
|
||||
static Htop_Reaction actionUntagAll(State* st) {
|
||||
for (int i = 0; i < Panel_size(st->panel); i++) {
|
||||
Process* p = (Process*) Panel_get(st->panel, i);
|
||||
for (int i = 0; i < Panel_size((Panel*)st->mainPanel); i++) {
|
||||
Process* p = (Process*) Panel_get((Panel*)st->mainPanel, i);
|
||||
p->tag = false;
|
||||
}
|
||||
return HTOP_REFRESH;
|
||||
}
|
||||
|
||||
static Htop_Reaction actionTagAllChildren(State* st) {
|
||||
Process* p = (Process*) Panel_getSelected(st->panel);
|
||||
Process* p = (Process*) Panel_getSelected((Panel*)st->mainPanel);
|
||||
if (!p)
|
||||
return HTOP_OK;
|
||||
|
||||
tagAllChildren(st->panel, p);
|
||||
tagAllChildren((Panel*)st->mainPanel, p);
|
||||
return HTOP_OK;
|
||||
}
|
||||
|
||||
static Htop_Reaction actionShowEnvScreen(State* st) {
|
||||
Process* p = (Process*) Panel_getSelected(st->panel);
|
||||
Process* p = (Process*) Panel_getSelected((Panel*)st->mainPanel);
|
||||
if (!p)
|
||||
return HTOP_OK;
|
||||
|
||||
@ -625,7 +644,7 @@ static Htop_Reaction actionShowEnvScreen(State* st) {
|
||||
}
|
||||
|
||||
static Htop_Reaction actionShowCommandScreen(State* st) {
|
||||
Process* p = (Process*) Panel_getSelected(st->panel);
|
||||
Process* p = (Process*) Panel_getSelected((Panel*)st->mainPanel);
|
||||
if (!p)
|
||||
return HTOP_OK;
|
||||
|
||||
@ -638,60 +657,58 @@ static Htop_Reaction actionShowCommandScreen(State* st) {
|
||||
}
|
||||
|
||||
void Action_setBindings(Htop_Action* keys) {
|
||||
keys[KEY_RESIZE] = actionResize;
|
||||
keys['M'] = actionSortByMemory;
|
||||
keys['T'] = actionSortByTime;
|
||||
keys['P'] = actionSortByCPU;
|
||||
keys['H'] = actionToggleUserlandThreads;
|
||||
keys['K'] = actionToggleKernelThreads;
|
||||
keys['p'] = actionToggleProgramPath;
|
||||
keys['m'] = actionToggleMergedCommand;
|
||||
keys['t'] = actionToggleTreeView;
|
||||
keys[KEY_F(5)] = actionToggleTreeView;
|
||||
keys[KEY_F(4)] = actionIncFilter;
|
||||
keys['\\'] = actionIncFilter;
|
||||
keys[KEY_F(3)] = actionIncSearch;
|
||||
keys['/'] = actionIncSearch;
|
||||
keys['n'] = actionIncNext;
|
||||
keys['N'] = actionIncPrev;
|
||||
|
||||
keys[']'] = actionHigherPriority;
|
||||
keys[KEY_F(7)] = actionHigherPriority;
|
||||
keys['['] = actionLowerPriority;
|
||||
keys[KEY_F(8)] = actionLowerPriority;
|
||||
keys['I'] = actionInvertSortOrder;
|
||||
keys[KEY_F(6)] = actionSetSortColumn;
|
||||
keys[KEY_F(18)] = actionExpandCollapseOrSortColumn;
|
||||
keys['<'] = actionSetSortColumn;
|
||||
keys[','] = actionSetSortColumn;
|
||||
keys['>'] = actionSetSortColumn;
|
||||
keys['.'] = actionSetSortColumn;
|
||||
keys[KEY_F(10)] = actionQuit;
|
||||
keys['q'] = actionQuit;
|
||||
keys['a'] = actionSetAffinity;
|
||||
keys[KEY_F(9)] = actionKill;
|
||||
keys['k'] = actionKill;
|
||||
keys[KEY_RECLICK] = actionExpandOrCollapse;
|
||||
keys['+'] = actionExpandOrCollapse;
|
||||
keys['='] = actionExpandOrCollapse;
|
||||
keys['-'] = actionExpandOrCollapse;
|
||||
keys['\177'] = actionCollapseIntoParent;
|
||||
keys['u'] = actionFilterByUser;
|
||||
keys['F'] = Action_follow;
|
||||
keys['S'] = actionSetup;
|
||||
keys['C'] = actionSetup;
|
||||
keys[KEY_F(2)] = actionSetup;
|
||||
keys['x'] = actionShowLocks;
|
||||
keys['l'] = actionLsof;
|
||||
keys['s'] = actionStrace;
|
||||
keys[' '] = actionTag;
|
||||
keys['\014'] = actionRedraw; // Ctrl+L
|
||||
keys[KEY_F(1)] = actionHelp;
|
||||
keys['h'] = actionHelp;
|
||||
keys['*'] = actionExpandOrCollapseAllBranches;
|
||||
keys['+'] = actionExpandOrCollapse;
|
||||
keys[','] = actionSetSortColumn;
|
||||
keys['-'] = actionExpandOrCollapse;
|
||||
keys['.'] = actionSetSortColumn;
|
||||
keys['/'] = actionIncSearch;
|
||||
keys['<'] = actionSetSortColumn;
|
||||
keys['='] = actionExpandOrCollapse;
|
||||
keys['>'] = actionSetSortColumn;
|
||||
keys['?'] = actionHelp;
|
||||
keys['C'] = actionSetup;
|
||||
keys['F'] = Action_follow;
|
||||
keys['H'] = actionToggleUserlandThreads;
|
||||
keys['I'] = actionInvertSortOrder;
|
||||
keys['K'] = actionToggleKernelThreads;
|
||||
keys['M'] = actionSortByMemory;
|
||||
keys['N'] = actionSortByPID;
|
||||
keys['P'] = actionSortByCPU;
|
||||
keys['S'] = actionSetup;
|
||||
keys['T'] = actionSortByTime;
|
||||
keys['U'] = actionUntagAll;
|
||||
keys['Z'] = actionTogglePauseProcessUpdate;
|
||||
keys['['] = actionLowerPriority;
|
||||
keys['\014'] = actionRedraw; // Ctrl+L
|
||||
keys['\177'] = actionCollapseIntoParent;
|
||||
keys['\\'] = actionIncFilter;
|
||||
keys[']'] = actionHigherPriority;
|
||||
keys['a'] = actionSetAffinity;
|
||||
keys['c'] = actionTagAllChildren;
|
||||
keys['e'] = actionShowEnvScreen;
|
||||
keys['h'] = actionHelp;
|
||||
keys['k'] = actionKill;
|
||||
keys['l'] = actionLsof;
|
||||
keys['m'] = actionToggleMergedCommand;
|
||||
keys['p'] = actionToggleProgramPath;
|
||||
keys['q'] = actionQuit;
|
||||
keys['s'] = actionStrace;
|
||||
keys['t'] = actionToggleTreeView;
|
||||
keys['u'] = actionFilterByUser;
|
||||
keys['w'] = actionShowCommandScreen;
|
||||
keys['Z'] = actionTogglePauseProcessUpdate;
|
||||
keys['x'] = actionShowLocks;
|
||||
keys[KEY_F(1)] = actionHelp;
|
||||
keys[KEY_F(2)] = actionSetup;
|
||||
keys[KEY_F(3)] = actionIncSearch;
|
||||
keys[KEY_F(4)] = actionIncFilter;
|
||||
keys[KEY_F(5)] = actionToggleTreeView;
|
||||
keys[KEY_F(6)] = actionSetSortColumn;
|
||||
keys[KEY_F(7)] = actionHigherPriority;
|
||||
keys[KEY_F(8)] = actionLowerPriority;
|
||||
keys[KEY_F(9)] = actionKill;
|
||||
keys[KEY_F(10)] = actionQuit;
|
||||
keys[KEY_F(18)] = actionExpandCollapseOrSortColumn;
|
||||
keys[KEY_RECLICK] = actionExpandOrCollapse;
|
||||
}
|
||||
|
16
Action.h
16
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);
|
||||
|
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.
|
||||
*/
|
||||
|
||||
@ -59,25 +59,25 @@ static void MaskItem_delete(Object* cast) {
|
||||
static void MaskItem_display(const Object* cast, RichString* out) {
|
||||
const MaskItem* this = (const MaskItem*)cast;
|
||||
assert (this != NULL);
|
||||
RichString_append(out, CRT_colors[CHECK_BOX], "[");
|
||||
RichString_appendAscii(out, CRT_colors[CHECK_BOX], "[");
|
||||
if (this->value == 2) {
|
||||
RichString_append(out, CRT_colors[CHECK_MARK], "x");
|
||||
RichString_appendAscii(out, CRT_colors[CHECK_MARK], "x");
|
||||
} else if (this->value == 1) {
|
||||
RichString_append(out, CRT_colors[CHECK_MARK], "o");
|
||||
RichString_appendAscii(out, CRT_colors[CHECK_MARK], "o");
|
||||
} else {
|
||||
RichString_append(out, CRT_colors[CHECK_MARK], " ");
|
||||
RichString_appendAscii(out, CRT_colors[CHECK_MARK], " ");
|
||||
}
|
||||
RichString_append(out, CRT_colors[CHECK_BOX], "]");
|
||||
RichString_append(out, CRT_colors[CHECK_TEXT], " ");
|
||||
RichString_appendAscii(out, CRT_colors[CHECK_BOX], "]");
|
||||
RichString_appendAscii(out, CRT_colors[CHECK_TEXT], " ");
|
||||
if (this->indent) {
|
||||
RichString_append(out, CRT_colors[PROCESS_TREE], this->indent);
|
||||
RichString_append(out, CRT_colors[PROCESS_TREE],
|
||||
RichString_appendWide(out, CRT_colors[PROCESS_TREE], this->indent);
|
||||
RichString_appendWide(out, CRT_colors[PROCESS_TREE],
|
||||
this->sub_tree == 2
|
||||
? CRT_treeStr[TREE_STR_OPEN]
|
||||
: CRT_treeStr[TREE_STR_SHUT]);
|
||||
RichString_append(out, CRT_colors[CHECK_TEXT], " ");
|
||||
RichString_appendAscii(out, CRT_colors[CHECK_TEXT], " ");
|
||||
}
|
||||
RichString_append(out, CRT_colors[CHECK_TEXT], this->text);
|
||||
RichString_appendWide(out, CRT_colors[CHECK_TEXT], this->text);
|
||||
}
|
||||
|
||||
static const ObjectClass MaskItem_class = {
|
||||
@ -173,7 +173,6 @@ static void AffinityPanel_update(AffinityPanel* this, bool keepSelected) {
|
||||
Panel* super = (Panel*) this;
|
||||
|
||||
FunctionBar_setLabel(super->currentBar, KEY_F(3), this->topoView ? "Collapse/Expand" : "");
|
||||
FunctionBar_draw(super->currentBar);
|
||||
|
||||
int oldSelected = Panel_getSelectedIndex(super);
|
||||
Panel_prune(super);
|
||||
@ -281,7 +280,7 @@ static MaskItem* AffinityPanel_addObject(AffinityPanel* this, hwloc_obj_t obj, u
|
||||
indent_buf[0] = '\0';
|
||||
if (depth > 0) {
|
||||
for (unsigned i = 1; i < depth; i++) {
|
||||
xSnprintf(&indent_buf[off], left, "%s ", (indent & (1u << i)) ? CRT_treeStr[TREE_STR_VERT] : " ");
|
||||
xSnprintf(&indent_buf[off], left, "%s ", (indent & (1U << i)) ? CRT_treeStr[TREE_STR_VERT] : " ");
|
||||
size_t len = strlen(&indent_buf[off]);
|
||||
off += len;
|
||||
left -= len;
|
||||
@ -323,9 +322,9 @@ static MaskItem* AffinityPanel_addObject(AffinityPanel* this, hwloc_obj_t obj, u
|
||||
static MaskItem* AffinityPanel_buildTopology(AffinityPanel* this, hwloc_obj_t obj, unsigned indent, MaskItem* parent) {
|
||||
MaskItem* item = AffinityPanel_addObject(this, obj, indent, parent);
|
||||
if (obj->next_sibling) {
|
||||
indent |= (1u << obj->depth);
|
||||
indent |= (1U << obj->depth);
|
||||
} else {
|
||||
indent &= ~(1u << obj->depth);
|
||||
indent &= ~(1U << obj->depth);
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < obj->arity; i++) {
|
||||
@ -358,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));
|
||||
@ -383,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);
|
||||
@ -419,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,21 +79,44 @@ 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);
|
||||
}
|
||||
|
||||
for (int i = 1; i < Platform_numberOfFields; i++) {
|
||||
// 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];
|
||||
xSnprintf(description, sizeof(description), "%s - %s", Process_fields[i].name, Process_fields[i].description);
|
||||
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,15 +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);
|
||||
FunctionBar_draw(panel->currentBar);
|
||||
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) {
|
||||
@ -46,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;
|
||||
@ -56,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;
|
||||
@ -68,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;
|
||||
@ -77,8 +80,9 @@ static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) {
|
||||
if (update) {
|
||||
this->settings->changed = true;
|
||||
Header_calculateHeight(header);
|
||||
Header_updateData(header);
|
||||
Header_draw(header);
|
||||
ScreenManager_resize(this->scr, this->scr->x1, header->height, this->scr->x2, this->scr->y2);
|
||||
ScreenManager_resize(this->scr);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -91,7 +95,51 @@ const PanelClass AvailableMetersPanel_class = {
|
||||
.eventHandler = AvailableMetersPanel_eventHandler
|
||||
};
|
||||
|
||||
AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* header, Panel* leftMeters, Panel* rightMeters, ScreenManager* scr, ProcessList* pl) {
|
||||
// Handle (&CPUMeter_class) entries in the AvailableMetersPanel
|
||||
static void AvailableMetersPanel_addCPUMeters(Panel* super, const MeterClass* type, const ProcessList* pl) {
|
||||
if (pl->existingCPUs > 1) {
|
||||
Panel_add(super, (Object*) ListItem_new("CPU average", 0));
|
||||
for (unsigned int i = 1; i <= pl->existingCPUs; i++) {
|
||||
char buffer[50];
|
||||
xSnprintf(buffer, sizeof(buffer), "%s %d", type->uiName, Settings_cpuId(pl->settings, i - 1));
|
||||
Panel_add(super, (Object*) ListItem_new(buffer, i));
|
||||
}
|
||||
} else {
|
||||
Panel_add(super, (Object*) ListItem_new(type->uiName, 1));
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
Panel* super;
|
||||
unsigned int id;
|
||||
unsigned int offset;
|
||||
} DynamicIterator;
|
||||
|
||||
static void AvailableMetersPanel_addDynamicMeter(ATTR_UNUSED ht_key_t key, void* value, void* data) {
|
||||
const DynamicMeter* meter = (const DynamicMeter*)value;
|
||||
DynamicIterator* iter = (DynamicIterator*)data;
|
||||
unsigned int identifier = (iter->offset << 16) | iter->id;
|
||||
const char* label = meter->description ? meter->description : meter->caption;
|
||||
if (!label)
|
||||
label = meter->name; /* last fallback to name, guaranteed set */
|
||||
Panel_add(iter->super, (Object*) ListItem_new(label, identifier));
|
||||
iter->id++;
|
||||
}
|
||||
|
||||
// Handle (&DynamicMeter_class) entries in the AvailableMetersPanel
|
||||
static void AvailableMetersPanel_addDynamicMeters(Panel* super, const ProcessList* pl, unsigned int offset) {
|
||||
DynamicIterator iter = { .super = super, .id = 1, .offset = offset };
|
||||
assert(pl->dynamicMeters != NULL);
|
||||
Hashtable_foreach(pl->dynamicMeters, AvailableMetersPanel_addDynamicMeter, &iter);
|
||||
}
|
||||
|
||||
// Handle remaining Platform Meter entries in the AvailableMetersPanel
|
||||
static void AvailableMetersPanel_addPlatformMeter(Panel* super, const MeterClass* type, unsigned int offset) {
|
||||
const char* label = type->description ? type->description : type->uiName;
|
||||
Panel_add(super, (Object*) ListItem_new(label, offset << 16));
|
||||
}
|
||||
|
||||
AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* header, size_t columns, MetersPanel** meterPanels, ScreenManager* scr, const ProcessList* pl) {
|
||||
AvailableMetersPanel* this = AllocThis(AvailableMetersPanel);
|
||||
Panel* super = (Panel*) this;
|
||||
FunctionBar* fuBar = FunctionBar_newEnterEsc("Add ", "Done ");
|
||||
@ -99,31 +147,24 @@ AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* heade
|
||||
|
||||
this->settings = settings;
|
||||
this->header = header;
|
||||
this->leftPanel = leftMeters;
|
||||
this->rightPanel = rightMeters;
|
||||
this->columns = columns;
|
||||
this->meterPanels = meterPanels;
|
||||
this->scr = scr;
|
||||
|
||||
Panel_setHeader(super, "Available meters");
|
||||
// Platform_meterTypes[0] should be always (&CPUMeter_class), which we will
|
||||
// handle separately in the code below.
|
||||
for (int i = 1; Platform_meterTypes[i]; i++) {
|
||||
// Platform_meterTypes[0] should be always (&CPUMeter_class) which we will
|
||||
// handle separately in the code below. Likewise, identifiers for Dynamic
|
||||
// Meters are handled separately - similar to CPUs, this allows generation
|
||||
// of multiple different Meters (also using 'param' to distinguish them).
|
||||
for (unsigned int i = 1; Platform_meterTypes[i]; i++) {
|
||||
const MeterClass* type = Platform_meterTypes[i];
|
||||
assert(type != &CPUMeter_class);
|
||||
const char* label = type->description ? type->description : type->uiName;
|
||||
Panel_add(super, (Object*) ListItem_new(label, i << 16));
|
||||
}
|
||||
// Handle (&CPUMeter_class)
|
||||
const MeterClass* type = &CPUMeter_class;
|
||||
int cpus = pl->cpuCount;
|
||||
if (cpus > 1) {
|
||||
Panel_add(super, (Object*) ListItem_new("CPU average", 0));
|
||||
for (int i = 1; i <= cpus; i++) {
|
||||
char buffer[50];
|
||||
xSnprintf(buffer, sizeof(buffer), "%s %d", type->uiName, Settings_cpuId(this->settings, i - 1));
|
||||
Panel_add(super, (Object*) ListItem_new(buffer, i));
|
||||
}
|
||||
} else {
|
||||
Panel_add(super, (Object*) ListItem_new("CPU", 1));
|
||||
if (type == &DynamicMeter_class)
|
||||
AvailableMetersPanel_addDynamicMeters(super, pl, i);
|
||||
else
|
||||
AvailableMetersPanel_addPlatformMeter(super, type, i);
|
||||
}
|
||||
AvailableMetersPanel_addCPUMeters(super, &CPUMeter_class, pl);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
@ -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, int len) {
|
||||
static void BatteryMeter_updateValues(Meter* this) {
|
||||
ACPresence isOnAC;
|
||||
double percent;
|
||||
|
||||
@ -29,30 +29,27 @@ static void BatteryMeter_updateValues(Meter* this, char* buffer, int len) {
|
||||
|
||||
if (isnan(percent)) {
|
||||
this->values[0] = NAN;
|
||||
xSnprintf(buffer, len, "N/A");
|
||||
xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "N/A");
|
||||
return;
|
||||
}
|
||||
|
||||
this->values[0] = percent;
|
||||
|
||||
const char *onAcText, *onBatteryText, *unknownText;
|
||||
|
||||
unknownText = "%.1f%%";
|
||||
if (this->mode == TEXT_METERMODE) {
|
||||
onAcText = "%.1f%% (Running on A/C)";
|
||||
onBatteryText = "%.1f%% (Running on battery)";
|
||||
} else {
|
||||
onAcText = "%.1f%%(A/C)";
|
||||
onBatteryText = "%.1f%%(bat)";
|
||||
const char* text;
|
||||
switch (isOnAC) {
|
||||
case AC_PRESENT:
|
||||
text = this->mode == TEXT_METERMODE ? " (Running on A/C)" : "(A/C)";
|
||||
break;
|
||||
case AC_ABSENT:
|
||||
text = this->mode == TEXT_METERMODE ? " (Running on battery)" : "(bat)";
|
||||
break;
|
||||
case AC_ERROR:
|
||||
default:
|
||||
text = "";
|
||||
break;
|
||||
}
|
||||
|
||||
if (isOnAC == AC_PRESENT) {
|
||||
xSnprintf(buffer, len, onAcText, percent);
|
||||
} else if (isOnAC == AC_ABSENT) {
|
||||
xSnprintf(buffer, len, onBatteryText, percent);
|
||||
} else {
|
||||
xSnprintf(buffer, len, unknownText, percent);
|
||||
}
|
||||
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.
|
||||
|
182
CPUMeter.c
182
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,39 +34,50 @@ 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, int 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), "%5.1f%%", percent);
|
||||
xSnprintf(cpuUsageBuffer, sizeof(cpuUsageBuffer), "%.1f%%", percent);
|
||||
}
|
||||
|
||||
if (this->pl->settings->showCPUFrequency) {
|
||||
@ -79,7 +89,7 @@ static void CPUMeter_updateValues(Meter* this, char* buffer, int 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, int 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, int 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_append(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]);
|
||||
RichString_append(out, CRT_colors[METER_TEXT], ":");
|
||||
RichString_append(out, CRT_colors[CPU_NORMAL], buffer);
|
||||
|
||||
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_appendnAscii(out, CRT_colors[CPU_NORMAL], buffer, len);
|
||||
if (this->pl->settings->detailedCPUTime) {
|
||||
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_KERNEL]);
|
||||
RichString_append(out, CRT_colors[METER_TEXT], "sy:");
|
||||
RichString_append(out, CRT_colors[CPU_SYSTEM], buffer);
|
||||
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_NICE]);
|
||||
RichString_append(out, CRT_colors[METER_TEXT], "ni:");
|
||||
RichString_append(out, CRT_colors[CPU_NICE_TEXT], buffer);
|
||||
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_IRQ]);
|
||||
RichString_append(out, CRT_colors[METER_TEXT], "hi:");
|
||||
RichString_append(out, CRT_colors[CPU_IRQ], buffer);
|
||||
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_SOFTIRQ]);
|
||||
RichString_append(out, CRT_colors[METER_TEXT], "si:");
|
||||
RichString_append(out, CRT_colors[CPU_SOFTIRQ], buffer);
|
||||
len = xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_KERNEL]);
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], "sy:");
|
||||
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_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_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_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]);
|
||||
RichString_append(out, CRT_colors[METER_TEXT], "st:");
|
||||
RichString_append(out, CRT_colors[CPU_STEAL], buffer);
|
||||
len = xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_STEAL]);
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], "st:");
|
||||
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]);
|
||||
RichString_append(out, CRT_colors[METER_TEXT], "gu:");
|
||||
RichString_append(out, CRT_colors[CPU_GUEST], buffer);
|
||||
len = xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_GUEST]);
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], "gu:");
|
||||
RichString_appendnAscii(out, CRT_colors[CPU_GUEST], buffer, len);
|
||||
}
|
||||
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_IOWAIT]);
|
||||
RichString_append(out, CRT_colors[METER_TEXT], "wa:");
|
||||
RichString_append(out, CRT_colors[CPU_IOWAIT], buffer);
|
||||
len = xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_IOWAIT]);
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], "wa:");
|
||||
RichString_appendnAscii(out, CRT_colors[CPU_IOWAIT], buffer, len);
|
||||
} else {
|
||||
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_KERNEL]);
|
||||
RichString_append(out, CRT_colors[METER_TEXT], "sys:");
|
||||
RichString_append(out, CRT_colors[CPU_SYSTEM], buffer);
|
||||
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_NICE]);
|
||||
RichString_append(out, CRT_colors[METER_TEXT], "low:");
|
||||
RichString_append(out, CRT_colors[CPU_NICE_TEXT], buffer);
|
||||
len = xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_KERNEL]);
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], "sys:");
|
||||
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_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]);
|
||||
RichString_append(out, CRT_colors[METER_TEXT], "vir:");
|
||||
RichString_append(out, CRT_colors[CPU_GUEST], buffer);
|
||||
len = xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_IRQ]);
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], "vir:");
|
||||
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_append(out, CRT_colors[METER_TEXT], "temp:");
|
||||
RichString_append(out, CRT_colors[METER_VALUE], cpuTemperatureBuffer);
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], "temp:");
|
||||
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,
|
||||
|
69
CRT.h
69
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,29 +13,31 @@ in the source distribution for its full text.
|
||||
|
||||
#include "Macros.h"
|
||||
#include "ProvideCurses.h"
|
||||
#include "Settings.h"
|
||||
|
||||
|
||||
typedef enum TreeStr_ {
|
||||
TREE_STR_HORZ,
|
||||
TREE_STR_VERT,
|
||||
TREE_STR_RTEE,
|
||||
TREE_STR_BEND,
|
||||
TREE_STR_TEND,
|
||||
TREE_STR_OPEN,
|
||||
TREE_STR_SHUT,
|
||||
TREE_STR_COUNT
|
||||
TREE_STR_ASC,
|
||||
TREE_STR_DESC,
|
||||
LAST_TREE_STR
|
||||
} TreeStr;
|
||||
|
||||
typedef enum ColorSchemes_ {
|
||||
COLORSCHEME_DEFAULT = 0,
|
||||
typedef enum ColorScheme_ {
|
||||
COLORSCHEME_DEFAULT,
|
||||
COLORSCHEME_MONOCHROME,
|
||||
COLORSCHEME_BLACKONWHITE,
|
||||
COLORSCHEME_LIGHTTERMINAL,
|
||||
COLORSCHEME_MIDNIGHT,
|
||||
COLORSCHEME_BLACKNIGHT,
|
||||
COLORSCHEME_BROKENGRAY,
|
||||
LAST_COLORSCHEME,
|
||||
} ColorSchemes;
|
||||
LAST_COLORSCHEME
|
||||
} ColorScheme;
|
||||
|
||||
typedef enum ColorElements_ {
|
||||
RESET_COLOR,
|
||||
@ -51,6 +53,7 @@ typedef enum ColorElements_ {
|
||||
PANEL_SELECTION_FOLLOW,
|
||||
PANEL_SELECTION_UNFOCUS,
|
||||
LARGE_NUMBER,
|
||||
METER_SHADOW,
|
||||
METER_TEXT,
|
||||
METER_VALUE,
|
||||
METER_VALUE_ERROR,
|
||||
@ -58,18 +61,20 @@ typedef enum ColorElements_ {
|
||||
METER_VALUE_IOWRITE,
|
||||
METER_VALUE_NOTICE,
|
||||
METER_VALUE_OK,
|
||||
METER_VALUE_WARN,
|
||||
LED_COLOR,
|
||||
UPTIME,
|
||||
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,
|
||||
@ -88,6 +93,11 @@ typedef enum ColorElements_ {
|
||||
MEMORY_BUFFERS,
|
||||
MEMORY_BUFFERS_TEXT,
|
||||
MEMORY_CACHE,
|
||||
MEMORY_SHARED,
|
||||
HUGEPAGE_1,
|
||||
HUGEPAGE_2,
|
||||
HUGEPAGE_3,
|
||||
HUGEPAGE_4,
|
||||
LOAD,
|
||||
LOAD_AVERAGE_FIFTEEN,
|
||||
LOAD_AVERAGE_FIVE,
|
||||
@ -99,6 +109,7 @@ typedef enum ColorElements_ {
|
||||
DATE,
|
||||
DATETIME,
|
||||
HELP_BOLD,
|
||||
HELP_SHADOW,
|
||||
HOSTNAME,
|
||||
CPU_NICE,
|
||||
CPU_NICE_TEXT,
|
||||
@ -120,6 +131,15 @@ typedef enum ColorElements_ {
|
||||
ZFS_COMPRESSED,
|
||||
ZFS_RATIO,
|
||||
ZRAM,
|
||||
DYNAMIC_GRAY,
|
||||
DYNAMIC_DARKGRAY,
|
||||
DYNAMIC_RED,
|
||||
DYNAMIC_GREEN,
|
||||
DYNAMIC_BLUE,
|
||||
DYNAMIC_CYAN,
|
||||
DYNAMIC_MAGENTA,
|
||||
DYNAMIC_YELLOW,
|
||||
DYNAMIC_WHITE,
|
||||
LAST_COLORELEMENT
|
||||
} ColorElements;
|
||||
|
||||
@ -127,9 +147,9 @@ void CRT_fatalError(const char* note) ATTR_NORETURN;
|
||||
|
||||
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_ALT(x) (KEY_F(64 - 26) + ((x) - 'A'))
|
||||
|
||||
extern const char* CRT_degreeSign;
|
||||
@ -144,39 +164,20 @@ extern const char* const* CRT_treeStr;
|
||||
|
||||
extern const int* CRT_colors;
|
||||
|
||||
extern int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT];
|
||||
|
||||
extern int CRT_cursorX;
|
||||
|
||||
extern int CRT_scrollHAmount;
|
||||
|
||||
extern int CRT_scrollWheelVAmount;
|
||||
|
||||
extern const char* CRT_termType;
|
||||
extern ColorScheme CRT_colorScheme;
|
||||
|
||||
extern int CRT_colorScheme;
|
||||
|
||||
extern long CRT_pageSize;
|
||||
extern long CRT_pageSizeKB;
|
||||
|
||||
#ifdef HAVE_SETUID_ENABLED
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
@ -17,11 +17,16 @@ in the source distribution for its full text.
|
||||
#include "ColumnsPanel.h"
|
||||
#include "DisplayOptionsPanel.h"
|
||||
#include "FunctionBar.h"
|
||||
#include "Header.h"
|
||||
#include "HeaderLayout.h"
|
||||
#include "HeaderOptionsPanel.h"
|
||||
#include "ListItem.h"
|
||||
#include "Macros.h"
|
||||
#include "MetersPanel.h"
|
||||
#include "Object.h"
|
||||
#include "ProvideCurses.h"
|
||||
#include "Vector.h"
|
||||
#include "XUtils.h"
|
||||
|
||||
|
||||
static const char* const CategoriesFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL};
|
||||
@ -33,14 +38,24 @@ static void CategoriesPanel_delete(Object* object) {
|
||||
free(this);
|
||||
}
|
||||
|
||||
void CategoriesPanel_makeMetersPage(CategoriesPanel* this) {
|
||||
MetersPanel* leftMeters = MetersPanel_new(this->settings, "Left column", this->header->columns[0], this->scr);
|
||||
MetersPanel* rightMeters = MetersPanel_new(this->settings, "Right column", this->header->columns[1], this->scr);
|
||||
leftMeters->rightNeighbor = rightMeters;
|
||||
rightMeters->leftNeighbor = leftMeters;
|
||||
Panel* availableMeters = (Panel*) AvailableMetersPanel_new(this->settings, this->header, (Panel*) leftMeters, (Panel*) rightMeters, this->scr, this->pl);
|
||||
ScreenManager_add(this->scr, (Panel*) leftMeters, 20);
|
||||
ScreenManager_add(this->scr, (Panel*) rightMeters, 20);
|
||||
static void CategoriesPanel_makeMetersPage(CategoriesPanel* this) {
|
||||
size_t columns = HeaderLayout_getColumns(this->scr->header->headerLayout);
|
||||
MetersPanel** meterPanels = xMallocArray(columns, sizeof(MetersPanel*));
|
||||
|
||||
for (size_t i = 0; i < columns; i++) {
|
||||
char titleBuffer[32];
|
||||
xSnprintf(titleBuffer, sizeof(titleBuffer), "Column %zu", i + 1);
|
||||
meterPanels[i] = MetersPanel_new(this->settings, titleBuffer, this->header->columns[i], this->scr);
|
||||
|
||||
if (i != 0) {
|
||||
meterPanels[i]->leftNeighbor = meterPanels[i - 1];
|
||||
meterPanels[i - 1]->rightNeighbor = meterPanels[i];
|
||||
}
|
||||
|
||||
ScreenManager_add(this->scr, (Panel*) meterPanels[i], 20);
|
||||
}
|
||||
|
||||
Panel* availableMeters = (Panel*) AvailableMetersPanel_new(this->settings, this->header, columns, meterPanels, this->scr, this->pl);
|
||||
ScreenManager_add(this->scr, availableMeters, -1);
|
||||
}
|
||||
|
||||
@ -50,17 +65,36 @@ static void CategoriesPanel_makeDisplayOptionsPage(CategoriesPanel* this) {
|
||||
}
|
||||
|
||||
static void CategoriesPanel_makeColorsPage(CategoriesPanel* this) {
|
||||
Panel* colors = (Panel*) ColorsPanel_new(this->settings, this->scr);
|
||||
Panel* colors = (Panel*) ColorsPanel_new(this->settings);
|
||||
ScreenManager_add(this->scr, colors, -1);
|
||||
}
|
||||
|
||||
static void CategoriesPanel_makeColumnsPage(CategoriesPanel* this) {
|
||||
Panel* columns = (Panel*) ColumnsPanel_new(this->settings);
|
||||
Panel* availableColumns = (Panel*) AvailableColumnsPanel_new(columns);
|
||||
Panel* availableColumns = (Panel*) AvailableColumnsPanel_new(columns, this->settings->dynamicColumns);
|
||||
ScreenManager_add(this->scr, columns, 20);
|
||||
ScreenManager_add(this->scr, availableColumns, -1);
|
||||
}
|
||||
|
||||
static void CategoriesPanel_makeHeaderOptionsPage(CategoriesPanel* this) {
|
||||
Panel* colors = (Panel*) HeaderOptionsPanel_new(this->settings, this->scr);
|
||||
ScreenManager_add(this->scr, colors, -1);
|
||||
}
|
||||
|
||||
typedef void (* CategoriesPanel_makePageFunc)(CategoriesPanel* ref);
|
||||
typedef struct CategoriesPanelPage_ {
|
||||
const char* name;
|
||||
CategoriesPanel_makePageFunc ctor;
|
||||
} CategoriesPanelPage;
|
||||
|
||||
static const CategoriesPanelPage categoriesPanelPages[] = {
|
||||
{ .name = "Display options", .ctor = CategoriesPanel_makeDisplayOptionsPage },
|
||||
{ .name = "Header layout", .ctor = CategoriesPanel_makeHeaderOptionsPage },
|
||||
{ .name = "Meters", .ctor = CategoriesPanel_makeMetersPage },
|
||||
{ .name = "Columns", .ctor = CategoriesPanel_makeColumnsPage },
|
||||
{ .name = "Colors", .ctor = CategoriesPanel_makeColorsPage },
|
||||
};
|
||||
|
||||
static HandlerResult CategoriesPanel_eventHandler(Panel* super, int ch) {
|
||||
CategoriesPanel* this = (CategoriesPanel*) super;
|
||||
|
||||
@ -98,19 +132,8 @@ static HandlerResult CategoriesPanel_eventHandler(Panel* super, int ch) {
|
||||
for (int i = 1; i < size; i++)
|
||||
ScreenManager_remove(this->scr, 1);
|
||||
|
||||
switch (selected) {
|
||||
case 0:
|
||||
CategoriesPanel_makeMetersPage(this);
|
||||
break;
|
||||
case 1:
|
||||
CategoriesPanel_makeDisplayOptionsPage(this);
|
||||
break;
|
||||
case 2:
|
||||
CategoriesPanel_makeColorsPage(this);
|
||||
break;
|
||||
case 3:
|
||||
CategoriesPanel_makeColumnsPage(this);
|
||||
break;
|
||||
if (selected >= 0 && (size_t)selected < ARRAYSIZE(categoriesPanelPages)) {
|
||||
categoriesPanelPages[selected].ctor(this);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@ -135,9 +158,10 @@ CategoriesPanel* CategoriesPanel_new(ScreenManager* scr, Settings* settings, Hea
|
||||
this->header = header;
|
||||
this->pl = pl;
|
||||
Panel_setHeader(super, "Setup");
|
||||
Panel_add(super, (Object*) ListItem_new("Meters", 0));
|
||||
Panel_add(super, (Object*) ListItem_new("Display options", 0));
|
||||
Panel_add(super, (Object*) ListItem_new("Colors", 0));
|
||||
Panel_add(super, (Object*) ListItem_new("Columns", 0));
|
||||
for (size_t i = 0; i < ARRAYSIZE(categoriesPanelPages); i++)
|
||||
Panel_add(super, (Object*) ListItem_new(categoriesPanelPages[i].name, 0));
|
||||
|
||||
ScreenManager_add(scr, super, 16);
|
||||
categoriesPanelPages[0].ctor(this);
|
||||
return this;
|
||||
}
|
||||
|
@ -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);
|
||||
|
231
ChangeLog
231
ChangeLog
@ -1,3 +1,227 @@
|
||||
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
|
||||
* Invert Process_compare so that superclass matches run first
|
||||
(thanks to Hisham Muhammad)
|
||||
* Unhardcode Mac OS tick-to-milliseconds conversion
|
||||
(thanks to Alexander Momchilov)
|
||||
* Check if clock_gettime needs linking of librt
|
||||
* Define O_PATH if not already defined
|
||||
(thanks to Chris Burr)
|
||||
* Add column on Mac for processes running under translation
|
||||
(thanks to Dániel Bakai)
|
||||
* Configure check for additional linker flags for keypad(3)
|
||||
* PSI Meter: constant width and only print ten-duration as bar
|
||||
* Sort in paused mode after inverting sort order
|
||||
* Handle absence of package CPU temperature
|
||||
* Meter: restore non-wide-character build
|
||||
* LibSensors: restore temperature for Raspberry Pi
|
||||
* MainPanel: do not reset hideProcessSelection on KEY_SHUFFLE
|
||||
* BarMeter: rework text padding
|
||||
* Panel: rework drawing of FunctionBar
|
||||
* Meter: fix artifacts with very tiny width
|
||||
* DragonFlyBSD updates
|
||||
* BUGFIX: Fix dlopen issue for libsensors5 for some platforms
|
||||
* BUGFIX: Fix broken tree display on inverted sort order
|
||||
* BUGFIX: Fix pause mode ("Z") in tree view
|
||||
* BUGFIX: Correct timebase for non-x86 CPUs on Darwin
|
||||
* BUGFIX: Avoid NULL dereference on zombie processes
|
||||
* Document dynamic bindings and assumed external configuration
|
||||
* Update key mapping documentation for sorting
|
||||
|
||||
What's new in version 3.0.3
|
||||
|
||||
* Process sorting in 'tree' mode
|
||||
@ -5,8 +229,9 @@ What's new in version 3.0.3
|
||||
* Improved command display/sort functionality
|
||||
(thanks to Narendran Gopalakrishnan)
|
||||
* Add screen for active file locks
|
||||
(thanks to Fynn J. Wulf)
|
||||
* Calculate library size (M_LRS column) from maps file
|
||||
(thanks to Fynn Wulf)
|
||||
(thanks to Fynn J. Wulf)
|
||||
* Add a Zram meter
|
||||
(thanks to Murloc Knight)
|
||||
* Add Linux cwd process column
|
||||
@ -49,8 +274,10 @@ What's new in version 3.0.3
|
||||
* Avoid expensive build of process tree when not using it
|
||||
* Include documentation for COMM and EXE
|
||||
* Distinguish display of no permissions for reading M_LRS
|
||||
* Only calculate M_LRS size every 5 seconds
|
||||
* Only calculate M_LRS size every 2 seconds
|
||||
* Improvements to comm / cmdline display functionality
|
||||
* Merged view for COMM, EXE and cmdline
|
||||
(thanks to Narendran Gopalakrishnan and Benny Baumann)
|
||||
* Consistent kernel thread display for COMM/EXE columns
|
||||
* Central fault handling for all platforms
|
||||
* Handle parsing envID & VPid from process status file
|
||||
|
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, int 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,24 +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"
|
||||
#include "XUtils.h"
|
||||
|
||||
|
||||
// TO ADD A NEW SCHEME:
|
||||
@ -52,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:
|
||||
@ -61,26 +59,22 @@ 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++)
|
||||
CheckItem_set((CheckItem*)Panel_get(super, i), false);
|
||||
CheckItem_set((CheckItem*)Panel_get(super, mark), true);
|
||||
|
||||
this->settings->colorScheme = mark;
|
||||
result = HANDLED;
|
||||
}
|
||||
|
||||
if (result == HANDLED) {
|
||||
this->settings->changed = true;
|
||||
const Header* header = this->scr->header;
|
||||
|
||||
CRT_setColors(mark);
|
||||
clear();
|
||||
Panel* menu = (Panel*) Vector_get(this->scr->panels, 0);
|
||||
Header_draw(header);
|
||||
FunctionBar_draw(super->currentBar);
|
||||
RichString_setAttr(&(super->header), CRT_colors[PANEL_HEADER_FOCUS]);
|
||||
RichString_setAttr(&(menu->header), CRT_colors[PANEL_HEADER_UNFOCUS]);
|
||||
ScreenManager_resize(this->scr, this->scr->x1, header->height, this->scr->x2, this->scr->y2);
|
||||
|
||||
result = HANDLED | REDRAW;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -92,14 +86,15 @@ 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);
|
||||
|
||||
Panel_setHeader(super, "Colors");
|
||||
for (int i = 0; ColorSchemeNames[i] != NULL; i++) {
|
||||
|
@ -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"
|
||||
@ -44,7 +47,7 @@ static HandlerResult ColumnsPanel_eventHandler(Panel* super, int ch) {
|
||||
{
|
||||
if (selected < size - 1) {
|
||||
this->moving = !(this->moving);
|
||||
Panel_setSelectionColor(super, this->moving ? CRT_colors[PANEL_SELECTION_FOLLOW] : CRT_colors[PANEL_SELECTION_FOCUS]);
|
||||
Panel_setSelectionColor(super, this->moving ? PANEL_SELECTION_FOLLOW : PANEL_SELECTION_FOCUS);
|
||||
ListItem* selectedItem = (ListItem*) Panel_getSelected(super);
|
||||
if (selectedItem)
|
||||
selectedItem->moving = this->moving;
|
||||
@ -115,6 +118,26 @@ const PanelClass ColumnsPanel_class = {
|
||||
.eventHandler = ColumnsPanel_eventHandler
|
||||
};
|
||||
|
||||
static void ColumnsPanel_add(Panel* super, unsigned int key, Hashtable* columns) {
|
||||
const char* name;
|
||||
if (key < LAST_PROCESSFIELD) {
|
||||
name = Process_fields[key].name;
|
||||
} else {
|
||||
const DynamicColumn* column = Hashtable_get(columns, key);
|
||||
assert(column);
|
||||
if (!column) {
|
||||
name = NULL;
|
||||
} else {
|
||||
name = column->caption ? column->caption : column->heading;
|
||||
if (!name)
|
||||
name = column->name; /* name is a mandatory field */
|
||||
}
|
||||
}
|
||||
if (name == NULL)
|
||||
name = "- ";
|
||||
Panel_add(super, (Object*) ListItem_new(name, key));
|
||||
}
|
||||
|
||||
ColumnsPanel* ColumnsPanel_new(Settings* settings) {
|
||||
ColumnsPanel* this = AllocThis(ColumnsPanel);
|
||||
Panel* super = (Panel*) this;
|
||||
@ -125,12 +148,11 @@ ColumnsPanel* ColumnsPanel_new(Settings* settings) {
|
||||
this->moving = false;
|
||||
Panel_setHeader(super, "Active Columns");
|
||||
|
||||
ProcessField* fields = this->settings->fields;
|
||||
for (; *fields; fields++) {
|
||||
if (Process_fields[*fields].name) {
|
||||
Panel_add(super, (Object*) ListItem_new(Process_fields[*fields].name, *fields));
|
||||
}
|
||||
}
|
||||
Hashtable* dynamicColumns = settings->dynamicColumns;
|
||||
const ProcessField* fields = settings->fields;
|
||||
for (; *fields; fields++)
|
||||
ColumnsPanel_add(super, *fields, dynamicColumns);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -143,6 +165,7 @@ void ColumnsPanel_update(Panel* super) {
|
||||
for (int i = 0; i < size; i++) {
|
||||
int key = ((ListItem*) Panel_get(super, i))->key;
|
||||
this->settings->fields[i] = key;
|
||||
if (key < LAST_PROCESSFIELD)
|
||||
this->settings->flags |= Process_fields[key].flags;
|
||||
}
|
||||
this->settings->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,6 +12,7 @@ in the source distribution for its full text.
|
||||
#include "Panel.h"
|
||||
#include "Settings.h"
|
||||
|
||||
|
||||
typedef struct ColumnsPanel_ {
|
||||
Panel super;
|
||||
|
||||
|
400
CommandLine.c
Normal file
400
CommandLine.c
Normal file
@ -0,0 +1,400 @@
|
||||
/*
|
||||
htop - CommandLine.c
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
(C) 2020-2021 htop dev team
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
#include "config.h" // IWYU pragma: keep
|
||||
|
||||
#include "CommandLine.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <getopt.h>
|
||||
#include <locale.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "Action.h"
|
||||
#include "CRT.h"
|
||||
#include "DynamicColumn.h"
|
||||
#include "DynamicMeter.h"
|
||||
#include "Hashtable.h"
|
||||
#include "Header.h"
|
||||
#include "IncSet.h"
|
||||
#include "MainPanel.h"
|
||||
#include "MetersPanel.h"
|
||||
#include "Panel.h"
|
||||
#include "Platform.h"
|
||||
#include "Process.h"
|
||||
#include "ProcessList.h"
|
||||
#include "ProvideCurses.h"
|
||||
#include "ScreenManager.h"
|
||||
#include "Settings.h"
|
||||
#include "UsersTable.h"
|
||||
#include "XUtils.h"
|
||||
|
||||
|
||||
static void printVersionFlag(const char* name) {
|
||||
printf("%s " VERSION "\n", name);
|
||||
}
|
||||
|
||||
static void printHelpFlag(const char* name) {
|
||||
printf("%s " VERSION "\n"
|
||||
COPYRIGHT "\n"
|
||||
"Released under the GNU GPLv2+.\n\n"
|
||||
"-C --no-color Use a monochrome color scheme\n"
|
||||
"-d --delay=DELAY Set the delay between updates, in tenths of seconds\n"
|
||||
"-F --filter=FILTER Show only the commands matching the given filter\n"
|
||||
"-h --help Print this help screen\n"
|
||||
"-H --highlight-changes[=DELAY] Highlight new and old processes\n"
|
||||
"-M --no-mouse Disable the mouse\n"
|
||||
"-p --pid=PID[,PID,PID...] Show only the given PIDs\n"
|
||||
" --readonly Disable all system and process changing features\n"
|
||||
"-s --sort-key=COLUMN Sort by COLUMN in list view (try --sort-key=help for a list)\n"
|
||||
"-t --tree Show the tree view (can be combined with -s)\n"
|
||||
"-u --user[=USERNAME] Show only processes for a given user (or $USER)\n"
|
||||
"-U --no-unicode Do not use unicode but plain ASCII\n"
|
||||
"-V --version Print version info\n", name);
|
||||
Platform_longOptionsUsage(name);
|
||||
printf("\n"
|
||||
"Long options may be passed with a single dash.\n\n"
|
||||
"Press F1 inside %s for online help.\n"
|
||||
"See 'man %s' for more information.\n", name, name);
|
||||
}
|
||||
|
||||
// ----------------------------------------
|
||||
|
||||
typedef struct CommandLineSettings_ {
|
||||
Hashtable* pidMatchList;
|
||||
char* commFilter;
|
||||
uid_t userId;
|
||||
int sortKey;
|
||||
int delay;
|
||||
bool useColors;
|
||||
bool enableMouse;
|
||||
bool treeView;
|
||||
bool allowUnicode;
|
||||
bool highlightChanges;
|
||||
int highlightDelaySecs;
|
||||
bool readonly;
|
||||
} CommandLineSettings;
|
||||
|
||||
static CommandLineSettings parseArguments(const char* program, int argc, char** argv) {
|
||||
|
||||
CommandLineSettings flags = {
|
||||
.pidMatchList = NULL,
|
||||
.commFilter = NULL,
|
||||
.userId = (uid_t)-1, // -1 is guaranteed to be an invalid uid_t (see setreuid(2))
|
||||
.sortKey = 0,
|
||||
.delay = -1,
|
||||
.useColors = true,
|
||||
.enableMouse = true,
|
||||
.treeView = false,
|
||||
.allowUnicode = true,
|
||||
.highlightChanges = false,
|
||||
.highlightDelaySecs = -1,
|
||||
.readonly = false,
|
||||
};
|
||||
|
||||
const struct option long_opts[] =
|
||||
{
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{"version", no_argument, 0, 'V'},
|
||||
{"delay", required_argument, 0, 'd'},
|
||||
{"sort-key", required_argument, 0, 's'},
|
||||
{"user", optional_argument, 0, 'u'},
|
||||
{"no-color", no_argument, 0, 'C'},
|
||||
{"no-colour", no_argument, 0, 'C'},
|
||||
{"no-mouse", no_argument, 0, 'M'},
|
||||
{"no-unicode", no_argument, 0, 'U'},
|
||||
{"tree", no_argument, 0, 't'},
|
||||
{"pid", required_argument, 0, 'p'},
|
||||
{"filter", required_argument, 0, 'F'},
|
||||
{"highlight-changes", optional_argument, 0, 'H'},
|
||||
{"readonly", no_argument, 0, 128},
|
||||
PLATFORM_LONG_OPTIONS
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
int opt, opti = 0;
|
||||
/* Parse arguments */
|
||||
while ((opt = getopt_long(argc, argv, "hVMCs:td:u::Up:F:H::", long_opts, &opti))) {
|
||||
if (opt == EOF)
|
||||
break;
|
||||
switch (opt) {
|
||||
case 'h':
|
||||
printHelpFlag(program);
|
||||
exit(0);
|
||||
case 'V':
|
||||
printVersionFlag(program);
|
||||
exit(0);
|
||||
case 's':
|
||||
assert(optarg); /* please clang analyzer, cause optarg can be NULL in the 'u' case */
|
||||
if (String_eq(optarg, "help")) {
|
||||
for (int j = 1; j < LAST_PROCESSFIELD; j++) {
|
||||
const char* name = Process_fields[j].name;
|
||||
const char* description = Process_fields[j].description;
|
||||
if (name) printf("%19s %s\n", name, description);
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
flags.sortKey = 0;
|
||||
for (int j = 1; j < LAST_PROCESSFIELD; j++) {
|
||||
if (Process_fields[j].name == NULL)
|
||||
continue;
|
||||
if (String_eq(optarg, Process_fields[j].name)) {
|
||||
flags.sortKey = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (flags.sortKey == 0) {
|
||||
fprintf(stderr, "Error: invalid column \"%s\".\n", optarg);
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case 'd':
|
||||
if (sscanf(optarg, "%16d", &(flags.delay)) == 1) {
|
||||
if (flags.delay < 1) flags.delay = 1;
|
||||
if (flags.delay > 100) flags.delay = 100;
|
||||
} else {
|
||||
fprintf(stderr, "Error: invalid delay value \"%s\".\n", optarg);
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case 'u':
|
||||
{
|
||||
const char *username = optarg;
|
||||
if (!username && optind < argc && argv[optind] != NULL &&
|
||||
(argv[optind][0] != '\0' && argv[optind][0] != '-')) {
|
||||
username = argv[optind++];
|
||||
}
|
||||
|
||||
if (!username) {
|
||||
flags.userId = geteuid();
|
||||
} else if (!Action_setUserOnly(username, &(flags.userId))) {
|
||||
fprintf(stderr, "Error: invalid user \"%s\".\n", username);
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'C':
|
||||
flags.useColors = false;
|
||||
break;
|
||||
case 'M':
|
||||
#ifdef HAVE_GETMOUSE
|
||||
flags.enableMouse = false;
|
||||
#endif
|
||||
break;
|
||||
case 'U':
|
||||
flags.allowUnicode = false;
|
||||
break;
|
||||
case 't':
|
||||
flags.treeView = true;
|
||||
break;
|
||||
case 'p': {
|
||||
assert(optarg); /* please clang analyzer, cause optarg can be NULL in the 'u' case */
|
||||
char* argCopy = xStrdup(optarg);
|
||||
char* saveptr;
|
||||
const char* pid = strtok_r(argCopy, ",", &saveptr);
|
||||
|
||||
if (!flags.pidMatchList) {
|
||||
flags.pidMatchList = Hashtable_new(8, false);
|
||||
}
|
||||
|
||||
while(pid) {
|
||||
unsigned int num_pid = atoi(pid);
|
||||
// deepcode ignore CastIntegerToAddress: we just want a non-NUll pointer here
|
||||
Hashtable_put(flags.pidMatchList, num_pid, (void *) 1);
|
||||
pid = strtok_r(NULL, ",", &saveptr);
|
||||
}
|
||||
free(argCopy);
|
||||
|
||||
break;
|
||||
}
|
||||
case 'F': {
|
||||
assert(optarg);
|
||||
free_and_xStrdup(&flags.commFilter, optarg);
|
||||
break;
|
||||
}
|
||||
case 'H': {
|
||||
const char *delay = optarg;
|
||||
if (!delay && optind < argc && argv[optind] != NULL &&
|
||||
(argv[optind][0] != '\0' && argv[optind][0] != '-')) {
|
||||
delay = argv[optind++];
|
||||
}
|
||||
if (delay) {
|
||||
if (sscanf(delay, "%16d", &(flags.highlightDelaySecs)) == 1) {
|
||||
if (flags.highlightDelaySecs < 1)
|
||||
flags.highlightDelaySecs = 1;
|
||||
} else {
|
||||
fprintf(stderr, "Error: invalid highlight delay value \"%s\".\n", delay);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
flags.highlightChanges = true;
|
||||
break;
|
||||
}
|
||||
case 128:
|
||||
flags.readonly = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (Platform_getLongOption(opt, argc, argv) == false)
|
||||
exit(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
static void CommandLine_delay(ProcessList* pl, unsigned long millisec) {
|
||||
struct timespec req = {
|
||||
.tv_sec = 0,
|
||||
.tv_nsec = millisec * 1000000L
|
||||
};
|
||||
while (nanosleep(&req, &req) == -1)
|
||||
continue;
|
||||
Platform_gettime_realtime(&pl->realtime, &pl->realtimeMs);
|
||||
}
|
||||
|
||||
static void setCommFilter(State* state, char** commFilter) {
|
||||
ProcessList* pl = state->pl;
|
||||
IncSet* inc = state->mainPanel->inc;
|
||||
|
||||
IncSet_setFilter(inc, *commFilter);
|
||||
pl->incFilter = IncSet_filter(inc);
|
||||
|
||||
free(*commFilter);
|
||||
*commFilter = NULL;
|
||||
}
|
||||
|
||||
int CommandLine_run(const char* name, int argc, char** argv) {
|
||||
|
||||
/* initialize locale */
|
||||
const char* lc_ctype;
|
||||
if ((lc_ctype = getenv("LC_CTYPE")) || (lc_ctype = getenv("LC_ALL")))
|
||||
setlocale(LC_CTYPE, lc_ctype);
|
||||
else
|
||||
setlocale(LC_CTYPE, "");
|
||||
|
||||
CommandLineSettings flags = parseArguments(name, argc, argv);
|
||||
|
||||
if (flags.readonly)
|
||||
Settings_enableReadonly();
|
||||
|
||||
Platform_init();
|
||||
|
||||
Process_setupColumnWidths();
|
||||
|
||||
UsersTable* ut = UsersTable_new();
|
||||
Hashtable* dc = DynamicColumns_new();
|
||||
Hashtable* dm = DynamicMeters_new();
|
||||
if (!dc)
|
||||
dc = Hashtable_new(0, true);
|
||||
|
||||
ProcessList* pl = ProcessList_new(ut, dm, dc, flags.pidMatchList, flags.userId);
|
||||
|
||||
Settings* settings = Settings_new(pl->activeCPUs, dc);
|
||||
pl->settings = settings;
|
||||
|
||||
Header* header = Header_new(pl, settings, 2);
|
||||
|
||||
Header_populateFromSettings(header);
|
||||
|
||||
if (flags.delay != -1)
|
||||
settings->delay = flags.delay;
|
||||
if (!flags.useColors)
|
||||
settings->colorScheme = COLORSCHEME_MONOCHROME;
|
||||
#ifdef HAVE_GETMOUSE
|
||||
if (!flags.enableMouse)
|
||||
settings->enableMouse = false;
|
||||
#endif
|
||||
if (flags.treeView)
|
||||
settings->treeView = true;
|
||||
if (flags.highlightChanges)
|
||||
settings->highlightChanges = true;
|
||||
if (flags.highlightDelaySecs != -1)
|
||||
settings->highlightDelaySecs = flags.highlightDelaySecs;
|
||||
if (flags.sortKey > 0) {
|
||||
// -t -s <key> means "tree sorted by key"
|
||||
// -s <key> means "list sorted by key" (previous existing behavior)
|
||||
if (!flags.treeView) {
|
||||
settings->treeView = false;
|
||||
}
|
||||
Settings_setSortKey(settings, flags.sortKey);
|
||||
}
|
||||
|
||||
CRT_init(settings, flags.allowUnicode);
|
||||
|
||||
MainPanel* panel = MainPanel_new();
|
||||
ProcessList_setPanel(pl, (Panel*) panel);
|
||||
|
||||
MainPanel_updateTreeFunctions(panel, settings->treeView);
|
||||
|
||||
State state = {
|
||||
.settings = settings,
|
||||
.ut = ut,
|
||||
.pl = pl,
|
||||
.mainPanel = panel,
|
||||
.header = header,
|
||||
.pauseProcessUpdate = false,
|
||||
.hideProcessSelection = false,
|
||||
};
|
||||
|
||||
MainPanel_setState(panel, &state);
|
||||
if (flags.commFilter)
|
||||
setCommFilter(&state, &(flags.commFilter));
|
||||
|
||||
ScreenManager* scr = ScreenManager_new(header, settings, &state, true);
|
||||
ScreenManager_add(scr, (Panel*) panel, -1);
|
||||
|
||||
ProcessList_scan(pl, false);
|
||||
CommandLine_delay(pl, 75);
|
||||
ProcessList_scan(pl, false);
|
||||
|
||||
if (settings->allBranchesCollapsed)
|
||||
ProcessList_collapseAllBranches(pl);
|
||||
|
||||
ScreenManager_run(scr, NULL, NULL);
|
||||
|
||||
attron(CRT_colors[RESET_COLOR]);
|
||||
mvhline(LINES - 1, 0, ' ', COLS);
|
||||
attroff(CRT_colors[RESET_COLOR]);
|
||||
refresh();
|
||||
|
||||
Platform_done();
|
||||
|
||||
CRT_done();
|
||||
|
||||
if (settings->changed) {
|
||||
int r = Settings_write(settings, false);
|
||||
if (r < 0)
|
||||
fprintf(stderr, "Can not save configuration to %s: %s\n", settings->filename, strerror(-r));
|
||||
}
|
||||
|
||||
Header_delete(header);
|
||||
ProcessList_delete(pl);
|
||||
|
||||
ScreenManager_delete(scr);
|
||||
MetersPanel_cleanup();
|
||||
|
||||
UsersTable_delete(ut);
|
||||
|
||||
if (flags.pidMatchList)
|
||||
Hashtable_delete(flags.pidMatchList);
|
||||
|
||||
CRT_resetSignalHandlers();
|
||||
|
||||
/* Delete these last, since they can get accessed in the crash handler */
|
||||
Settings_delete(settings);
|
||||
DynamicColumns_delete(dc);
|
||||
DynamicMeters_delete(dm);
|
||||
|
||||
return 0;
|
||||
}
|
14
CommandLine.h
Normal file
14
CommandLine.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef HEADER_CommandLine
|
||||
#define HEADER_CommandLine
|
||||
/*
|
||||
htop - CommandLine.h
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
(C) 2020-2021 htop dev team
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
|
||||
int CommandLine_run(const char* name, int argc, char** argv);
|
||||
|
||||
#endif
|
@ -2,13 +2,13 @@
|
||||
|
||||
#include "CommandScreen.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "Macros.h"
|
||||
#include "Panel.h"
|
||||
#include "ProvideCurses.h"
|
||||
#include "XUtils.h"
|
||||
|
||||
|
||||
static void CommandScreen_scan(InfoScreen* this) {
|
||||
@ -17,9 +17,10 @@ static void CommandScreen_scan(InfoScreen* this) {
|
||||
Panel_prune(panel);
|
||||
|
||||
const char* p = Process_getCommand(this->process);
|
||||
char* line = xMalloc(COLS + 1);
|
||||
char line[COLS + 1];
|
||||
int line_offset = 0, last_spc = -1, len;
|
||||
for (; *p != '\0'; p++, line_offset++) {
|
||||
assert(line_offset >= 0 && (size_t)line_offset < sizeof(line));
|
||||
line[line_offset] = *p;
|
||||
if (*p == ' ') {
|
||||
last_spc = line_offset;
|
||||
@ -41,7 +42,6 @@ static void CommandScreen_scan(InfoScreen* this) {
|
||||
InfoScreen_addLine(this, line);
|
||||
}
|
||||
|
||||
free(line);
|
||||
Panel_setSelected(panel, idx);
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
8
Compat.c
8
Compat.c
@ -1,7 +1,7 @@
|
||||
/*
|
||||
htop - Compat.c
|
||||
(C) 2020 Christian Göttsche
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
(C) 2020 htop dev team
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
@ -37,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;
|
||||
}
|
||||
@ -94,7 +94,7 @@ int Compat_openat(const char* dirpath,
|
||||
|
||||
#endif /* !HAVE_OPENAT */
|
||||
|
||||
int Compat_readlinkat(int dirfd,
|
||||
ssize_t Compat_readlinkat(int dirfd,
|
||||
const char* dirpath,
|
||||
const char* pathname,
|
||||
char* buf,
|
||||
|
8
Compat.h
8
Compat.h
@ -2,8 +2,8 @@
|
||||
#define HEADER_Compat
|
||||
/*
|
||||
htop - Compat.h
|
||||
(C) 2020 Christian Göttsche
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
(C) 2020 htop dev team
|
||||
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 <fcntl.h>
|
||||
#include <stddef.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/stat.h> // IWYU pragma: keep
|
||||
|
||||
|
||||
int Compat_faccessat(int dirfd,
|
||||
@ -50,7 +50,7 @@ int Compat_openat(openat_arg_t dirpath, const char* pathname, int flags);
|
||||
|
||||
#endif /* HAVE_OPENAT */
|
||||
|
||||
int Compat_readlinkat(int dirfd,
|
||||
ssize_t Compat_readlinkat(int dirfd,
|
||||
const char* dirpath,
|
||||
const char* pathname,
|
||||
char* buf,
|
||||
|
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, int 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, int 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 Christian Göttsche
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
(C) 2020 htop dev team
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
@ -9,12 +9,12 @@ in the source distribution for its full text.
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "CRT.h"
|
||||
#include "Macros.h"
|
||||
#include "Object.h"
|
||||
#include "Platform.h"
|
||||
#include "ProcessList.h"
|
||||
#include "RichString.h"
|
||||
#include "XUtils.h"
|
||||
|
||||
@ -26,51 +26,55 @@ static const int DiskIOMeter_attributes[] = {
|
||||
};
|
||||
|
||||
static bool hasData = false;
|
||||
static unsigned long int cached_read_diff = 0;
|
||||
static unsigned long int cached_write_diff = 0;
|
||||
static double cached_utilisation_diff = 0.0;
|
||||
static uint32_t cached_read_diff;
|
||||
static uint32_t cached_write_diff;
|
||||
static double cached_utilisation_diff;
|
||||
|
||||
static void DiskIOMeter_updateValues(Meter* this, char* buffer, int len) {
|
||||
static unsigned long long int cached_last_update = 0;
|
||||
static void DiskIOMeter_updateValues(Meter* this) {
|
||||
const ProcessList* pl = this->pl;
|
||||
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
unsigned long long int timeInMilliSeconds = (unsigned long long int)tv.tv_sec * 1000 + (unsigned long long int)tv.tv_usec / 1000;
|
||||
unsigned long long int passedTimeInMs = timeInMilliSeconds - cached_last_update;
|
||||
static uint64_t cached_last_update;
|
||||
uint64_t passedTimeInMs = pl->realtimeMs - cached_last_update;
|
||||
|
||||
/* update only every 500ms */
|
||||
if (passedTimeInMs > 500) {
|
||||
static unsigned long int cached_read_total = 0;
|
||||
static unsigned long int cached_write_total = 0;
|
||||
static unsigned long int cached_msTimeSpend_total = 0;
|
||||
static uint64_t cached_read_total;
|
||||
static uint64_t cached_write_total;
|
||||
static uint64_t cached_msTimeSpend_total;
|
||||
uint64_t diff;
|
||||
|
||||
cached_last_update = timeInMilliSeconds;
|
||||
cached_last_update = pl->realtimeMs;
|
||||
|
||||
DiskIOData data;
|
||||
|
||||
hasData = Platform_getDiskIO(&data);
|
||||
if (!hasData) {
|
||||
this->values[0] = 0;
|
||||
xSnprintf(buffer, len, "no data");
|
||||
xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "no data");
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.totalBytesRead > cached_read_total) {
|
||||
cached_read_diff = (data.totalBytesRead - cached_read_total) / 1024; /* Meter_humanUnit() expects unit in kilo */
|
||||
diff = data.totalBytesRead - cached_read_total;
|
||||
diff /= 1024; /* Meter_humanUnit() expects unit in kilo */
|
||||
cached_read_diff = (uint32_t)diff;
|
||||
} else {
|
||||
cached_read_diff = 0;
|
||||
}
|
||||
cached_read_total = data.totalBytesRead;
|
||||
|
||||
if (data.totalBytesWritten > cached_write_total) {
|
||||
cached_write_diff = (data.totalBytesWritten - cached_write_total) / 1024; /* Meter_humanUnit() expects unit in kilo */
|
||||
diff = data.totalBytesWritten - cached_write_total;
|
||||
diff /= 1024; /* Meter_humanUnit() expects unit in kilo */
|
||||
cached_write_diff = (uint32_t)diff;
|
||||
} else {
|
||||
cached_write_diff = 0;
|
||||
}
|
||||
cached_write_total = data.totalBytesWritten;
|
||||
|
||||
if (data.totalMsTimeSpend > cached_msTimeSpend_total) {
|
||||
cached_utilisation_diff = 100 * (double)(data.totalMsTimeSpend - cached_msTimeSpend_total) / passedTimeInMs;
|
||||
diff = data.totalMsTimeSpend - cached_msTimeSpend_total;
|
||||
cached_utilisation_diff = 100.0 * (double)diff / passedTimeInMs;
|
||||
} else {
|
||||
cached_utilisation_diff = 0.0;
|
||||
}
|
||||
@ -83,35 +87,36 @@ static void DiskIOMeter_updateValues(Meter* this, char* buffer, int len) {
|
||||
char bufferRead[12], bufferWrite[12];
|
||||
Meter_humanUnit(bufferRead, cached_read_diff, sizeof(bufferRead));
|
||||
Meter_humanUnit(bufferWrite, cached_write_diff, sizeof(bufferWrite));
|
||||
snprintf(buffer, len, "%sB %sB %.1f%%", bufferRead, bufferWrite, cached_utilisation_diff);
|
||||
snprintf(this->txtBuffer, sizeof(this->txtBuffer), "%sB %sB %.1f%%", bufferRead, bufferWrite, cached_utilisation_diff);
|
||||
}
|
||||
|
||||
static void DIskIOMeter_display(ATTR_UNUSED const Object* cast, RichString* out) {
|
||||
static void DiskIOMeter_display(ATTR_UNUSED const Object* cast, RichString* out) {
|
||||
if (!hasData) {
|
||||
RichString_write(out, CRT_colors[METER_VALUE_ERROR], "no data");
|
||||
RichString_writeAscii(out, CRT_colors[METER_VALUE_ERROR], "no data");
|
||||
return;
|
||||
}
|
||||
|
||||
char buffer[16];
|
||||
int len;
|
||||
|
||||
int color = cached_utilisation_diff > 40.0 ? METER_VALUE_NOTICE : METER_VALUE;
|
||||
xSnprintf(buffer, sizeof(buffer), "%.1f%%", cached_utilisation_diff);
|
||||
RichString_write(out, CRT_colors[color], buffer);
|
||||
len = xSnprintf(buffer, sizeof(buffer), "%.1f%%", cached_utilisation_diff);
|
||||
RichString_appendnAscii(out, CRT_colors[color], buffer, len);
|
||||
|
||||
RichString_append(out, CRT_colors[METER_TEXT], " read: ");
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], " read: ");
|
||||
Meter_humanUnit(buffer, cached_read_diff, sizeof(buffer));
|
||||
RichString_append(out, CRT_colors[METER_VALUE_IOREAD], buffer);
|
||||
RichString_appendAscii(out, CRT_colors[METER_VALUE_IOREAD], buffer);
|
||||
|
||||
RichString_append(out, CRT_colors[METER_TEXT], " write: ");
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], " write: ");
|
||||
Meter_humanUnit(buffer, cached_write_diff, sizeof(buffer));
|
||||
RichString_append(out, CRT_colors[METER_VALUE_IOWRITE], buffer);
|
||||
RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], buffer);
|
||||
}
|
||||
|
||||
const MeterClass DiskIOMeter_class = {
|
||||
.super = {
|
||||
.extends = Class(Meter),
|
||||
.delete = Meter_delete,
|
||||
.display = DIskIOMeter_display
|
||||
.display = DiskIOMeter_display
|
||||
},
|
||||
.updateValues = DiskIOMeter_updateValues,
|
||||
.defaultMode = TEXT_METERMODE,
|
||||
|
@ -1,18 +1,19 @@
|
||||
#ifndef HEADER_DiskIOMeter
|
||||
#define HEADER_DiskIOMeter
|
||||
/*
|
||||
h top - DiskIOMeter*.h
|
||||
(C) 2020 Christian Göttsche
|
||||
Released under the GNU GPLv2, see the COPYING file
|
||||
htop - DiskIOMeter.h
|
||||
(C) 2020 htop dev team
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
#include "Meter.h"
|
||||
|
||||
|
||||
typedef struct DiskIOData_ {
|
||||
unsigned long int totalBytesRead;
|
||||
unsigned long int totalBytesWritten;
|
||||
unsigned long int totalMsTimeSpend;
|
||||
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,7 +18,6 @@ in the source distribution for its full text.
|
||||
#include "Object.h"
|
||||
#include "OptionItem.h"
|
||||
#include "ProvideCurses.h"
|
||||
#include "XUtils.h"
|
||||
|
||||
|
||||
static const char* const DisplayOptionsFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL};
|
||||
@ -73,8 +72,9 @@ static HandlerResult DisplayOptionsPanel_eventHandler(Panel* super, int ch) {
|
||||
Header* header = this->scr->header;
|
||||
Header_calculateHeight(header);
|
||||
Header_reinit(header);
|
||||
Header_updateData(header);
|
||||
Header_draw(header);
|
||||
ScreenManager_resize(this->scr, this->scr->x1, header->height, this->scr->x2, this->scr->y2);
|
||||
ScreenManager_resize(this->scr);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -98,6 +98,8 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager*
|
||||
|
||||
Panel_setHeader(super, "Display options");
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("Tree view", &(settings->treeView)));
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("- Tree view is always sorted by PID (htop 2 behavior)", &(settings->treeViewAlwaysByPID)));
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("- Tree view is collapsed by default", &(settings->allBranchesCollapsed)));
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("Shadow other users' processes", &(settings->shadowOtherUsers)));
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("Hide kernel threads", &(settings->hideKernelThreads)));
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("Hide userland process threads", &(settings->hideUserlandThreads)));
|
||||
@ -105,6 +107,7 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager*
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("Show custom thread names", &(settings->showThreadNames)));
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("Show program path", &(settings->showProgramPath)));
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("Highlight program \"basename\"", &(settings->highlightBaseName)));
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("Highlight out-dated/removed programs", &(settings->highlightDeletedExe)));
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("Merge exe, comm and cmdline in Command", &(settings->showMergedCommand)));
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("- Try to find comm in cmdline (when Command is merged)", &(settings->findCommInCmdline)));
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("- Try to strip exe from cmdline (when Command is merged)", &(settings->stripExeFromCmdline)));
|
||||
@ -116,14 +119,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
|
29
EnvScreen.c
29
EnvScreen.c
@ -5,7 +5,6 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "CRT.h"
|
||||
#include "Macros.h"
|
||||
#include "Panel.h"
|
||||
#include "Platform.h"
|
||||
@ -14,40 +13,29 @@
|
||||
#include "XUtils.h"
|
||||
|
||||
|
||||
const InfoScreenClass EnvScreen_class = {
|
||||
.super = {
|
||||
.extends = Class(Object),
|
||||
.delete = EnvScreen_delete
|
||||
},
|
||||
.scan = EnvScreen_scan,
|
||||
.draw = EnvScreen_draw
|
||||
};
|
||||
|
||||
EnvScreen* EnvScreen_new(Process* process) {
|
||||
EnvScreen* this = xMalloc(sizeof(EnvScreen));
|
||||
Object_setClass(this, Class(EnvScreen));
|
||||
return (EnvScreen*) InfoScreen_init(&this->super, process, NULL, LINES - 3, " ");
|
||||
return (EnvScreen*) InfoScreen_init(&this->super, process, NULL, LINES - 2, " ");
|
||||
}
|
||||
|
||||
void EnvScreen_delete(Object* this) {
|
||||
free(InfoScreen_done((InfoScreen*)this));
|
||||
}
|
||||
|
||||
void EnvScreen_draw(InfoScreen* this) {
|
||||
static void EnvScreen_draw(InfoScreen* this) {
|
||||
InfoScreen_drawTitled(this, "Environment of process %d - %s", this->process->pid, Process_getCommand(this->process));
|
||||
}
|
||||
|
||||
void EnvScreen_scan(InfoScreen* this) {
|
||||
static void EnvScreen_scan(InfoScreen* this) {
|
||||
Panel* panel = this->display;
|
||||
int idx = MAXIMUM(Panel_getSelectedIndex(panel), 0);
|
||||
|
||||
Panel_prune(panel);
|
||||
|
||||
CRT_dropPrivileges();
|
||||
char* env = Platform_getProcessEnv(this->process->pid);
|
||||
CRT_restorePrivileges();
|
||||
if (env) {
|
||||
for (char* p = env; *p; p = strrchr(p, 0) + 1)
|
||||
for (const char* p = env; *p; p = strrchr(p, 0) + 1)
|
||||
InfoScreen_addLine(this, p);
|
||||
free(env);
|
||||
}
|
||||
@ -59,3 +47,12 @@ void EnvScreen_scan(InfoScreen* this) {
|
||||
Vector_insertionSort(panel->items);
|
||||
Panel_setSelected(panel, idx);
|
||||
}
|
||||
|
||||
const InfoScreenClass EnvScreen_class = {
|
||||
.super = {
|
||||
.extends = Class(Object),
|
||||
.delete = EnvScreen_delete
|
||||
},
|
||||
.scan = EnvScreen_scan,
|
||||
.draw = EnvScreen_draw
|
||||
};
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "Object.h"
|
||||
#include "Process.h"
|
||||
|
||||
|
||||
typedef struct EnvScreen_ {
|
||||
InfoScreen super;
|
||||
} EnvScreen;
|
||||
@ -15,8 +16,4 @@ EnvScreen* EnvScreen_new(Process* process);
|
||||
|
||||
void EnvScreen_delete(Object* this);
|
||||
|
||||
void EnvScreen_draw(InfoScreen* this);
|
||||
|
||||
void EnvScreen_scan(InfoScreen* this);
|
||||
|
||||
#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.
|
||||
*/
|
||||
|
||||
@ -112,10 +112,11 @@ void FunctionBar_drawExtra(const FunctionBar* this, const char* buffer, int attr
|
||||
attrset(attr);
|
||||
}
|
||||
mvaddstr(LINES - 1, x, buffer);
|
||||
attrset(CRT_colors[RESET_COLOR]);
|
||||
x += strlen(buffer);
|
||||
}
|
||||
|
||||
attrset(CRT_colors[RESET_COLOR]);
|
||||
|
||||
if (setCursor) {
|
||||
CRT_cursorX = x;
|
||||
curs_set(1);
|
||||
@ -132,10 +133,10 @@ void FunctionBar_append(const char* buffer, int attr) {
|
||||
} else {
|
||||
attrset(attr);
|
||||
}
|
||||
mvaddstr(LINES - 1, currentLen, buffer);
|
||||
mvaddstr(LINES - 1, currentLen + 1, buffer);
|
||||
attrset(CRT_colors[RESET_COLOR]);
|
||||
|
||||
currentLen += strlen(buffer);
|
||||
currentLen += strlen(buffer) + 1;
|
||||
}
|
||||
|
||||
int FunctionBar_synthesizeEvent(const FunctionBar* this, int pos) {
|
||||
|
@ -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;
|
||||
|
98
Hashtable.c
98
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,25 +11,44 @@ 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,
|
||||
@ -39,15 +58,15 @@ static void Hashtable_dump(const Hashtable* this) {
|
||||
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++;
|
||||
}
|
||||
@ -57,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++;
|
||||
}
|
||||
@ -79,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));
|
||||
@ -114,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));
|
||||
@ -123,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 (;;) {
|
||||
@ -166,7 +184,7 @@ static void insert(Hashtable* this, hkey_t key, void* value) {
|
||||
}
|
||||
}
|
||||
|
||||
void Hashtable_setSize(Hashtable* this, unsigned int size) {
|
||||
void Hashtable_setSize(Hashtable* this, size_t size) {
|
||||
|
||||
assert(Hashtable_isConsistent(this));
|
||||
|
||||
@ -174,14 +192,14 @@ void Hashtable_setSize(Hashtable* this, unsigned int size) {
|
||||
return;
|
||||
|
||||
HashtableItem* oldBuckets = this->buckets;
|
||||
unsigned int oldSize = this->size;
|
||||
size_t oldSize = this->size;
|
||||
|
||||
this->size = nextPrime(size);
|
||||
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;
|
||||
|
||||
@ -193,15 +211,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);
|
||||
|
||||
@ -210,11 +232,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));
|
||||
@ -229,7 +251,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];
|
||||
@ -265,12 +287,12 @@ void* Hashtable_remove(Hashtable* this, hkey_t key) {
|
||||
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));
|
||||
@ -295,7 +317,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);
|
||||
|
||||
|
265
Header.c
265
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, 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,33 +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);
|
||||
}
|
||||
|
||||
char* Header_readMeterName(Header* this, int i, int column) {
|
||||
Vector* meters = this->columns[column];
|
||||
Meter* meter = (Meter*) Vector_get(meters, i);
|
||||
|
||||
int nameLen = strlen(Meter_name(meter));
|
||||
int len = nameLen + 100;
|
||||
char* name = xMalloc(len);
|
||||
memcpy(name, Meter_name(meter), nameLen);
|
||||
name[nameLen] = '\0';
|
||||
if (meter->param)
|
||||
xSnprintf(name + nameLen, len - nameLen, "(%d)", meter->param);
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
MeterModeId Header_readMeterMode(Header* this, int i, int column) {
|
||||
Vector* meters = this->columns[column];
|
||||
|
||||
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++) {
|
||||
@ -165,35 +188,97 @@ void Header_reinit(Header* this) {
|
||||
}
|
||||
|
||||
void Header_draw(const Header* this) {
|
||||
int height = this->height;
|
||||
int pad = this->pad;
|
||||
const int height = this->height;
|
||||
const int pad = this->pad;
|
||||
attrset(CRT_colors[RESET_COLOR]);
|
||||
for (int y = 0; y < height; y++) {
|
||||
mvhline(y, 0, ' ', COLS);
|
||||
}
|
||||
int width = COLS / this->nrColumns - (pad * this->nrColumns - 1) - 1;
|
||||
const int width = COLS - pad;
|
||||
int x = pad;
|
||||
float roundingLoss = 0.0F;
|
||||
|
||||
Header_forEachColumn(this, col) {
|
||||
Vector* meters = this->columns[col];
|
||||
float colWidth = (float)width * HeaderLayout_layouts[this->headerLayout].widths[col] / 100.0F;
|
||||
|
||||
roundingLoss += colWidth - floorf(colWidth);
|
||||
if (roundingLoss >= 1.0F) {
|
||||
colWidth += 1.0F;
|
||||
roundingLoss -= 1.0F;
|
||||
}
|
||||
|
||||
for (int y = (pad / 2), i = 0; i < Vector_size(meters); i++) {
|
||||
Meter* meter = (Meter*) Vector_get(meters, i);
|
||||
meter->draw(meter, x, y, width);
|
||||
|
||||
float actualWidth = colWidth;
|
||||
|
||||
/* 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 += (float)width * HeaderLayout_layouts[this->headerLayout].widths[col + j] / 100.0F;
|
||||
}
|
||||
}
|
||||
|
||||
assert(meter->draw);
|
||||
meter->draw(meter, x, y, floorf(actualWidth));
|
||||
y += meter->h;
|
||||
}
|
||||
x += width + pad;
|
||||
|
||||
x += floorf(colWidth);
|
||||
}
|
||||
}
|
||||
|
||||
void Header_updateData(Header* this) {
|
||||
Header_forEachColumn(this, col) {
|
||||
Vector* meters = this->columns[col];
|
||||
int items = Vector_size(meters);
|
||||
for (int i = 0; i < items; i++) {
|
||||
Meter* meter = (Meter*) Vector_get(meters, i);
|
||||
Meter_updateValues(meter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate how many columns the current meter is allowed to span,
|
||||
* by counting how many columns to the right are empty or contain a BlankMeter.
|
||||
* Returns the number of columns to span, i.e. if the direct neighbor is occupied 1.
|
||||
*/
|
||||
static int calcColumnWidthCount(const Header* this, const Meter* curMeter, const int pad, const unsigned int curColumn, const int curHeight) {
|
||||
for (size_t i = curColumn + 1; i < HeaderLayout_getColumns(this->headerLayout); i++) {
|
||||
const Vector* meters = this->columns[i];
|
||||
|
||||
int height = pad;
|
||||
for (int j = 0; j < Vector_size(meters); j++) {
|
||||
const Meter* meter = (const Meter*) Vector_get(meters, j);
|
||||
|
||||
if (height >= curHeight + curMeter->h)
|
||||
break;
|
||||
|
||||
height += meter->h;
|
||||
if (height <= curHeight)
|
||||
continue;
|
||||
|
||||
if (!Object_isA((const Object*) meter, (const ObjectClass*) &BlankMeter_class))
|
||||
return i - curColumn;
|
||||
}
|
||||
}
|
||||
|
||||
return HeaderLayout_getColumns(this->headerLayout) - curColumn;
|
||||
}
|
||||
|
||||
int Header_calculateHeight(Header* this) {
|
||||
int pad = this->settings->headerMargin ? 2 : 0;
|
||||
const int pad = this->settings->headerMargin ? 2 : 0;
|
||||
int maxHeight = pad;
|
||||
|
||||
Header_forEachColumn(this, col) {
|
||||
Vector* meters = this->columns[col];
|
||||
const Vector* meters = this->columns[col];
|
||||
int height = pad;
|
||||
for (int i = 0; i < Vector_size(meters); i++) {
|
||||
Meter* meter = (Meter*) Vector_get(meters, i);
|
||||
meter->columnWidthCount = calcColumnWidthCount(this, meter, pad, col, height);
|
||||
height += meter->h;
|
||||
}
|
||||
maxHeight = MAXIMUM(maxHeight, height);
|
||||
|
26
Header.h
26
Header.h
@ -3,50 +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);
|
||||
|
||||
char* Header_readMeterName(Header* this, int i, int column);
|
||||
|
||||
MeterModeId Header_readMeterMode(Header* this, int i, int column);
|
||||
Meter* Header_addMeterByClass(Header* this, const MeterClass* type, unsigned int param, unsigned int column);
|
||||
|
||||
void Header_reinit(Header* this);
|
||||
|
||||
void Header_draw(const Header* this);
|
||||
|
||||
void Header_updateData(Header* this);
|
||||
|
||||
int Header_calculateHeight(Header* this);
|
||||
|
||||
#endif
|
||||
|
77
HeaderLayout.h
Normal file
77
HeaderLayout.h
Normal file
@ -0,0 +1,77 @@
|
||||
#ifndef HEADER_HeaderLayout
|
||||
#define HEADER_HeaderLayout
|
||||
/*
|
||||
htop - HeaderLayout.h
|
||||
(C) 2021 htop dev team
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
#include "config.h" // IWYU pragma: keep
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "Macros.h"
|
||||
#include "XUtils.h"
|
||||
|
||||
|
||||
typedef enum HeaderLayout_ {
|
||||
HF_TWO_50_50,
|
||||
HF_TWO_33_67,
|
||||
HF_TWO_67_33,
|
||||
HF_THREE_33_34_33,
|
||||
HF_THREE_25_25_50,
|
||||
HF_THREE_25_50_25,
|
||||
HF_THREE_50_25_25,
|
||||
HF_THREE_40_20_40,
|
||||
HF_FOUR_25_25_25_25,
|
||||
LAST_HEADER_LAYOUT
|
||||
} HeaderLayout;
|
||||
|
||||
static const struct {
|
||||
uint8_t columns;
|
||||
const uint8_t widths[4];
|
||||
const char* name;
|
||||
const char* description;
|
||||
} HeaderLayout_layouts[LAST_HEADER_LAYOUT] = {
|
||||
[HF_TWO_50_50] = { 2, { 50, 50, 0, 0 }, "two_50_50", "2 columns - 50/50 (default)", },
|
||||
[HF_TWO_33_67] = { 2, { 33, 67, 0, 0 }, "two_33_67", "2 columns - 33/67", },
|
||||
[HF_TWO_67_33] = { 2, { 67, 33, 0, 0 }, "two_67_33", "2 columns - 67/33", },
|
||||
[HF_THREE_33_34_33] = { 3, { 33, 34, 33, 0 }, "three_33_34_33", "3 columns - 33/34/33", },
|
||||
[HF_THREE_25_25_50] = { 3, { 25, 25, 50, 0 }, "three_25_25_50", "3 columns - 25/25/50", },
|
||||
[HF_THREE_25_50_25] = { 3, { 25, 50, 25, 0 }, "three_25_50_25", "3 columns - 25/50/25", },
|
||||
[HF_THREE_50_25_25] = { 3, { 50, 25, 25, 0 }, "three_50_25_25", "3 columns - 50/25/25", },
|
||||
[HF_THREE_40_20_40] = { 3, { 40, 20, 40, 0 }, "three_40_20_40", "3 columns - 40/20/40", },
|
||||
[HF_FOUR_25_25_25_25] = { 4, { 25, 25, 25, 25 }, "four_25_25_25_25", "4 columns - 25/25/25/25", },
|
||||
};
|
||||
|
||||
static inline size_t HeaderLayout_getColumns(HeaderLayout hLayout) {
|
||||
/* assert the layout is initialized */
|
||||
assert(0 <= hLayout);
|
||||
assert(hLayout < LAST_HEADER_LAYOUT);
|
||||
assert(HeaderLayout_layouts[hLayout].name[0]);
|
||||
assert(HeaderLayout_layouts[hLayout].description[0]);
|
||||
return HeaderLayout_layouts[hLayout].columns;
|
||||
}
|
||||
|
||||
static inline const char* HeaderLayout_getName(HeaderLayout hLayout) {
|
||||
/* assert the layout is initialized */
|
||||
assert(0 <= hLayout);
|
||||
assert(hLayout < LAST_HEADER_LAYOUT);
|
||||
assert(HeaderLayout_layouts[hLayout].name[0]);
|
||||
assert(HeaderLayout_layouts[hLayout].description[0]);
|
||||
return HeaderLayout_layouts[hLayout].name;
|
||||
}
|
||||
|
||||
static inline HeaderLayout HeaderLayout_fromName(const char* name) {
|
||||
for (size_t i = 0; i < LAST_HEADER_LAYOUT; i++) {
|
||||
if (String_eq(HeaderLayout_layouts[i].name, name))
|
||||
return (HeaderLayout) i;
|
||||
}
|
||||
|
||||
return LAST_HEADER_LAYOUT;
|
||||
}
|
||||
|
||||
#endif /* HEADER_HeaderLayout */
|
87
HeaderOptionsPanel.c
Normal file
87
HeaderOptionsPanel.c
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
htop - HeaderOptionsPanel.c
|
||||
(C) 2021 htop dev team
|
||||
Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
#include "HeaderOptionsPanel.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "CRT.h"
|
||||
#include "FunctionBar.h"
|
||||
#include "Header.h"
|
||||
#include "HeaderLayout.h"
|
||||
#include "Object.h"
|
||||
#include "OptionItem.h"
|
||||
#include "ProvideCurses.h"
|
||||
|
||||
|
||||
static const char* const HeaderOptionsFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL};
|
||||
|
||||
static void HeaderOptionsPanel_delete(Object* object) {
|
||||
Panel* super = (Panel*) object;
|
||||
HeaderOptionsPanel* this = (HeaderOptionsPanel*) object;
|
||||
Panel_done(super);
|
||||
free(this);
|
||||
}
|
||||
|
||||
static HandlerResult HeaderOptionsPanel_eventHandler(Panel* super, int ch) {
|
||||
HeaderOptionsPanel* this = (HeaderOptionsPanel*) super;
|
||||
|
||||
HandlerResult result = IGNORED;
|
||||
int mark;
|
||||
|
||||
switch(ch) {
|
||||
case 0x0a:
|
||||
case 0x0d:
|
||||
case KEY_ENTER:
|
||||
case KEY_MOUSE:
|
||||
case KEY_RECLICK:
|
||||
case ' ':
|
||||
mark = Panel_getSelectedIndex(super);
|
||||
assert(mark >= 0);
|
||||
assert(mark < LAST_HEADER_LAYOUT);
|
||||
|
||||
for (int i = 0; i < LAST_HEADER_LAYOUT; i++)
|
||||
CheckItem_set((CheckItem*)Panel_get(super, i), false);
|
||||
CheckItem_set((CheckItem*)Panel_get(super, mark), true);
|
||||
|
||||
Header_setLayout(this->scr->header, mark);
|
||||
this->settings->changed = true;
|
||||
|
||||
ScreenManager_resize(this->scr);
|
||||
|
||||
result = HANDLED;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const PanelClass HeaderOptionsPanel_class = {
|
||||
.super = {
|
||||
.extends = Class(Panel),
|
||||
.delete = HeaderOptionsPanel_delete
|
||||
},
|
||||
.eventHandler = HeaderOptionsPanel_eventHandler
|
||||
};
|
||||
|
||||
HeaderOptionsPanel* HeaderOptionsPanel_new(Settings* settings, ScreenManager* scr) {
|
||||
HeaderOptionsPanel* this = AllocThis(HeaderOptionsPanel);
|
||||
Panel* super = (Panel*) this;
|
||||
FunctionBar* fuBar = FunctionBar_new(HeaderOptionsFunctions, NULL, NULL);
|
||||
Panel_init(super, 1, 1, 1, 1, Class(CheckItem), true, fuBar);
|
||||
|
||||
this->scr = scr;
|
||||
this->settings = settings;
|
||||
|
||||
Panel_setHeader(super, "Header Layout");
|
||||
for (int i = 0; i < LAST_HEADER_LAYOUT; i++) {
|
||||
Panel_add(super, (Object*) CheckItem_newByVal(HeaderLayout_layouts[i].description, false));
|
||||
}
|
||||
CheckItem_set((CheckItem*)Panel_get(super, 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, int 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
|
||||
|
64
IncSet.c
64
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 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));
|
||||
@ -41,7 +48,7 @@ static inline void IncMode_initSearch(IncMode* search) {
|
||||
|
||||
static const char* const filterFunctions[] = {"Done ", "Clear ", " Filter: ", NULL};
|
||||
static const char* const filterKeys[] = {"Enter", "Esc", " "};
|
||||
static int filterEvents[] = {13, 27, ERR};
|
||||
static const int filterEvents[] = {13, 27, ERR};
|
||||
|
||||
static inline void IncMode_initFilter(IncMode* filter) {
|
||||
memset(filter, 0, sizeof(IncMode));
|
||||
@ -54,12 +61,13 @@ static inline void IncMode_done(IncMode* mode) {
|
||||
}
|
||||
|
||||
IncSet* IncSet_new(FunctionBar* bar) {
|
||||
IncSet* this = xCalloc(1, sizeof(IncSet));
|
||||
IncSet* this = xMalloc(sizeof(IncSet));
|
||||
IncMode_initSearch(&(this->modes[INC_SEARCH]));
|
||||
IncMode_initFilter(&(this->modes[INC_FILTER]));
|
||||
this->active = NULL;
|
||||
this->filtering = false;
|
||||
this->defaultBar = bar;
|
||||
this->filtering = false;
|
||||
this->found = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -69,8 +77,8 @@ void IncSet_delete(IncSet* this) {
|
||||
free(this);
|
||||
}
|
||||
|
||||
static void updateWeakPanel(IncSet* this, Panel* panel, Vector* lines) {
|
||||
Object* selected = Panel_getSelected(panel);
|
||||
static void updateWeakPanel(const IncSet* this, Panel* panel, Vector* lines) {
|
||||
const Object* selected = Panel_getSelected(panel);
|
||||
Panel_prune(panel);
|
||||
if (this->filtering) {
|
||||
int n = 0;
|
||||
@ -97,25 +105,19 @@ static void updateWeakPanel(IncSet* this, Panel* panel, Vector* lines) {
|
||||
}
|
||||
}
|
||||
|
||||
static bool search(IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelValue) {
|
||||
static bool search(const IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelValue) {
|
||||
int size = Panel_size(panel);
|
||||
bool found = false;
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (String_contains_i(getPanelValue(panel, i), mode->buffer)) {
|
||||
Panel_setSelected(panel, i);
|
||||
found = true;
|
||||
break;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
FunctionBar_drawExtra(mode->bar,
|
||||
mode->buffer,
|
||||
found ? -1 : CRT_colors[FAILED_SEARCH],
|
||||
true);
|
||||
return found;
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool IncMode_find(IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelValue, int step) {
|
||||
static bool IncMode_find(const IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelValue, int step) {
|
||||
int size = Panel_size(panel);
|
||||
int here = Panel_getSelectedIndex(panel);
|
||||
int i = here;
|
||||
@ -138,14 +140,6 @@ static bool IncMode_find(IncMode* mode, Panel* panel, IncMode_GetPanelValue getP
|
||||
}
|
||||
}
|
||||
|
||||
bool IncSet_next(IncSet* this, IncType type, Panel* panel, IncMode_GetPanelValue getPanelValue) {
|
||||
return IncMode_find(&this->modes[type], panel, getPanelValue, 1);
|
||||
}
|
||||
|
||||
bool IncSet_prev(IncSet* this, IncType type, Panel* panel, IncMode_GetPanelValue getPanelValue) {
|
||||
return IncMode_find(&this->modes[type], panel, getPanelValue, -1);
|
||||
}
|
||||
|
||||
bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue getPanelValue, Vector* lines) {
|
||||
if (ch == ERR)
|
||||
return true;
|
||||
@ -154,15 +148,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) {
|
||||
@ -172,7 +166,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;
|
||||
@ -187,7 +181,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;
|
||||
@ -202,7 +196,6 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue
|
||||
}
|
||||
this->active = NULL;
|
||||
Panel_setDefaultBar(panel);
|
||||
FunctionBar_draw(this->defaultBar);
|
||||
doSearch = false;
|
||||
}
|
||||
if (doSearch) {
|
||||
@ -215,19 +208,18 @@ 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]);
|
||||
FunctionBar_drawExtra(this->active->bar, this->active->buffer, -1, true);
|
||||
panel->currentBar = this->active->bar;
|
||||
}
|
||||
|
||||
void IncSet_drawBar(const IncSet* this) {
|
||||
if (this->active) {
|
||||
FunctionBar_drawExtra(this->active->bar, this->active->buffer, -1, true);
|
||||
FunctionBar_drawExtra(this->active->bar, this->active->buffer, (this->active->isFilter || this->found) ? -1 : CRT_colors[FAILED_SEARCH], true);
|
||||
} else {
|
||||
FunctionBar_draw(this->defaultBar);
|
||||
}
|
||||
|
9
IncSet.h
9
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 {
|
||||
@ -40,6 +41,8 @@ static inline const char* IncSet_filter(const IncSet* this) {
|
||||
return this->filtering ? this->modes[INC_FILTER].buffer : NULL;
|
||||
}
|
||||
|
||||
void IncSet_setFilter(IncSet* this, const char* filter);
|
||||
|
||||
typedef const char* (*IncMode_GetPanelValue)(Panel*, int);
|
||||
|
||||
void IncSet_reset(IncSet* this, IncType type);
|
||||
@ -48,10 +51,6 @@ 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);
|
||||
|
47
InfoScreen.c
47
InfoScreen.c
@ -4,7 +4,6 @@
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "CRT.h"
|
||||
@ -19,16 +18,16 @@ static const char* const InfoScreenFunctions[] = {"Search ", "Filter ", "Refresh
|
||||
|
||||
static const char* const InfoScreenKeys[] = {"F3", "F4", "F5", "Esc"};
|
||||
|
||||
static int InfoScreenEvents[] = {KEY_F(3), KEY_F(4), KEY_F(5), 27};
|
||||
static const int InfoScreenEvents[] = {KEY_F(3), KEY_F(4), KEY_F(5), 27};
|
||||
|
||||
InfoScreen* InfoScreen_init(InfoScreen* this, const Process* process, FunctionBar* bar, int height, const char* panelHeader) {
|
||||
this->process = process;
|
||||
if (!bar) {
|
||||
bar = FunctionBar_new(InfoScreenFunctions, InfoScreenKeys, InfoScreenEvents);
|
||||
}
|
||||
this->display = Panel_new(0, 1, COLS, height, false, Class(ListItem), bar);
|
||||
this->display = Panel_new(0, 1, COLS, height, Class(ListItem), false, bar);
|
||||
this->inc = IncSet_new(bar);
|
||||
this->lines = Vector_new(this->display->items->type, true, DEFAULT_SIZE);
|
||||
this->lines = Vector_new(Vector_type(this->display->items), true, DEFAULT_SIZE);
|
||||
Panel_setHeader(this->display, panelHeader);
|
||||
return this;
|
||||
}
|
||||
@ -44,21 +43,21 @@ void InfoScreen_drawTitled(InfoScreen* this, const char* fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
|
||||
char* title = xMalloc(COLS + 1);
|
||||
int len = vsnprintf(title, COLS + 1, fmt, ap);
|
||||
char title[COLS + 1];
|
||||
int len = vsnprintf(title, sizeof(title), fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (len > COLS) {
|
||||
memset(&title[COLS - 3], '.', 3);
|
||||
}
|
||||
|
||||
attrset(CRT_colors[METER_TEXT]);
|
||||
mvhline(0, 0, ' ', COLS);
|
||||
mvwprintw(stdscr, 0, 0, title);
|
||||
mvaddstr(0, 0, title);
|
||||
attrset(CRT_colors[DEFAULT_COLOR]);
|
||||
this->display->needsRedraw = true;
|
||||
Panel_draw(this->display, true, true);
|
||||
Panel_draw(this->display, true, true, true, false);
|
||||
|
||||
IncSet_drawBar(this->inc);
|
||||
free(title);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void InfoScreen_addLine(InfoScreen* this, const char* line) {
|
||||
@ -89,12 +88,15 @@ void InfoScreen_run(InfoScreen* this) {
|
||||
bool looping = true;
|
||||
while (looping) {
|
||||
|
||||
Panel_draw(panel, true, true);
|
||||
Panel_draw(panel, false, true, true, false);
|
||||
IncSet_drawBar(this->inc);
|
||||
|
||||
if (this->inc->active) {
|
||||
(void) move(LINES - 1, CRT_cursorX);
|
||||
}
|
||||
#ifdef HAVE_SET_ESCDELAY
|
||||
set_escdelay(25);
|
||||
#endif
|
||||
int ch = getch();
|
||||
|
||||
if (ch == ERR) {
|
||||
@ -104,18 +106,29 @@ void InfoScreen_run(InfoScreen* this) {
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_GETMOUSE
|
||||
if (ch == KEY_MOUSE) {
|
||||
MEVENT mevent;
|
||||
int ok = getmouse(&mevent);
|
||||
if (ok == OK) {
|
||||
if (mevent.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);
|
||||
@ -135,8 +148,10 @@ void InfoScreen_run(InfoScreen* this) {
|
||||
break;
|
||||
case KEY_F(5):
|
||||
clear();
|
||||
if (As_InfoScreen(this)->scan)
|
||||
if (As_InfoScreen(this)->scan) {
|
||||
Vector_prune(this->lines);
|
||||
InfoScreen_scan(this);
|
||||
}
|
||||
|
||||
InfoScreen_draw(this);
|
||||
break;
|
||||
@ -151,8 +166,10 @@ void InfoScreen_run(InfoScreen* this) {
|
||||
break;
|
||||
case KEY_RESIZE:
|
||||
Panel_resize(panel, COLS, LINES - 2);
|
||||
if (As_InfoScreen(this)->scan)
|
||||
if (As_InfoScreen(this)->scan) {
|
||||
Vector_prune(this->lines);
|
||||
InfoScreen_scan(this);
|
||||
}
|
||||
|
||||
InfoScreen_draw(this);
|
||||
break;
|
||||
|
@ -16,7 +16,6 @@ typedef struct InfoScreen_ {
|
||||
Object super;
|
||||
const Process* process;
|
||||
Panel* display;
|
||||
FunctionBar* bar;
|
||||
IncSet* inc;
|
||||
Vector* lines;
|
||||
} InfoScreen;
|
||||
|
10
ListItem.c
10
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.
|
||||
*/
|
||||
|
||||
@ -29,15 +29,13 @@ static void ListItem_display(const Object* cast, RichString* out) {
|
||||
assert (this != NULL);
|
||||
|
||||
if (this->moving) {
|
||||
RichString_write(out, CRT_colors[DEFAULT_COLOR],
|
||||
RichString_writeWide(out, CRT_colors[DEFAULT_COLOR],
|
||||
#ifdef HAVE_LIBNCURSESW
|
||||
CRT_utf8 ? "↕ " :
|
||||
#endif
|
||||
"+ ");
|
||||
} else {
|
||||
RichString_prune(out);
|
||||
}
|
||||
RichString_append(out, CRT_colors[DEFAULT_COLOR], this->value);
|
||||
RichString_appendWide(out, CRT_colors[DEFAULT_COLOR], this->value);
|
||||
}
|
||||
|
||||
ListItem* ListItem_new(const char* value, int key) {
|
||||
@ -57,7 +55,7 @@ void ListItem_append(ListItem* this, const char* text) {
|
||||
this->value[newLen] = '\0';
|
||||
}
|
||||
|
||||
static long ListItem_compare(const void* cast1, const void* cast2) {
|
||||
static int ListItem_compare(const void* cast1, const void* cast2) {
|
||||
const ListItem* obj1 = (const ListItem*) cast1;
|
||||
const ListItem* obj2 = (const ListItem*) cast2;
|
||||
return strcmp(obj1->value, obj2->value);
|
||||
|
@ -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;
|
||||
|
@ -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, int size) {
|
||||
static const int OK_attributes[] = {
|
||||
METER_VALUE_OK
|
||||
};
|
||||
|
||||
static const int Medium_attributes[] = {
|
||||
METER_VALUE_WARN
|
||||
};
|
||||
|
||||
static const int High_attributes[] = {
|
||||
METER_VALUE_ERROR
|
||||
};
|
||||
|
||||
static void LoadAverageMeter_updateValues(Meter* this) {
|
||||
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_write(out, CRT_colors[LOAD_AVERAGE_ONE], buffer);
|
||||
xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[1]);
|
||||
RichString_append(out, CRT_colors[LOAD_AVERAGE_FIVE], buffer);
|
||||
xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[2]);
|
||||
RichString_append(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, int 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_write(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
|
||||
|
77
MainPanel.c
77
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,18 @@ 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) {
|
||||
FunctionBar* bar = MainPanel_getFunctionBar(this);
|
||||
FunctionBar_setLabel(bar, KEY_F(5), mode ? "Sorted" : "Tree ");
|
||||
FunctionBar_setLabel(bar, KEY_F(5), mode ? "List " : "Tree ");
|
||||
}
|
||||
|
||||
void MainPanel_pidSearch(MainPanel* this, int ch) {
|
||||
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 +45,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,7 +57,18 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
|
||||
|
||||
Htop_Reaction reaction = HTOP_OK;
|
||||
|
||||
if (ch != ERR)
|
||||
/* 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;
|
||||
|
||||
if (EVENT_IS_HEADER_CLICK(ch)) {
|
||||
@ -60,7 +77,11 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
|
||||
Settings* settings = this->state->settings;
|
||||
int hx = super->scrollH + x + 1;
|
||||
ProcessField field = ProcessList_keyAt(pl, hx);
|
||||
if (field == settings->sortKey) {
|
||||
if (settings->treeView && settings->treeViewAlwaysByPID) {
|
||||
settings->treeView = false;
|
||||
settings->direction = 1;
|
||||
reaction |= Action_setSortKey(settings, field);
|
||||
} else if (field == Settings_getActiveSortKey(settings)) {
|
||||
Settings_invertSortOrder(settings);
|
||||
} else {
|
||||
reaction |= Action_setSortKey(settings, field);
|
||||
@ -68,7 +89,7 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
|
||||
reaction |= HTOP_RECALCULATE | HTOP_REDRAW_BAR | HTOP_SAVE_SETTINGS;
|
||||
result = HANDLED;
|
||||
} else if (ch != ERR && this->inc->active) {
|
||||
bool filterChanged = IncSet_handleKey(this->inc, ch, super, (IncMode_GetPanelValue) MainPanel_getValue, NULL);
|
||||
bool filterChanged = IncSet_handleKey(this->inc, ch, super, MainPanel_getValue, NULL);
|
||||
if (filterChanged) {
|
||||
this->state->pl->incFilter = IncSet_filter(this->inc);
|
||||
reaction = HTOP_REFRESH | HTOP_REDRAW_BAR;
|
||||
@ -96,16 +117,15 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
|
||||
|
||||
if (reaction & HTOP_REDRAW_BAR) {
|
||||
MainPanel_updateTreeFunctions(this, this->state->settings->treeView);
|
||||
IncSet_drawBar(this->inc);
|
||||
if (this->state->pauseProcessUpdate) {
|
||||
FunctionBar_append("PAUSED", CRT_colors[PAUSED]);
|
||||
}
|
||||
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 |= REDRAW;
|
||||
result |= REFRESH;
|
||||
}
|
||||
if (reaction & HTOP_RECALCULATE) {
|
||||
result |= RESCAN;
|
||||
@ -118,24 +138,19 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
|
||||
}
|
||||
if (!(reaction & HTOP_KEEP_FOLLOWING)) {
|
||||
this->state->pl->following = -1;
|
||||
Panel_setSelectionColor(super, CRT_colors[PANEL_SELECTION_FOCUS]);
|
||||
Panel_setSelectionColor(super, PANEL_SELECTION_FOCUS);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
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,17 +175,37 @@ bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, Ar
|
||||
return ok;
|
||||
}
|
||||
|
||||
static void MainPanel_drawFunctionBar(Panel* super, bool hideFunctionBar) {
|
||||
MainPanel* this = (MainPanel*) super;
|
||||
|
||||
// Do not hide active search and filter bar.
|
||||
if (hideFunctionBar && !this->inc->active)
|
||||
return;
|
||||
|
||||
IncSet_drawBar(this->inc);
|
||||
if (this->state->pauseProcessUpdate) {
|
||||
FunctionBar_append("PAUSED", CRT_colors[PAUSED]);
|
||||
}
|
||||
}
|
||||
|
||||
static void MainPanel_printHeader(Panel* super) {
|
||||
MainPanel* this = (MainPanel*) super;
|
||||
ProcessList_printHeader(this->state->pl, &super->header);
|
||||
}
|
||||
|
||||
const PanelClass MainPanel_class = {
|
||||
.super = {
|
||||
.extends = Class(Panel),
|
||||
.delete = MainPanel_delete
|
||||
},
|
||||
.eventHandler = MainPanel_eventHandler
|
||||
.eventHandler = MainPanel_eventHandler,
|
||||
.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.
|
||||
*/
|
||||
|
||||
@ -34,12 +34,8 @@ typedef bool(*MainPanel_ForeachProcessFn)(Process*, Arg);
|
||||
|
||||
void MainPanel_updateTreeFunctions(MainPanel* this, bool mode);
|
||||
|
||||
void MainPanel_pidSearch(MainPanel* this, int ch);
|
||||
|
||||
int MainPanel_selectedPid(MainPanel* this);
|
||||
|
||||
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;
|
||||
|
252
Makefile.am
252
Makefile.am
@ -1,16 +1,30 @@
|
||||
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 \
|
||||
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
|
||||
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 = \
|
||||
@ -24,6 +38,7 @@ myhtopsources = \
|
||||
ClockMeter.c \
|
||||
ColorsPanel.c \
|
||||
ColumnsPanel.c \
|
||||
CommandLine.c \
|
||||
CommandScreen.c \
|
||||
Compat.c \
|
||||
CPUMeter.c \
|
||||
@ -32,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 \
|
||||
@ -59,6 +77,7 @@ myhtopsources = \
|
||||
Settings.c \
|
||||
SignalsPanel.c \
|
||||
SwapMeter.c \
|
||||
SysArchMeter.c \
|
||||
TasksMeter.c \
|
||||
TraceScreen.c \
|
||||
UptimeMeter.c \
|
||||
@ -79,16 +98,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 \
|
||||
@ -97,6 +121,7 @@ myhtopheaders = \
|
||||
Macros.h \
|
||||
MainPanel.h \
|
||||
MemoryMeter.h \
|
||||
MemorySwapMeter.h \
|
||||
Meter.h \
|
||||
MetersPanel.h \
|
||||
NetworkIOMeter.h \
|
||||
@ -113,6 +138,7 @@ myhtopheaders = \
|
||||
Settings.h \
|
||||
SignalsPanel.h \
|
||||
SwapMeter.h \
|
||||
SysArchMeter.h \
|
||||
TasksMeter.h \
|
||||
TraceScreen.h \
|
||||
UptimeMeter.h \
|
||||
@ -124,6 +150,10 @@ myhtopheaders = \
|
||||
# -----
|
||||
|
||||
linux_platform_headers = \
|
||||
generic/gettime.h \
|
||||
generic/hostname.h \
|
||||
generic/uname.h \
|
||||
linux/HugePageMeter.h \
|
||||
linux/IOPriority.h \
|
||||
linux/IOPriorityPanel.h \
|
||||
linux/LibSensors.h \
|
||||
@ -131,6 +161,7 @@ linux_platform_headers = \
|
||||
linux/LinuxProcessList.h \
|
||||
linux/Platform.h \
|
||||
linux/PressureStallMeter.h \
|
||||
linux/ProcessField.h \
|
||||
linux/SELinuxMeter.h \
|
||||
linux/SystemdMeter.h \
|
||||
linux/ZramMeter.h \
|
||||
@ -139,9 +170,11 @@ linux_platform_headers = \
|
||||
zfs/ZfsArcStats.h \
|
||||
zfs/ZfsCompressedArcMeter.h
|
||||
|
||||
if HTOP_LINUX
|
||||
AM_LDFLAGS += -rdynamic
|
||||
myhtopplatsources = \
|
||||
linux_platform_sources = \
|
||||
generic/gettime.c \
|
||||
generic/hostname.c \
|
||||
generic/uname.c \
|
||||
linux/HugePageMeter.c \
|
||||
linux/IOPriorityPanel.c \
|
||||
linux/LibSensors.c \
|
||||
linux/LinuxProcess.c \
|
||||
@ -152,121 +185,246 @@ 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
|
||||
# -------
|
||||
|
||||
freebsd_platform_headers = \
|
||||
freebsd/Platform.h \
|
||||
freebsd/FreeBSDProcessList.h \
|
||||
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
|
||||
# ------------
|
||||
|
||||
dragonflybsd_platform_headers = \
|
||||
dragonflybsd/Platform.h \
|
||||
dragonflybsd/DragonFlyBSDProcessList.h \
|
||||
dragonflybsd/DragonFlyBSDProcess.h
|
||||
dragonflybsd/DragonFlyBSDProcess.h \
|
||||
dragonflybsd/Platform.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
|
||||
AM_LDFLAGS += -lkvm -lkinfo
|
||||
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 = \
|
||||
openbsd/Platform.h \
|
||||
generic/gettime.h \
|
||||
generic/hostname.h \
|
||||
generic/uname.h \
|
||||
openbsd/OpenBSDProcessList.h \
|
||||
openbsd/OpenBSDProcess.h
|
||||
openbsd/OpenBSDProcess.h \
|
||||
openbsd/Platform.h \
|
||||
openbsd/ProcessField.h
|
||||
|
||||
openbsd_platform_sources = \
|
||||
generic/gettime.c \
|
||||
generic/hostname.c \
|
||||
generic/uname.c \
|
||||
openbsd/OpenBSDProcessList.c \
|
||||
openbsd/OpenBSDProcess.c \
|
||||
openbsd/Platform.c
|
||||
|
||||
if HTOP_OPENBSD
|
||||
myhtopplatsources = openbsd/Platform.c openbsd/OpenBSDProcessList.c \
|
||||
openbsd/OpenBSDProcess.c
|
||||
|
||||
myhtopplatheaders = $(openbsd_platform_headers)
|
||||
myhtopplatsources = $(openbsd_platform_sources)
|
||||
endif
|
||||
|
||||
# Darwin
|
||||
# ------
|
||||
|
||||
darwin_platform_headers = \
|
||||
darwin/Platform.h \
|
||||
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 = \
|
||||
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:
|
||||
@ -276,10 +434,10 @@ profile:
|
||||
$(MAKE) all AM_CPPFLAGS="-pg -O2 -DNDEBUG"
|
||||
|
||||
debug:
|
||||
$(MAKE) all AM_CPPFLAGS="-ggdb -DDEBUG"
|
||||
$(MAKE) all AM_CPPFLAGS="-ggdb3 -Og" CFLAGS="`printf ' %s ' "$(CFLAGS)"|sed -E 's#[[:space:]]-O[^[:space:]]+[[:space:]]# #g'` -ggdb3 -Og"
|
||||
|
||||
coverage:
|
||||
$(MAKE) all AM_CPPFLAGS="-fprofile-arcs -ftest-coverage -DDEBUG" AM_LDFLAGS="-lgcov"
|
||||
$(MAKE) all AM_CPPFLAGS="-fprofile-arcs -ftest-coverage" AM_LDFLAGS="-lgcov"
|
||||
|
||||
cppcheck:
|
||||
cppcheck -q -v . --enable=all -DHAVE_OPENVZ
|
||||
|
@ -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,37 +19,64 @@ 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, int 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);
|
||||
buffer += written;
|
||||
if ((size -= written) > 0) {
|
||||
*buffer++ = '/';
|
||||
size--;
|
||||
METER_BUFFER_CHECK(buffer, size, written);
|
||||
|
||||
METER_BUFFER_APPEND_CHR(buffer, size, '/');
|
||||
|
||||
Meter_humanUnit(buffer, this->total, size);
|
||||
}
|
||||
}
|
||||
|
||||
static void MemoryMeter_display(const Object* cast, RichString* out) {
|
||||
char buffer[50];
|
||||
const Meter* this = (const Meter*)cast;
|
||||
RichString_write(out, CRT_colors[METER_TEXT], ":");
|
||||
Meter_humanUnit(buffer, this->total, 50);
|
||||
RichString_append(out, CRT_colors[METER_VALUE], buffer);
|
||||
Meter_humanUnit(buffer, this->values[0], 50);
|
||||
RichString_append(out, CRT_colors[METER_TEXT], " used:");
|
||||
RichString_append(out, CRT_colors[MEMORY_USED], buffer);
|
||||
Meter_humanUnit(buffer, this->values[1], 50);
|
||||
RichString_append(out, CRT_colors[METER_TEXT], " buffers:");
|
||||
RichString_append(out, CRT_colors[MEMORY_BUFFERS_TEXT], buffer);
|
||||
Meter_humanUnit(buffer, this->values[2], 50);
|
||||
RichString_append(out, CRT_colors[METER_TEXT], " cache:");
|
||||
RichString_append(out, CRT_colors[MEMORY_CACHE], buffer);
|
||||
|
||||
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 = {
|
||||
@ -57,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
|
185
Meter.c
185
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.
|
||||
*/
|
||||
|
||||
@ -20,6 +20,7 @@ in the source distribution for its full text.
|
||||
#include "Object.h"
|
||||
#include "ProvideCurses.h"
|
||||
#include "RichString.h"
|
||||
#include "Settings.h"
|
||||
#include "XUtils.h"
|
||||
|
||||
|
||||
@ -31,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);
|
||||
@ -48,10 +50,10 @@ Meter* Meter_new(const struct ProcessList_* pl, int param, const MeterClass* typ
|
||||
return this;
|
||||
}
|
||||
|
||||
int Meter_humanUnit(char* buffer, unsigned long int value, int size) {
|
||||
int Meter_humanUnit(char* buffer, unsigned long int value, size_t size) {
|
||||
const char* prefix = "KMGTPEZY";
|
||||
unsigned long int powi = 1;
|
||||
unsigned int written, powj = 1, precision = 2;
|
||||
unsigned int powj = 1, precision = 2;
|
||||
|
||||
for (;;) {
|
||||
if (value / 1024 < powi)
|
||||
@ -73,10 +75,7 @@ int Meter_humanUnit(char* buffer, unsigned long int value, int size) {
|
||||
break;
|
||||
}
|
||||
|
||||
written = snprintf(buffer, size, "%.*f%c",
|
||||
precision, (double) value / powi, *prefix);
|
||||
|
||||
return written;
|
||||
return snprintf(buffer, size, "%.*f%c", precision, (double) value / powi, *prefix);
|
||||
}
|
||||
|
||||
void Meter_delete(Object* cast) {
|
||||
@ -94,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_write(out, CRT_colors[Meter_attributes(this)[0]], buffer);
|
||||
RichString_writeWide(out, CRT_colors[Meter_attributes(this)[0]], this->txtBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
@ -133,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;
|
||||
@ -156,19 +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, METER_BUFFER_LEN - 1);
|
||||
(void) w;
|
||||
|
||||
const char* caption = Meter_getCaption(this);
|
||||
attrset(CRT_colors[METER_TEXT]);
|
||||
mvaddstr(y, x, this->caption);
|
||||
int captionLen = strlen(this->caption);
|
||||
x += captionLen;
|
||||
mvaddnstr(y, x, caption, w - 1);
|
||||
attrset(CRT_colors[RESET_COLOR]);
|
||||
|
||||
int captionLen = strlen(caption);
|
||||
x += captionLen;
|
||||
w -= captionLen;
|
||||
if (w <= 0)
|
||||
return;
|
||||
|
||||
RichString_begin(out);
|
||||
Meter_displayBuffer(this, buffer, &out);
|
||||
RichString_printVal(out, y, x);
|
||||
RichString_end(out);
|
||||
Meter_displayBuffer(this, &out);
|
||||
RichString_printoffnVal(out, y, x, 0, w - 1);
|
||||
RichString_delete(&out);
|
||||
}
|
||||
|
||||
/* ---------- BarMeterMode ---------- */
|
||||
@ -176,36 +175,48 @@ 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, METER_BUFFER_LEN - 1);
|
||||
|
||||
const char* caption = Meter_getCaption(this);
|
||||
w -= 2;
|
||||
attrset(CRT_colors[METER_TEXT]);
|
||||
int captionLen = 3;
|
||||
mvaddnstr(y, x, this->caption, captionLen);
|
||||
mvaddnstr(y, x, caption, captionLen);
|
||||
x += captionLen;
|
||||
w -= captionLen;
|
||||
attrset(CRT_colors[BAR_BORDER]);
|
||||
mvaddch(y, x, '[');
|
||||
mvaddch(y, x + w, ']');
|
||||
mvaddch(y, x + MAXIMUM(w, 0), ']');
|
||||
attrset(CRT_colors[RESET_COLOR]);
|
||||
|
||||
w--;
|
||||
x++;
|
||||
|
||||
if (w < 1) {
|
||||
attrset(CRT_colors[RESET_COLOR]);
|
||||
if (w < 1)
|
||||
return;
|
||||
}
|
||||
|
||||
// The text in the bar is right aligned;
|
||||
// calculate needed padding and generate leading spaces
|
||||
const int textLen = mbstowcs(NULL, buffer, 0);
|
||||
const int padding = MAXIMUM(w - textLen, 0);
|
||||
|
||||
// Pad with maximal spaces and then calculate needed starting position offset
|
||||
RichString_begin(bar);
|
||||
RichString_appendChr(&bar, ' ', padding);
|
||||
RichString_append(&bar, 0, buffer);
|
||||
assert(RichString_sizeVal(bar) >= w);
|
||||
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
|
||||
// Truncate meter text at a space character
|
||||
for (int pos = 2 * w; pos > w; pos--) {
|
||||
if (RichString_getCharVal(bar, pos) == ' ') {
|
||||
while (pos > w && RichString_getCharVal(bar, pos - 1) == ' ')
|
||||
pos--;
|
||||
startPos = pos - w;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If still too large, print the start not the end
|
||||
startPos = MINIMUM(startPos, w);
|
||||
}
|
||||
assert(startPos >= 0);
|
||||
assert(startPos <= w);
|
||||
assert(startPos + w <= RichString_sizeVal(bar));
|
||||
|
||||
int blockSizes[10];
|
||||
|
||||
@ -223,11 +234,11 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
|
||||
// (Control against invalid values)
|
||||
nextOffset = CLAMP(nextOffset, 0, w);
|
||||
for (int j = offset; j < nextOffset; j++)
|
||||
if (RichString_getCharVal(bar, j) == ' ') {
|
||||
if (RichString_getCharVal(bar, startPos + j) == ' ') {
|
||||
if (CRT_colorScheme == COLORSCHEME_MONOCHROME) {
|
||||
RichString_setChar(&bar, j, BarMeterMode_characters[i]);
|
||||
RichString_setChar(&bar, startPos + j, BarMeterMode_characters[i]);
|
||||
} else {
|
||||
RichString_setChar(&bar, j, '|');
|
||||
RichString_setChar(&bar, startPos + j, '|');
|
||||
}
|
||||
}
|
||||
offset = nextOffset;
|
||||
@ -236,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++) {
|
||||
attrset(CRT_colors[Meter_attributes(this)[i]]);
|
||||
RichString_printoffnVal(bar, y, x + offset, offset, blockSizes[i]);
|
||||
int attr = this->curAttributes ? this->curAttributes[i] : Meter_attributes(this)[i];
|
||||
RichString_setAttrn(&bar, CRT_colors[attr], startPos + offset, blockSizes[i]);
|
||||
RichString_printoffnVal(bar, y, x + offset, startPos + offset, MINIMUM(blockSizes[i], w - offset));
|
||||
offset += blockSizes[i];
|
||||
offset = CLAMP(offset, 0, w);
|
||||
}
|
||||
if (offset < w) {
|
||||
attrset(CRT_colors[BAR_SHADOW]);
|
||||
RichString_printoffnVal(bar, y, x + offset, offset, w - offset);
|
||||
RichString_setAttrn(&bar, CRT_colors[BAR_SHADOW], startPos + offset, w - offset);
|
||||
RichString_printoffnVal(bar, y, x + offset, startPos + offset, w - offset);
|
||||
}
|
||||
|
||||
RichString_end(bar);
|
||||
RichString_delete(&bar);
|
||||
|
||||
move(y, x + w + 1);
|
||||
attrset(CRT_colors[RESET_COLOR]);
|
||||
@ -274,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;
|
||||
@ -296,29 +308,24 @@ static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
|
||||
GraphMeterMode_pixPerRow = PIXPERROW_ASCII;
|
||||
}
|
||||
|
||||
const char* caption = Meter_getCaption(this);
|
||||
attrset(CRT_colors[METER_TEXT]);
|
||||
int captionLen = 3;
|
||||
mvaddnstr(y, x, this->caption, captionLen);
|
||||
mvaddnstr(y, x, caption, captionLen);
|
||||
x += captionLen;
|
||||
w -= captionLen;
|
||||
|
||||
struct timeval now;
|
||||
gettimeofday(&now, NULL);
|
||||
if (!timercmp(&now, &(data->time), <)) {
|
||||
if (!timercmp(&pl->realtime, &(data->time), <)) {
|
||||
int globalDelay = this->pl->settings->delay;
|
||||
struct timeval delay = { .tv_sec = globalDelay / 10, .tv_usec = (globalDelay - ((globalDelay / 10) * 10)) * 100000 };
|
||||
timeradd(&now, &delay, &(data->time));
|
||||
struct timeval delay = { .tv_sec = globalDelay / 10, .tv_usec = (globalDelay % 10) * 100000L };
|
||||
timeradd(&pl->realtime, &delay, &(data->time));
|
||||
|
||||
for (int i = 0; i < nValues - 1; i++)
|
||||
data->values[i] = data->values[i + 1];
|
||||
|
||||
char buffer[nValues];
|
||||
Meter_updateValues(this, buffer, nValues - 1);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -329,8 +336,10 @@ static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
|
||||
}
|
||||
for (; i < nValues - 1; i += 2, k++) {
|
||||
int pix = GraphMeterMode_pixPerRow * GRAPH_HEIGHT;
|
||||
int v1 = CLAMP((int) lround(data->values[i] * pix), 1, pix);
|
||||
int v2 = CLAMP((int) lround(data->values[i + 1] * pix), 1, pix);
|
||||
if (this->total < 1)
|
||||
this->total = 1;
|
||||
int v1 = CLAMP((int) lround(data->values[i] / this->total * pix), 1, pix);
|
||||
int v2 = CLAMP((int) lround(data->values[i + 1] / this->total * pix), 1, pix);
|
||||
|
||||
int colorIdx = GRAPH_1;
|
||||
for (int line = 0; line < GRAPH_HEIGHT; line++) {
|
||||
@ -371,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;
|
||||
@ -380,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, METER_BUFFER_LEN - 1);
|
||||
|
||||
RichString_begin(out);
|
||||
Meter_displayBuffer(this, buffer, &out);
|
||||
Meter_displayBuffer(this, &out);
|
||||
|
||||
int yText =
|
||||
#ifdef HAVE_LIBNCURSESW
|
||||
@ -392,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++) {
|
||||
char c = RichString_getCharVal(out, 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 = {
|
||||
@ -444,15 +459,11 @@ const MeterMode* const Meter_modes[] = {
|
||||
|
||||
/* Blank meter */
|
||||
|
||||
static void BlankMeter_updateValues(Meter* this, char* buffer, int size) {
|
||||
(void) this; (void) buffer; (void) 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[] = {
|
||||
|
59
Meter.h
59
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,31 @@ 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 { \
|
||||
if ((written) < 0 || (size_t)(written) >= (size)) { \
|
||||
return; \
|
||||
} \
|
||||
(buffer) += (written); \
|
||||
(size) -= (size_t)(written); \
|
||||
} while (0)
|
||||
|
||||
#define METER_BUFFER_APPEND_CHR(buffer, size, c) \
|
||||
do { \
|
||||
if ((size) < 2) { \
|
||||
return; \
|
||||
} \
|
||||
*(buffer)++ = c; \
|
||||
*(buffer) = '\0'; \
|
||||
(size)--; \
|
||||
if ((size) == 0) { \
|
||||
return; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
struct Meter_;
|
||||
typedef struct Meter_ Meter;
|
||||
@ -26,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*, int);
|
||||
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;
|
||||
@ -44,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))
|
||||
@ -54,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_ {
|
||||
@ -72,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;
|
||||
@ -99,9 +136,9 @@ typedef enum {
|
||||
|
||||
extern const MeterClass Meter_class;
|
||||
|
||||
Meter* Meter_new(const ProcessList* pl, int param, const MeterClass* type);
|
||||
Meter* Meter_new(const ProcessList* pl, unsigned int param, const MeterClass* type);
|
||||
|
||||
int Meter_humanUnit(char* buffer, unsigned long int value, int size);
|
||||
int Meter_humanUnit(char* buffer, unsigned long int value, size_t size);
|
||||
|
||||
void Meter_delete(Object* cast);
|
||||
|
||||
@ -109,7 +146,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.
|
||||
*/
|
||||
|
||||
@ -22,7 +22,7 @@ in the source distribution for its full text.
|
||||
// we call them "Styles".
|
||||
static const char* const MetersFunctions[] = {"Style ", "Move ", " ", "Delete", "Done ", NULL};
|
||||
static const char* const MetersKeys[] = {"Space", "Enter", "", "Del", "F10"};
|
||||
static int MetersEvents[] = {' ', 13, ERR, KEY_DC, KEY_F(10)};
|
||||
static const int MetersEvents[] = {' ', 13, ERR, KEY_DC, KEY_F(10)};
|
||||
|
||||
// We avoid UTF-8 arrows ← → here as they might display full-width on Chinese
|
||||
// terminals, breaking our aligning.
|
||||
@ -30,7 +30,7 @@ static int MetersEvents[] = {' ', 13, ERR, KEY_DC, KEY_F(10)};
|
||||
// considered "Ambiguous characters".
|
||||
static const char* const MetersMovingFunctions[] = {"Style ", "Lock ", "Up ", "Down ", "Left ", "Right ", " ", "Delete", "Done ", NULL};
|
||||
static const char* const MetersMovingKeys[] = {"Space", "Enter", "Up", "Dn", "<-", "->", " ", "Del", "F10"};
|
||||
static int MetersMovingEvents[] = {' ', 13, KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, ERR, KEY_DC, KEY_F(10)};
|
||||
static const int MetersMovingEvents[] = {' ', 13, KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, ERR, KEY_DC, KEY_F(10)};
|
||||
static FunctionBar* Meters_movingBar = NULL;
|
||||
|
||||
void MetersPanel_cleanup() {
|
||||
@ -55,13 +55,12 @@ void MetersPanel_setMoving(MetersPanel* this, bool moving) {
|
||||
selected->moving = moving;
|
||||
}
|
||||
if (!moving) {
|
||||
Panel_setSelectionColor(super, CRT_colors[PANEL_SELECTION_FOCUS]);
|
||||
Panel_setSelectionColor(super, PANEL_SELECTION_FOCUS);
|
||||
Panel_setDefaultBar(super);
|
||||
} else {
|
||||
Panel_setSelectionColor(super, CRT_colors[PANEL_SELECTION_FOLLOW]);
|
||||
Panel_setSelectionColor(super, PANEL_SELECTION_FOLLOW);
|
||||
super->currentBar = Meters_movingBar;
|
||||
}
|
||||
FunctionBar_draw(this->super.currentBar);
|
||||
}
|
||||
|
||||
static inline bool moveToNeighbor(MetersPanel* this, MetersPanel* neighbor, int selected) {
|
||||
@ -186,8 +185,7 @@ static HandlerResult MetersPanel_eventHandler(Panel* super, int ch) {
|
||||
Header* header = this->scr->header;
|
||||
this->settings->changed = true;
|
||||
Header_calculateHeight(header);
|
||||
Header_draw(header);
|
||||
ScreenManager_resize(this->scr, this->scr->x1, header->height, this->scr->x2, this->scr->y2);
|
||||
ScreenManager_resize(this->scr);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -217,7 +215,7 @@ MetersPanel* MetersPanel_new(Settings* settings, const char* header, Vector* met
|
||||
this->leftNeighbor = NULL;
|
||||
Panel_setHeader(super, header);
|
||||
for (int i = 0; i < Vector_size(meters); i++) {
|
||||
Meter* meter = (Meter*) Vector_get(meters, i);
|
||||
const Meter* meter = (const Meter*) Vector_get(meters, i);
|
||||
Panel_add(super, (Object*) Meter_toListItem(meter, false));
|
||||
}
|
||||
return this;
|
||||
|
@ -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.
|
||||
*/
|
||||
|
||||
|
104
NetworkIOMeter.c
104
NetworkIOMeter.c
@ -1,13 +1,14 @@
|
||||
#include "NetworkIOMeter.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <sys/time.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "CRT.h"
|
||||
#include "Macros.h"
|
||||
#include "Object.h"
|
||||
#include "Platform.h"
|
||||
#include "Process.h"
|
||||
#include "ProcessList.h"
|
||||
#include "RichString.h"
|
||||
#include "XUtils.h"
|
||||
|
||||
@ -19,93 +20,104 @@ static const int NetworkIOMeter_attributes[] = {
|
||||
|
||||
static bool hasData = false;
|
||||
|
||||
static unsigned long int cached_rxb_diff = 0;
|
||||
static unsigned long int cached_rxp_diff = 0;
|
||||
static unsigned long int cached_txb_diff = 0;
|
||||
static unsigned long int cached_txp_diff = 0;
|
||||
static uint32_t cached_rxb_diff;
|
||||
static uint32_t cached_rxp_diff;
|
||||
static uint32_t cached_txb_diff;
|
||||
static uint32_t cached_txp_diff;
|
||||
|
||||
static void NetworkIOMeter_updateValues(ATTR_UNUSED Meter* this, char* buffer, int len) {
|
||||
static unsigned long long int cached_last_update = 0;
|
||||
static void NetworkIOMeter_updateValues(Meter* this) {
|
||||
const ProcessList* pl = this->pl;
|
||||
static uint64_t cached_last_update = 0;
|
||||
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
unsigned long long int timeInMilliSeconds = (unsigned long long int)tv.tv_sec * 1000 + (unsigned long long int)tv.tv_usec / 1000;
|
||||
unsigned long long int passedTimeInMs = timeInMilliSeconds - cached_last_update;
|
||||
uint64_t passedTimeInMs = pl->realtimeMs - cached_last_update;
|
||||
|
||||
/* update only every 500ms */
|
||||
if (passedTimeInMs > 500) {
|
||||
static unsigned long int cached_rxb_total = 0;
|
||||
static unsigned long int cached_rxp_total = 0;
|
||||
static unsigned long int cached_txb_total = 0;
|
||||
static unsigned long int cached_txp_total = 0;
|
||||
static uint64_t cached_rxb_total;
|
||||
static uint64_t cached_rxp_total;
|
||||
static uint64_t cached_txb_total;
|
||||
static uint64_t cached_txp_total;
|
||||
uint64_t diff;
|
||||
|
||||
cached_last_update = timeInMilliSeconds;
|
||||
cached_last_update = pl->realtimeMs;
|
||||
|
||||
unsigned long int bytesReceived, packetsReceived, bytesTransmitted, packetsTransmitted;
|
||||
|
||||
hasData = Platform_getNetworkIO(&bytesReceived, &packetsReceived, &bytesTransmitted, &packetsTransmitted);
|
||||
NetworkIOData data;
|
||||
hasData = Platform_getNetworkIO(&data);
|
||||
if (!hasData) {
|
||||
xSnprintf(buffer, len, "no data");
|
||||
xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "no data");
|
||||
return;
|
||||
}
|
||||
|
||||
if (bytesReceived > cached_rxb_total) {
|
||||
cached_rxb_diff = (bytesReceived - cached_rxb_total) / 1024; /* Meter_humanUnit() expects unit in kilo */
|
||||
cached_rxb_diff = 1000.0 * cached_rxb_diff / passedTimeInMs; /* convert to per second */
|
||||
if (data.bytesReceived > cached_rxb_total) {
|
||||
diff = data.bytesReceived - cached_rxb_total;
|
||||
diff /= ONE_K; /* Meter_humanUnit() expects unit in kilo */
|
||||
diff = (1000 * diff) / passedTimeInMs; /* convert to per second */
|
||||
cached_rxb_diff = (uint32_t)diff;
|
||||
} else {
|
||||
cached_rxb_diff = 0;
|
||||
}
|
||||
cached_rxb_total = bytesReceived;
|
||||
cached_rxb_total = data.bytesReceived;
|
||||
|
||||
if (packetsReceived > cached_rxp_total) {
|
||||
cached_rxp_diff = packetsReceived - cached_rxp_total;
|
||||
if (data.packetsReceived > cached_rxp_total) {
|
||||
diff = data.packetsReceived - cached_rxp_total;
|
||||
cached_rxp_diff = (uint32_t)diff;
|
||||
} else {
|
||||
cached_rxp_diff = 0;
|
||||
}
|
||||
cached_rxp_total = packetsReceived;
|
||||
cached_rxp_total = data.packetsReceived;
|
||||
|
||||
if (bytesTransmitted > cached_txb_total) {
|
||||
cached_txb_diff = (bytesTransmitted - cached_txb_total) / 1024; /* Meter_humanUnit() expects unit in kilo */
|
||||
cached_txb_diff = 1000.0 * cached_txb_diff / passedTimeInMs; /* convert to per second */
|
||||
if (data.bytesTransmitted > cached_txb_total) {
|
||||
diff = data.bytesTransmitted - cached_txb_total;
|
||||
diff /= ONE_K; /* Meter_humanUnit() expects unit in kilo */
|
||||
diff = (1000 * diff) / passedTimeInMs; /* convert to per second */
|
||||
cached_txb_diff = (uint32_t)diff;
|
||||
} else {
|
||||
cached_txb_diff = 0;
|
||||
}
|
||||
cached_txb_total = bytesTransmitted;
|
||||
cached_txb_total = data.bytesTransmitted;
|
||||
|
||||
if (packetsTransmitted > cached_txp_total) {
|
||||
cached_txp_diff = packetsTransmitted - cached_txp_total;
|
||||
if (data.packetsTransmitted > cached_txp_total) {
|
||||
diff = data.packetsTransmitted - cached_txp_total;
|
||||
cached_txp_diff = (uint32_t)diff;
|
||||
} else {
|
||||
cached_txp_diff = 0;
|
||||
}
|
||||
cached_txp_total = packetsTransmitted;
|
||||
cached_txp_total = data.packetsTransmitted;
|
||||
}
|
||||
|
||||
this->values[0] = cached_rxb_diff;
|
||||
this->values[1] = cached_txb_diff;
|
||||
if (cached_rxb_diff + cached_txb_diff > this->total) {
|
||||
this->total = cached_rxb_diff + cached_txb_diff;
|
||||
}
|
||||
|
||||
char bufferBytesReceived[12], bufferBytesTransmitted[12];
|
||||
Meter_humanUnit(bufferBytesReceived, cached_rxb_diff, sizeof(bufferBytesReceived));
|
||||
Meter_humanUnit(bufferBytesTransmitted, cached_txb_diff, sizeof(bufferBytesTransmitted));
|
||||
xSnprintf(buffer, len, "rx:%siB/s tx:%siB/s", bufferBytesReceived, bufferBytesTransmitted);
|
||||
xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "rx:%siB/s tx:%siB/s", bufferBytesReceived, bufferBytesTransmitted);
|
||||
}
|
||||
|
||||
static void NetworkIOMeter_display(ATTR_UNUSED const Object* cast, RichString* out) {
|
||||
if (!hasData) {
|
||||
RichString_write(out, CRT_colors[METER_VALUE_ERROR], "no data");
|
||||
RichString_writeAscii(out, CRT_colors[METER_VALUE_ERROR], "no data");
|
||||
return;
|
||||
}
|
||||
|
||||
char buffer[64];
|
||||
int len;
|
||||
|
||||
RichString_write(out, CRT_colors[METER_TEXT], "rx: ");
|
||||
RichString_writeAscii(out, CRT_colors[METER_TEXT], "rx: ");
|
||||
Meter_humanUnit(buffer, cached_rxb_diff, sizeof(buffer));
|
||||
RichString_append(out, CRT_colors[METER_VALUE_IOREAD], buffer);
|
||||
RichString_append(out, CRT_colors[METER_VALUE_IOREAD], "iB/s");
|
||||
RichString_appendAscii(out, CRT_colors[METER_VALUE_IOREAD], buffer);
|
||||
RichString_appendAscii(out, CRT_colors[METER_VALUE_IOREAD], "iB/s");
|
||||
|
||||
RichString_append(out, CRT_colors[METER_TEXT], " tx: ");
|
||||
RichString_appendAscii(out, CRT_colors[METER_TEXT], " tx: ");
|
||||
Meter_humanUnit(buffer, cached_txb_diff, sizeof(buffer));
|
||||
RichString_append(out, CRT_colors[METER_VALUE_IOWRITE], buffer);
|
||||
RichString_append(out, CRT_colors[METER_VALUE_IOWRITE], "iB/s");
|
||||
RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], 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_append(out, CRT_colors[METER_TEXT], buffer);
|
||||
len = xSnprintf(buffer, sizeof(buffer), " (%u/%u packets) ", cached_rxp_diff, cached_txp_diff);
|
||||
RichString_appendnAscii(out, CRT_colors[METER_TEXT], buffer, len);
|
||||
}
|
||||
|
||||
const MeterClass NetworkIOMeter_class = {
|
||||
@ -116,7 +128,7 @@ const MeterClass NetworkIOMeter_class = {
|
||||
},
|
||||
.updateValues = NetworkIOMeter_updateValues,
|
||||
.defaultMode = TEXT_METERMODE,
|
||||
.maxItems = 0,
|
||||
.maxItems = 2,
|
||||
.total = 100.0,
|
||||
.attributes = NetworkIOMeter_attributes,
|
||||
.name = "NetworkIO",
|
||||
|
@ -3,6 +3,14 @@
|
||||
|
||||
#include "Meter.h"
|
||||
|
||||
|
||||
typedef struct NetworkIOData_ {
|
||||
uint64_t bytesReceived;
|
||||
uint64_t packetsReceived;
|
||||
uint64_t bytesTransmitted;
|
||||
uint64_t packetsTransmitted;
|
||||
} NetworkIOData;
|
||||
|
||||
extern const MeterClass NetworkIOMeter_class;
|
||||
|
||||
#endif /* HEADER_NetworkIOMeter */
|
||||
|
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.
|
||||
*/
|
||||
|
||||
@ -74,7 +74,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 NODE NAME");
|
||||
}
|
||||
|
||||
void OpenFilesScreen_delete(Object* this) {
|
||||
|
@ -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;
|
||||
|
24
OptionItem.c
24
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.
|
||||
*/
|
||||
|
||||
@ -12,7 +12,9 @@ in the source distribution for its full text.
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "CRT.h"
|
||||
#include "Macros.h"
|
||||
#include "RichString.h"
|
||||
#include "XUtils.h"
|
||||
|
||||
|
||||
static void OptionItem_delete(Object* cast) {
|
||||
@ -27,14 +29,14 @@ static void CheckItem_display(const Object* cast, RichString* out) {
|
||||
const CheckItem* this = (const CheckItem*)cast;
|
||||
assert (this != NULL);
|
||||
|
||||
RichString_write(out, CRT_colors[CHECK_BOX], "[");
|
||||
RichString_writeAscii(out, CRT_colors[CHECK_BOX], "[");
|
||||
if (CheckItem_get(this)) {
|
||||
RichString_append(out, CRT_colors[CHECK_MARK], "x");
|
||||
RichString_appendAscii(out, CRT_colors[CHECK_MARK], "x");
|
||||
} else {
|
||||
RichString_append(out, CRT_colors[CHECK_MARK], " ");
|
||||
RichString_appendAscii(out, CRT_colors[CHECK_MARK], " ");
|
||||
}
|
||||
RichString_append(out, CRT_colors[CHECK_BOX], "] ");
|
||||
RichString_append(out, CRT_colors[CHECK_TEXT], this->super.text);
|
||||
RichString_appendAscii(out, CRT_colors[CHECK_BOX], "] ");
|
||||
RichString_appendWide(out, CRT_colors[CHECK_TEXT], this->super.text);
|
||||
}
|
||||
|
||||
static void NumberItem_display(const Object* cast, RichString* out) {
|
||||
@ -42,7 +44,7 @@ static void NumberItem_display(const Object* cast, RichString* out) {
|
||||
assert (this != NULL);
|
||||
|
||||
char buffer[12];
|
||||
RichString_write(out, CRT_colors[CHECK_BOX], "[");
|
||||
RichString_writeAscii(out, CRT_colors[CHECK_BOX], "[");
|
||||
int written;
|
||||
if (this->scale < 0) {
|
||||
written = xSnprintf(buffer, sizeof(buffer), "%.*f", -this->scale, pow(10, this->scale) * NumberItem_get(this));
|
||||
@ -51,12 +53,12 @@ static void NumberItem_display(const Object* cast, RichString* out) {
|
||||
} else {
|
||||
written = xSnprintf(buffer, sizeof(buffer), "%d", NumberItem_get(this));
|
||||
}
|
||||
RichString_append(out, CRT_colors[CHECK_MARK], buffer);
|
||||
RichString_append(out, CRT_colors[CHECK_BOX], "]");
|
||||
RichString_appendnAscii(out, CRT_colors[CHECK_MARK], buffer, written);
|
||||
RichString_appendAscii(out, CRT_colors[CHECK_BOX], "]");
|
||||
for (int i = written; i < 5; i++) {
|
||||
RichString_append(out, CRT_colors[CHECK_BOX], " ");
|
||||
RichString_appendAscii(out, CRT_colors[CHECK_BOX], " ");
|
||||
}
|
||||
RichString_append(out, CRT_colors[CHECK_TEXT], this->super.text);
|
||||
RichString_appendWide(out, CRT_colors[CHECK_TEXT], this->super.text);
|
||||
}
|
||||
|
||||
const OptionItemClass OptionItem_class = {
|
||||
|
@ -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.
|
||||
*/
|
||||
|
||||
|
118
Panel.c
118
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));
|
||||
@ -55,11 +55,13 @@ void Panel_init(Panel* this, int x, int y, int w, int h, const ObjectClass* type
|
||||
this->scrollH = 0;
|
||||
this->selected = 0;
|
||||
this->oldSelected = 0;
|
||||
this->selectedLen = 0;
|
||||
this->needsRedraw = true;
|
||||
this->wasFocus = false;
|
||||
RichString_beginAllocated(this->header);
|
||||
this->defaultBar = fuBar;
|
||||
this->currentBar = fuBar;
|
||||
this->selectionColor = CRT_colors[PANEL_SELECTION_FOCUS];
|
||||
this->selectionColorId = PANEL_SELECTION_FOCUS;
|
||||
}
|
||||
|
||||
void Panel_done(Panel* this) {
|
||||
@ -67,22 +69,15 @@ void Panel_done(Panel* this) {
|
||||
free(this->eventHandlerState);
|
||||
Vector_delete(this->items);
|
||||
FunctionBar_delete(this->defaultBar);
|
||||
RichString_end(this->header);
|
||||
RichString_delete(&this->header);
|
||||
}
|
||||
|
||||
void Panel_setSelectionColor(Panel* this, int color) {
|
||||
this->selectionColor = color;
|
||||
}
|
||||
|
||||
RichString* Panel_getHeader(Panel* this) {
|
||||
assert (this != NULL);
|
||||
|
||||
this->needsRedraw = true;
|
||||
return &(this->header);
|
||||
void Panel_setSelectionColor(Panel* this, ColorElements colorId) {
|
||||
this->selectionColorId = colorId;
|
||||
}
|
||||
|
||||
inline void Panel_setHeader(Panel* this, const char* header) {
|
||||
RichString_write(&(this->header), CRT_colors[PANEL_HEADER_FOCUS], header);
|
||||
RichString_writeWide(&(this->header), CRT_colors[PANEL_HEADER_FOCUS], header);
|
||||
this->needsRedraw = true;
|
||||
}
|
||||
|
||||
@ -97,10 +92,6 @@ void Panel_move(Panel* this, int x, int y) {
|
||||
void Panel_resize(Panel* this, int w, int h) {
|
||||
assert (this != NULL);
|
||||
|
||||
if (RichString_sizeVal(this->header) > 0) {
|
||||
h--;
|
||||
}
|
||||
|
||||
this->w = w;
|
||||
this->h = h;
|
||||
this->needsRedraw = true;
|
||||
@ -181,13 +172,13 @@ void Panel_moveSelectedDown(Panel* this) {
|
||||
}
|
||||
}
|
||||
|
||||
int Panel_getSelectedIndex(Panel* this) {
|
||||
int Panel_getSelectedIndex(const Panel* this) {
|
||||
assert (this != NULL);
|
||||
|
||||
return this->selected;
|
||||
}
|
||||
|
||||
int Panel_size(Panel* this) {
|
||||
int Panel_size(const Panel* this) {
|
||||
assert (this != NULL);
|
||||
|
||||
return Vector_size(this->items);
|
||||
@ -217,7 +208,7 @@ void Panel_splice(Panel* this, Vector* from) {
|
||||
this->needsRedraw = true;
|
||||
}
|
||||
|
||||
void Panel_draw(Panel* this, bool focus, bool highlightSelected) {
|
||||
void Panel_draw(Panel* this, bool force_redraw, bool focus, bool highlightSelected, bool hideFunctionBar) {
|
||||
assert (this != NULL);
|
||||
|
||||
int size = Vector_size(this->items);
|
||||
@ -226,12 +217,21 @@ void Panel_draw(Panel* this, bool focus, bool highlightSelected) {
|
||||
int x = this->x;
|
||||
int h = this->h;
|
||||
|
||||
int headerLen = RichString_sizeVal(this->header);
|
||||
if (headerLen > 0) {
|
||||
int attr = focus
|
||||
if (hideFunctionBar)
|
||||
h++;
|
||||
|
||||
const int header_attr = focus
|
||||
? CRT_colors[PANEL_HEADER_FOCUS]
|
||||
: CRT_colors[PANEL_HEADER_UNFOCUS];
|
||||
attrset(attr);
|
||||
if (force_redraw) {
|
||||
if (Panel_printHeaderFn(this))
|
||||
Panel_printHeader(this);
|
||||
else
|
||||
RichString_setAttr(&this->header, header_attr);
|
||||
}
|
||||
int headerLen = RichString_sizeVal(this->header);
|
||||
if (headerLen > 0) {
|
||||
attrset(header_attr);
|
||||
mvhline(y, x, ' ', this->w);
|
||||
if (scrollH < headerLen) {
|
||||
RichString_printoffnVal(this->header, y, x, scrollH,
|
||||
@ -239,14 +239,15 @@ void Panel_draw(Panel* this, bool focus, bool highlightSelected) {
|
||||
}
|
||||
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
|
||||
@ -262,13 +263,13 @@ void Panel_draw(Panel* this, bool focus, bool highlightSelected) {
|
||||
int upTo = MINIMUM(first + h, size);
|
||||
|
||||
int selectionColor = focus
|
||||
? this->selectionColor
|
||||
? CRT_colors[this->selectionColorId]
|
||||
: CRT_colors[PANEL_SELECTION_UNFOCUS];
|
||||
|
||||
if (this->needsRedraw) {
|
||||
if (this->needsRedraw || force_redraw) {
|
||||
int line = 0;
|
||||
for (int i = first; line < h && i < upTo; i++) {
|
||||
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);
|
||||
@ -286,21 +287,20 @@ void Panel_draw(Panel* this, bool focus, bool highlightSelected) {
|
||||
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) {
|
||||
mvhline(y + line, x, ' ', this->w);
|
||||
line++;
|
||||
}
|
||||
this->needsRedraw = false;
|
||||
|
||||
} else {
|
||||
Object* oldObj = Vector_get(this->items, this->oldSelected);
|
||||
const Object* oldObj = Vector_get(this->items, this->oldSelected);
|
||||
RichString_begin(old);
|
||||
Object_display(oldObj, &old);
|
||||
int oldLen = RichString_sizeVal(old);
|
||||
Object* newObj = Vector_get(this->items, this->selected);
|
||||
const Object* newObj = Vector_get(this->items, this->selected);
|
||||
RichString_begin(new);
|
||||
Object_display(newObj, &new);
|
||||
int newLen = RichString_sizeVal(new);
|
||||
@ -316,20 +316,38 @@ void Panel_draw(Panel* this, bool focus, bool highlightSelected) {
|
||||
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, 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:
|
||||
@ -363,27 +381,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:
|
||||
@ -408,7 +418,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) {
|
||||
@ -443,7 +453,7 @@ HandlerResult Panel_selectByTyping(Panel* this, int ch) {
|
||||
}
|
||||
|
||||
if (len < 99) {
|
||||
buffer[len] = ch;
|
||||
buffer[len] = (char) ch;
|
||||
buffer[len + 1] = '\0';
|
||||
}
|
||||
|
||||
@ -460,7 +470,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';
|
||||
}
|
||||
|
||||
|
41
Panel.h
41
Panel.h
@ -3,12 +3,16 @@
|
||||
/*
|
||||
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"
|
||||
#include "FunctionBar.h"
|
||||
#include "Object.h"
|
||||
#include "RichString.h"
|
||||
@ -22,9 +26,11 @@ typedef enum HandlerResult_ {
|
||||
HANDLED = 0x01,
|
||||
IGNORED = 0x02,
|
||||
BREAK_LOOP = 0x04,
|
||||
REDRAW = 0x08,
|
||||
RESCAN = 0x10,
|
||||
SYNTH_KEY = 0x20,
|
||||
REFRESH = 0x08,
|
||||
REDRAW = 0x10,
|
||||
RESCAN = 0x20,
|
||||
RESIZE = 0x40,
|
||||
SYNTH_KEY = 0x80,
|
||||
} HandlerResult;
|
||||
|
||||
#define EVENT_SET_SELECTED (-1)
|
||||
@ -34,15 +40,23 @@ typedef enum HandlerResult_ {
|
||||
#define EVENT_HEADER_CLICK_GET_X(ev_) ((ev_) + 10000)
|
||||
|
||||
typedef HandlerResult (*Panel_EventHandler)(Panel*, int);
|
||||
typedef void (*Panel_DrawFunctionBar)(Panel*, bool);
|
||||
typedef void (*Panel_PrintHeader)(Panel*);
|
||||
|
||||
typedef struct PanelClass_ {
|
||||
const ObjectClass super;
|
||||
const Panel_EventHandler eventHandler;
|
||||
const Panel_DrawFunctionBar drawFunctionBar;
|
||||
const Panel_PrintHeader printHeader;
|
||||
} PanelClass;
|
||||
|
||||
#define As_Panel(this_) ((const PanelClass*)((this_)->super.klass))
|
||||
#define Panel_eventHandlerFn(this_) As_Panel(this_)->eventHandler
|
||||
#define Panel_eventHandler(this_, ev_) As_Panel(this_)->eventHandler((Panel*)(this_), ev_)
|
||||
#define Panel_eventHandler(this_, ev_) (assert(As_Panel(this_)->eventHandler), As_Panel(this_)->eventHandler((Panel*)(this_), ev_))
|
||||
#define Panel_drawFunctionBarFn(this_) As_Panel(this_)->drawFunctionBar
|
||||
#define Panel_drawFunctionBar(this_, hideFB_) (assert(As_Panel(this_)->drawFunctionBar), As_Panel(this_)->drawFunctionBar((Panel*)(this_), hideFB_))
|
||||
#define Panel_printHeaderFn(this_) As_Panel(this_)->printHeader
|
||||
#define Panel_printHeader(this_) (assert(As_Panel(this_)->printHeader), As_Panel(this_)->printHeader((Panel*)(this_)))
|
||||
|
||||
struct Panel_ {
|
||||
Object super;
|
||||
@ -53,12 +67,13 @@ struct Panel_ {
|
||||
int selectedLen;
|
||||
void* eventHandlerState;
|
||||
int scrollV;
|
||||
short scrollH;
|
||||
int scrollH;
|
||||
bool needsRedraw;
|
||||
bool wasFocus;
|
||||
FunctionBar* currentBar;
|
||||
FunctionBar* defaultBar;
|
||||
RichString header;
|
||||
int selectionColor;
|
||||
ColorElements selectionColorId;
|
||||
};
|
||||
|
||||
#define Panel_setDefaultBar(this_) do { (this_)->currentBar = (this_)->defaultBar; } while (0)
|
||||
@ -67,7 +82,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);
|
||||
|
||||
@ -75,9 +90,7 @@ 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, int color);
|
||||
|
||||
RichString* Panel_getHeader(Panel* this);
|
||||
void Panel_setSelectionColor(Panel* this, ColorElements colorId);
|
||||
|
||||
void Panel_setHeader(Panel* this, const char* header);
|
||||
|
||||
@ -103,13 +116,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 focus, bool highlightSelected);
|
||||
void Panel_draw(Panel* this, bool force_redraw, bool focus, bool highlightSelected, bool hideFunctionBar);
|
||||
|
||||
void Panel_splice(Panel* this, Vector* from);
|
||||
|
||||
|
296
Process.h
296
Process.h
@ -4,27 +4,25 @@
|
||||
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.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "Object.h"
|
||||
#include "ProcessField.h"
|
||||
#include "RichString.h"
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#define SYS_ioprio_get __NR_ioprio_get
|
||||
#define SYS_ioprio_set __NR_ioprio_set
|
||||
#endif
|
||||
|
||||
#define PROCESS_FLAG_IO 0x0001
|
||||
#define PROCESS_FLAG_IO 0x00000001
|
||||
#define PROCESS_FLAG_CWD 0x00000002
|
||||
|
||||
#define DEFAULT_HIGHLIGHT_SECS 5
|
||||
|
||||
typedef enum ProcessFields {
|
||||
typedef enum ProcessField_ {
|
||||
NULL_PROCESSFIELD = 0,
|
||||
PID = 1,
|
||||
COMM = 2,
|
||||
@ -32,7 +30,7 @@ typedef enum ProcessFields {
|
||||
PPID = 4,
|
||||
PGRP = 5,
|
||||
SESSION = 6,
|
||||
TTY_NR = 7,
|
||||
TTY = 7,
|
||||
TPGID = 8,
|
||||
MINFLT = 10,
|
||||
MAJFLT = 12,
|
||||
@ -50,101 +48,255 @@ typedef enum ProcessFields {
|
||||
NLWP = 51,
|
||||
TGID = 52,
|
||||
PERCENT_NORM_CPU = 53,
|
||||
} ProcessField;
|
||||
ELAPSED = 54,
|
||||
PROC_COMM = 124,
|
||||
PROC_EXE = 125,
|
||||
CWD = 126,
|
||||
|
||||
typedef struct ProcessPidColumn_ {
|
||||
int id;
|
||||
const char* label;
|
||||
} ProcessPidColumn;
|
||||
/* Platform specific fields, defined in ${platform}/ProcessField.h */
|
||||
PLATFORM_PROCESS_FIELDS
|
||||
|
||||
/* Do not add new fields after this entry (dynamic entries follow) */
|
||||
LAST_PROCESSFIELD
|
||||
} ProcessField;
|
||||
|
||||
struct Settings_;
|
||||
|
||||
/* Holds information about regions of the cmdline that should be
|
||||
* highlighted (e.g. program basename, delimiter, comm). */
|
||||
typedef struct ProcessCmdlineHighlight_ {
|
||||
size_t offset; /* first character to highlight */
|
||||
size_t length; /* How many characters to highlight, zero if unused */
|
||||
int attr; /* The attributes used to highlight */
|
||||
int flags; /* Special flags used for selective highlighting, zero for always */
|
||||
} ProcessCmdlineHighlight;
|
||||
|
||||
/* ProcessMergedCommand is populated by Process_makeCommandStr: It
|
||||
* contains the merged Command string, and the information needed by
|
||||
* Process_writeCommand to color the string. str will be NULL for kernel
|
||||
* threads and zombies */
|
||||
typedef struct ProcessMergedCommand_ {
|
||||
char* str; /* merged Command string */
|
||||
size_t highlightCount; /* how many portions of cmdline to highlight */
|
||||
ProcessCmdlineHighlight highlights[8]; /* which portions of cmdline to highlight */
|
||||
bool cmdlineChanged : 1; /* whether cmdline changed */
|
||||
bool exeChanged : 1; /* whether exe changed */
|
||||
bool commChanged : 1; /* whether comm changed */
|
||||
bool prevMergeSet : 1; /* whether showMergedCommand was set */
|
||||
bool prevPathSet : 1; /* whether showProgramPath was set */
|
||||
bool prevCommSet : 1; /* whether findCommInCmdline was set */
|
||||
bool prevCmdlineSet : 1; /* whether stripExeFromCmdline was set */
|
||||
bool prevShowThreadNames : 1; /* whether showThreadNames was set */
|
||||
} ProcessMergedCommand;
|
||||
|
||||
typedef struct Process_ {
|
||||
/* Super object for emulated OOP */
|
||||
Object super;
|
||||
|
||||
/* Pointer to quasi-global data structures */
|
||||
const struct ProcessList_* processList;
|
||||
const struct Settings_* settings;
|
||||
|
||||
unsigned long long int time;
|
||||
/* Process identifier */
|
||||
pid_t pid;
|
||||
|
||||
/* Parent process identifier */
|
||||
pid_t ppid;
|
||||
|
||||
/* Thread group identifier */
|
||||
pid_t tgid;
|
||||
char* comm; /* use Process_getCommand() for Command actually displayed */
|
||||
int commLen;
|
||||
int indent;
|
||||
|
||||
int basenameOffset;
|
||||
bool updated;
|
||||
/* Process group identifier */
|
||||
int pgrp;
|
||||
|
||||
char state;
|
||||
bool tag;
|
||||
bool showChildren;
|
||||
bool show;
|
||||
bool wasShown;
|
||||
unsigned int pgrp;
|
||||
unsigned int session;
|
||||
unsigned int tty_nr;
|
||||
/* Session identifier */
|
||||
int session;
|
||||
|
||||
/* Foreground group identifier of the controlling terminal */
|
||||
int tpgid;
|
||||
uid_t st_uid;
|
||||
unsigned long int flags;
|
||||
int processor;
|
||||
|
||||
float percent_cpu;
|
||||
float percent_mem;
|
||||
/* This is a kernel (helper) task */
|
||||
bool isKernelThread;
|
||||
|
||||
/* This is a userland thread / LWP */
|
||||
bool isUserlandThread;
|
||||
|
||||
/* Controlling terminal identifier of the process */
|
||||
unsigned long int tty_nr;
|
||||
|
||||
/* Controlling terminal name of the process */
|
||||
char* tty_name;
|
||||
|
||||
/* User identifier */
|
||||
uid_t st_uid;
|
||||
|
||||
/* User name */
|
||||
const char* user;
|
||||
|
||||
/* Process runtime (in hundredth of a second) */
|
||||
unsigned long long int time;
|
||||
|
||||
/*
|
||||
* Process name including arguments.
|
||||
* Use Process_getCommand() for Command actually displayed.
|
||||
*/
|
||||
char* cmdline;
|
||||
|
||||
/* End Offset in cmdline of the process basename */
|
||||
int cmdlineBasenameEnd;
|
||||
|
||||
/* Start Offset in cmdline of the process basename */
|
||||
int cmdlineBasenameStart;
|
||||
|
||||
/* The process' "command" name */
|
||||
char* procComm;
|
||||
|
||||
/* The main process executable */
|
||||
char* procExe;
|
||||
|
||||
/* The process/thread working directory */
|
||||
char* procCwd;
|
||||
|
||||
/* Offset in procExe of the process basename */
|
||||
int procExeBasenameOffset;
|
||||
|
||||
/* Tells if the executable has been replaced in the filesystem since start */
|
||||
bool procExeDeleted;
|
||||
|
||||
/* Tells if the process uses replaced shared libraries since start */
|
||||
bool usesDeletedLib;
|
||||
|
||||
/* CPU number last executed on */
|
||||
int processor;
|
||||
|
||||
/* CPU usage during last cycle (in percent) */
|
||||
float percent_cpu;
|
||||
|
||||
/* Memory usage during last cycle (in percent) */
|
||||
float percent_mem;
|
||||
|
||||
/* Scheduling priority */
|
||||
long int priority;
|
||||
|
||||
/* Nice value */
|
||||
long int nice;
|
||||
|
||||
/* Number of threads in this process */
|
||||
long int nlwp;
|
||||
char starttime_show[8];
|
||||
|
||||
/* Process start time (in seconds elapsed since the Epoch) */
|
||||
time_t starttime_ctime;
|
||||
|
||||
/* Process start time (cached formatted string) */
|
||||
char starttime_show[8];
|
||||
|
||||
/* Total program size (in kilobytes) */
|
||||
long m_virt;
|
||||
|
||||
/* Resident set size (in kilobytes) */
|
||||
long m_resident;
|
||||
|
||||
int exit_signal;
|
||||
|
||||
time_t seenTs;
|
||||
time_t tombTs;
|
||||
|
||||
/* Number of minor faults the process has made which have not required loading a memory page from disk */
|
||||
unsigned long int minflt;
|
||||
|
||||
/* Number of major faults the process has made which have required loading a memory page from disk */
|
||||
unsigned long int majflt;
|
||||
|
||||
/*
|
||||
* Process state (platform dependent):
|
||||
* D - Waiting
|
||||
* I - Idle
|
||||
* L - Acquiring lock
|
||||
* R - Running
|
||||
* S - Sleeping
|
||||
* T - Stopped (on a signal)
|
||||
* X - Dead
|
||||
* Z - Zombie
|
||||
* t - Tracing stop
|
||||
* ? - Unknown
|
||||
*/
|
||||
char state;
|
||||
|
||||
/* Whether the process was updated during the current scan */
|
||||
bool updated;
|
||||
|
||||
/* Whether the process was tagged by the user */
|
||||
bool tag;
|
||||
|
||||
/* Whether to display this process */
|
||||
bool show;
|
||||
|
||||
/* Whether this process was shown last cycle */
|
||||
bool wasShown;
|
||||
|
||||
/* Whether to show children of this process in tree-mode */
|
||||
bool showChildren;
|
||||
|
||||
/*
|
||||
* Internal time counts for showing new and exited processes.
|
||||
*/
|
||||
uint64_t seenStampMs;
|
||||
uint64_t tombStampMs;
|
||||
|
||||
/*
|
||||
* Internal state for tree-mode.
|
||||
*/
|
||||
int indent;
|
||||
unsigned int tree_left;
|
||||
unsigned int tree_right;
|
||||
unsigned int tree_depth;
|
||||
unsigned int tree_index;
|
||||
|
||||
/*
|
||||
* Internal state for merged Command display
|
||||
*/
|
||||
ProcessMergedCommand mergedCommand;
|
||||
} Process;
|
||||
|
||||
typedef struct ProcessFieldData_ {
|
||||
/* Name (displayed in setup menu) */
|
||||
const char* name;
|
||||
|
||||
/* Title (display in main screen); must have same width as the printed values */
|
||||
const char* title;
|
||||
|
||||
/* Description (displayed in setup menu) */
|
||||
const char* description;
|
||||
|
||||
/* Scan flag to enable scan-method otherwise not run */
|
||||
uint32_t flags;
|
||||
|
||||
/* Whether the values are process identifiers; adjusts the width of title and values if true */
|
||||
bool pidColumn;
|
||||
|
||||
/* Whether the column should be sorted in descending order by default */
|
||||
bool defaultSortDesc;
|
||||
} ProcessFieldData;
|
||||
|
||||
// Implemented in platform-specific code:
|
||||
void Process_writeField(const Process* this, RichString* str, ProcessField field);
|
||||
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 ProcessFieldData Process_fields[];
|
||||
extern ProcessPidColumn Process_pidColumns[];
|
||||
extern char Process_pidFormat[20];
|
||||
extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD];
|
||||
#define PROCESS_MAX_PID_DIGITS 19
|
||||
extern int Process_pidDigits;
|
||||
|
||||
typedef Process* (*Process_New)(const struct Settings_*);
|
||||
typedef void (*Process_WriteField)(const Process*, RichString*, ProcessField);
|
||||
typedef int (*Process_CompareByKey)(const Process*, const Process*, ProcessField);
|
||||
typedef const char* (*Process_GetCommandStr)(const Process*);
|
||||
|
||||
typedef struct ProcessClass_ {
|
||||
const ObjectClass super;
|
||||
const Process_WriteField writeField;
|
||||
const Process_CompareByKey compareByKey;
|
||||
const Process_GetCommandStr getCommandStr;
|
||||
} ProcessClass;
|
||||
|
||||
#define As_Process(this_) ((const ProcessClass*)((this_)->super.klass))
|
||||
|
||||
#define Process_getCommand(this_) (As_Process(this_)->getCommandStr ? As_Process(this_)->getCommandStr((const Process*)(this_)) : ((const Process*)(this_))->comm)
|
||||
#define Process_getCommand(this_) (As_Process(this_)->getCommandStr ? As_Process(this_)->getCommandStr((const Process*)(this_)) : Process_getCommandStr((const Process*)(this_)))
|
||||
#define Process_compareByKey(p1_, p2_, key_) (As_Process(p1_)->compareByKey ? (As_Process(p1_)->compareByKey(p1_, p2_, key_)) : Process_compareByKey_Base(p1_, p2_, key_))
|
||||
|
||||
static inline pid_t Process_getParentPid(const Process* this) {
|
||||
return this->tgid == this->pid ? this->ppid : this->tgid;
|
||||
@ -154,30 +306,57 @@ static inline bool Process_isChildOf(const Process* this, pid_t pid) {
|
||||
return pid == Process_getParentPid(this);
|
||||
}
|
||||
|
||||
#define Process_sortState(state) ((state) == 'I' ? 0x100 : (state))
|
||||
static inline bool Process_isKernelThread(const Process* this) {
|
||||
return this->isKernelThread;
|
||||
}
|
||||
|
||||
static inline bool Process_isUserlandThread(const Process* this) {
|
||||
return this->isUserlandThread;
|
||||
}
|
||||
|
||||
static inline bool Process_isThread(const Process* this) {
|
||||
return Process_isUserlandThread(this) || Process_isKernelThread(this);
|
||||
}
|
||||
|
||||
#define CMDLINE_HIGHLIGHT_FLAG_SEPARATOR 0x00000001
|
||||
#define CMDLINE_HIGHLIGHT_FLAG_BASENAME 0x00000002
|
||||
#define CMDLINE_HIGHLIGHT_FLAG_COMM 0x00000004
|
||||
#define CMDLINE_HIGHLIGHT_FLAG_DELETED 0x00000008
|
||||
|
||||
#define ONE_K 1024UL
|
||||
#define ONE_M (ONE_K * ONE_K)
|
||||
#define ONE_G (ONE_M * ONE_K)
|
||||
#define ONE_T (1ULL * ONE_G * ONE_K)
|
||||
#define ONE_P (1ULL * ONE_T * ONE_K)
|
||||
|
||||
#define ONE_DECIMAL_K 1000UL
|
||||
#define ONE_DECIMAL_M (ONE_DECIMAL_K * ONE_DECIMAL_K)
|
||||
#define ONE_DECIMAL_G (ONE_DECIMAL_M * ONE_DECIMAL_K)
|
||||
#define ONE_DECIMAL_T (1ULL * ONE_DECIMAL_G * ONE_DECIMAL_K)
|
||||
#define ONE_DECIMAL_P (1ULL * ONE_DECIMAL_T * ONE_DECIMAL_K)
|
||||
|
||||
void Process_setupColumnWidths(void);
|
||||
|
||||
void Process_humanNumber(RichString* str, unsigned long long number, bool coloring);
|
||||
/* Takes number in bytes (base 1024). Prints 6 columns. */
|
||||
void Process_printBytes(RichString* str, unsigned long long number, bool coloring);
|
||||
|
||||
void Process_colorNumber(RichString* str, unsigned long long number, bool coloring);
|
||||
/* Takes number in kilo bytes (base 1024). Prints 6 columns. */
|
||||
void Process_printKBytes(RichString* str, unsigned long long number, bool coloring);
|
||||
|
||||
void Process_printTime(RichString* str, unsigned long long totalHundredths);
|
||||
/* Takes number as count (base 1000). Prints 12 columns. */
|
||||
void Process_printCount(RichString* str, unsigned long long number, bool coloring);
|
||||
|
||||
/* Takes time in hundredths of a seconds. Prints 9 columns. */
|
||||
void Process_printTime(RichString* str, unsigned long long totalHundredths, bool coloring);
|
||||
|
||||
/* Takes rate in bare unit (base 1024) per second. Prints 12 columns. */
|
||||
void Process_printRate(RichString* str, double rate, bool coloring);
|
||||
|
||||
void Process_fillStarttimeBuffer(Process* this);
|
||||
|
||||
void Process_outputRate(RichString* str, char* buffer, int 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, int* attr);
|
||||
|
||||
void Process_display(const Object* cast, RichString* out);
|
||||
|
||||
@ -199,6 +378,21 @@ bool Process_changePriorityBy(Process* this, Arg delta);
|
||||
|
||||
bool Process_sendSignal(Process* this, Arg sgn);
|
||||
|
||||
long Process_pidCompare(const void* v1, const void* v2);
|
||||
int Process_pidCompare(const void* v1, const void* v2);
|
||||
|
||||
int Process_compareByKey_Base(const Process* p1, const Process* p2, ProcessField key);
|
||||
|
||||
// Avoid direct calls, use Process_getCommand instead
|
||||
const char* Process_getCommandStr(const Process* this);
|
||||
|
||||
void Process_updateComm(Process* this, const char* comm);
|
||||
void Process_updateCmdline(Process* this, const char* cmdline, int basenameStart, int basenameEnd);
|
||||
void Process_updateExe(Process* this, const char* exe);
|
||||
|
||||
/* This function constructs the string that is displayed by
|
||||
* Process_writeCommand and also returned by Process_getCommandStr */
|
||||
void Process_makeCommandStr(Process* this);
|
||||
|
||||
void Process_writeCommand(const Process* this, int attr, int baseAttr, RichString* str);
|
||||
|
||||
#endif
|
||||
|
214
ProcessList.c
214
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.
|
||||
*/
|
||||
|
||||
@ -10,15 +10,17 @@ in the source distribution for its full text.
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.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
|
||||
|
||||
@ -28,13 +30,18 @@ ProcessList* ProcessList_init(ProcessList* this, const ObjectClass* klass, Users
|
||||
|
||||
this->usersTable = usersTable;
|
||||
this->pidMatchList = pidMatchList;
|
||||
this->dynamicMeters = dynamicMeters;
|
||||
this->dynamicColumns = dynamicColumns;
|
||||
|
||||
this->userId = userId;
|
||||
|
||||
// set later by platform-specific code
|
||||
this->cpuCount = 0;
|
||||
this->activeCPUs = 0;
|
||||
this->existingCPUs = 0;
|
||||
this->monotonicMs = 0;
|
||||
|
||||
this->scanTs = 0;
|
||||
// always maintain valid realtime timestamps
|
||||
Platform_gettime_realtime(&this->realtime, &this->realtimeMs);
|
||||
|
||||
#ifdef HAVE_LIBHWLOC
|
||||
this->topologyOk = false;
|
||||
@ -78,22 +85,63 @@ void ProcessList_setPanel(ProcessList* this, Panel* panel) {
|
||||
this->panel = panel;
|
||||
}
|
||||
|
||||
void ProcessList_printHeader(ProcessList* this, RichString* header) {
|
||||
RichString_prune(header);
|
||||
|
||||
const ProcessField* fields = this->settings->fields;
|
||||
|
||||
for (int i = 0; fields[i]; i++) {
|
||||
const char* field = Process_fields[fields[i]].title;
|
||||
if (!field) {
|
||||
field = "- ";
|
||||
static const char* alignedDynamicColumnTitle(const ProcessList* this, int key) {
|
||||
const DynamicColumn* column = Hashtable_get(this->dynamicColumns, key);
|
||||
if (column == NULL)
|
||||
return "- ";
|
||||
static char titleBuffer[DYNAMIC_MAX_COLUMN_WIDTH + /* space */ 1 + /* null terminator */ + 1];
|
||||
int width = column->width;
|
||||
if (!width || abs(width) > DYNAMIC_MAX_COLUMN_WIDTH)
|
||||
width = DYNAMIC_DEFAULT_COLUMN_WIDTH;
|
||||
xSnprintf(titleBuffer, sizeof(titleBuffer), "%*s", width, column->heading);
|
||||
return titleBuffer;
|
||||
}
|
||||
|
||||
int color = (this->settings->sortKey == fields[i]) ?
|
||||
CRT_colors[PANEL_SELECTION_FOCUS] : CRT_colors[PANEL_HEADER_FOCUS];
|
||||
RichString_append(header, color, field);
|
||||
if (COMM == fields[i] && this->settings->showMergedCommand) {
|
||||
RichString_append(header, color, "(merged)");
|
||||
static const char* alignedProcessFieldTitle(const ProcessList* this, ProcessField field) {
|
||||
if (field >= LAST_PROCESSFIELD)
|
||||
return alignedDynamicColumnTitle(this, field);
|
||||
|
||||
const char* title = Process_fields[field].title;
|
||||
if (!title)
|
||||
return "- ";
|
||||
|
||||
if (!Process_fields[field].pidColumn)
|
||||
return title;
|
||||
|
||||
static char titleBuffer[PROCESS_MAX_PID_DIGITS + /* space */ 1 + /* null-terminator */ + 1];
|
||||
xSnprintf(titleBuffer, sizeof(titleBuffer), "%*s ", Process_pidDigits, title);
|
||||
|
||||
return titleBuffer;
|
||||
}
|
||||
|
||||
void ProcessList_printHeader(const ProcessList* this, RichString* header) {
|
||||
RichString_rewind(header, RichString_size(header));
|
||||
|
||||
const Settings* settings = this->settings;
|
||||
const ProcessField* fields = settings->fields;
|
||||
|
||||
ProcessField key = Settings_getActiveSortKey(settings);
|
||||
|
||||
for (int i = 0; fields[i]; i++) {
|
||||
int color;
|
||||
if (settings->treeView && settings->treeViewAlwaysByPID) {
|
||||
color = CRT_colors[PANEL_HEADER_FOCUS];
|
||||
} else if (key == fields[i]) {
|
||||
color = CRT_colors[PANEL_SELECTION_FOCUS];
|
||||
} else {
|
||||
color = CRT_colors[PANEL_HEADER_FOCUS];
|
||||
}
|
||||
|
||||
RichString_appendWide(header, color, alignedProcessFieldTitle(this, fields[i]));
|
||||
if (key == fields[i] && RichString_getCharVal(*header, RichString_size(header) - 1) == ' ') {
|
||||
RichString_rewind(header, 1); // rewind to override space
|
||||
RichString_appendnWide(header,
|
||||
CRT_colors[PANEL_SELECTION_FOCUS],
|
||||
CRT_treeStr[Settings_getActiveDirection(this->settings) == 1 ? TREE_STR_ASC : TREE_STR_DESC],
|
||||
1);
|
||||
}
|
||||
if (COMM == fields[i] && settings->showMergedCommand) {
|
||||
RichString_appendAscii(header, color, "(merged)");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -104,7 +152,7 @@ void ProcessList_add(ProcessList* this, Process* p) {
|
||||
p->processList = this;
|
||||
|
||||
// highlighting processes found in first scan by first scan marked "far in the past"
|
||||
p->seenTs = this->scanTs;
|
||||
p->seenStampMs = this->monotonicMs;
|
||||
|
||||
Vector_add(this->processes, p);
|
||||
Hashtable_put(this->processTable, p->pid, p);
|
||||
@ -114,14 +162,14 @@ void ProcessList_add(ProcessList* this, Process* p) {
|
||||
assert(Hashtable_count(this->processTable) == Vector_count(this->processes));
|
||||
}
|
||||
|
||||
void ProcessList_remove(ProcessList* this, Process* p) {
|
||||
void ProcessList_remove(ProcessList* this, const Process* p) {
|
||||
assert(Vector_indexOf(this->processes, p, Process_pidCompare) != -1);
|
||||
assert(Hashtable_get(this->processTable, p->pid) != NULL);
|
||||
|
||||
Process* pp = Hashtable_remove(this->processTable, p->pid);
|
||||
const Process* pp = Hashtable_remove(this->processTable, p->pid);
|
||||
assert(pp == p); (void)pp;
|
||||
|
||||
unsigned int pid = p->pid;
|
||||
pid_t pid = p->pid;
|
||||
int idx = Vector_indexOf(this->processes, p, Process_pidCompare);
|
||||
assert(idx != -1);
|
||||
|
||||
@ -129,18 +177,15 @@ void ProcessList_remove(ProcessList* this, Process* p) {
|
||||
Vector_remove(this->processes, idx);
|
||||
}
|
||||
|
||||
assert(Hashtable_get(this->processTable, pid) == NULL); (void)pid;
|
||||
if (this->following != -1 && this->following == pid) {
|
||||
this->following = -1;
|
||||
Panel_setSelectionColor(this->panel, PANEL_SELECTION_FOCUS);
|
||||
}
|
||||
|
||||
assert(Hashtable_get(this->processTable, pid) == NULL);
|
||||
assert(Hashtable_count(this->processTable) == Vector_count(this->processes));
|
||||
}
|
||||
|
||||
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.
|
||||
//
|
||||
@ -148,7 +193,7 @@ int ProcessList_size(ProcessList* this) {
|
||||
//
|
||||
// The algorithm is based on `depth-first search`,
|
||||
// even though `breadth-first search` approach may be more efficient on first glance,
|
||||
// after comparision it may be not, as it's not safe to go deeper without first updating the tree structure.
|
||||
// after comparison it may be not, as it's not safe to go deeper without first updating the tree structure.
|
||||
// If it would be safe that approach would likely bring an advantage in performance.
|
||||
//
|
||||
// Each call of the function looks for a 'layer'. A 'layer' is a list of processes with the same depth.
|
||||
@ -170,7 +215,7 @@ static void ProcessList_updateTreeSetLayer(ProcessList* this, unsigned int leftB
|
||||
if (layerSize == 0)
|
||||
return;
|
||||
|
||||
Vector* layer = Vector_new(this->processes->type, false, layerSize);
|
||||
Vector* layer = Vector_new(Vector_type(this->processes), false, layerSize);
|
||||
|
||||
// Find all processes on the same layer (process with the same `deep` value
|
||||
// and included in a range from `leftBound` to `rightBound`).
|
||||
@ -279,6 +324,11 @@ static void ProcessList_updateTreeSet(ProcessList* this) {
|
||||
}
|
||||
|
||||
static void ProcessList_buildTreeBranch(ProcessList* this, pid_t pid, int level, int indent, int direction, bool show, int* node_counter, int* node_index) {
|
||||
// On OpenBSD the kernel thread 'swapper' has pid 0.
|
||||
// Do not treat it as root of any tree.
|
||||
if (pid == 0)
|
||||
return;
|
||||
|
||||
Vector* children = Vector_new(Class(Process), false, DEFAULT_SIZE);
|
||||
|
||||
for (int i = Vector_size(this->processes) - 1; i >= 0; i--) {
|
||||
@ -328,14 +378,14 @@ static void ProcessList_buildTreeBranch(ProcessList* this, pid_t pid, int level,
|
||||
Vector_delete(children);
|
||||
}
|
||||
|
||||
static long ProcessList_treeProcessCompare(const void* v1, const void* v2) {
|
||||
static int ProcessList_treeProcessCompare(const void* v1, const void* v2) {
|
||||
const Process* p1 = (const Process*)v1;
|
||||
const Process* p2 = (const Process*)v2;
|
||||
|
||||
return SPACESHIP_NUMBER(p1->tree_left, p2->tree_left);
|
||||
}
|
||||
|
||||
static long ProcessList_treeProcessCompareByPID(const void* v1, const void* v2) {
|
||||
static int ProcessList_treeProcessCompareByPID(const void* v1, const void* v2) {
|
||||
const Process* p1 = (const Process*)v1;
|
||||
const Process* p2 = (const Process*)v2;
|
||||
|
||||
@ -346,7 +396,7 @@ static long ProcessList_treeProcessCompareByPID(const void* v1, const void* v2)
|
||||
static void ProcessList_buildTree(ProcessList* this) {
|
||||
int node_counter = 1;
|
||||
int node_index = 0;
|
||||
int direction = this->settings->direction;
|
||||
int direction = Settings_getActiveDirection(this->settings);
|
||||
|
||||
// Sort by PID
|
||||
Vector_quickSortCustomCompare(this->processes, ProcessList_treeProcessCompareByPID);
|
||||
@ -445,12 +495,7 @@ ProcessField ProcessList_keyAt(const ProcessList* this, int at) {
|
||||
const ProcessField* fields = this->settings->fields;
|
||||
ProcessField field;
|
||||
for (int i = 0; (field = fields[i]); i++) {
|
||||
const char* title = Process_fields[field].title;
|
||||
if (!title) {
|
||||
title = "- ";
|
||||
}
|
||||
|
||||
int len = strlen(title);
|
||||
int len = strlen(alignedProcessFieldTitle(this, field));
|
||||
if (at >= x && at <= x + len) {
|
||||
return field;
|
||||
}
|
||||
@ -467,54 +512,89 @@ void ProcessList_expandTree(ProcessList* this) {
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessList_collapseAllBranches(ProcessList* this) {
|
||||
int size = Vector_size(this->processes);
|
||||
for (int i = 0; i < size; i++) {
|
||||
Process* process = (Process*) Vector_get(this->processes, i);
|
||||
// FreeBSD has pid 0 = kernel and pid 1 = init, so init has tree_depth = 1
|
||||
if (process->tree_depth > 0 && process->pid > 1)
|
||||
process->showChildren = false;
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessList_rebuildPanel(ProcessList* this) {
|
||||
const char* incFilter = this->incFilter;
|
||||
|
||||
int currPos = Panel_getSelectedIndex(this->panel);
|
||||
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->processes);
|
||||
int idx = 0;
|
||||
for (int i = 0; i < size; i++) {
|
||||
bool hidden = false;
|
||||
Process* p = ProcessList_get(this, i);
|
||||
bool foundFollowed = false;
|
||||
|
||||
for (int i = 0; i < processCount; i++) {
|
||||
Process* p = (Process*) Vector_get(this->processes, i);
|
||||
|
||||
if ( (!p->show)
|
||||
|| (this->userId != (uid_t) -1 && (p->st_uid != this->userId))
|
||||
|| (incFilter && !(String_contains_i(Process_getCommand(p), incFilter)))
|
||||
|| (this->pidMatchList && !Hashtable_get(this->pidMatchList, p->tgid)) )
|
||||
hidden = true;
|
||||
continue;
|
||||
|
||||
if (!hidden) {
|
||||
Panel_set(this->panel, idx, (Object*)p);
|
||||
if ((this->following == -1 && idx == currPos) || (this->following != -1 && p->pid == currPid)) {
|
||||
|
||||
if (this->following != -1 && p->pid == this->following) {
|
||||
foundFollowed = true;
|
||||
Panel_setSelected(this->panel, idx);
|
||||
this->panel->scrollV = currScrollV;
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
|
||||
if (this->following != -1 && !foundFollowed) {
|
||||
/* Reset if current followed pid not found */
|
||||
this->following = -1;
|
||||
Panel_setSelectionColor(this->panel, PANEL_SELECTION_FOCUS);
|
||||
}
|
||||
|
||||
if (this->following == -1) {
|
||||
/* If the last item was selected, keep the new last item selected */
|
||||
if (currPos > 0 && currPos == currSize - 1)
|
||||
Panel_setSelected(this->panel, Panel_size(this->panel) - 1);
|
||||
else
|
||||
Panel_setSelected(this->panel, currPos);
|
||||
|
||||
this->panel->scrollV = currScrollV;
|
||||
}
|
||||
}
|
||||
|
||||
Process* ProcessList_getProcess(ProcessList* this, pid_t pid, bool* preExisting, Process_New constructor) {
|
||||
Process* proc = (Process*) Hashtable_get(this->processTable, pid);
|
||||
*preExisting = proc;
|
||||
*preExisting = proc != NULL;
|
||||
if (proc) {
|
||||
assert(Vector_indexOf(this->processes, proc, Process_pidCompare) != -1);
|
||||
assert(proc->pid == pid);
|
||||
} else {
|
||||
proc = constructor(this->settings);
|
||||
assert(proc->comm == NULL);
|
||||
assert(proc->cmdline == NULL);
|
||||
proc->pid = pid;
|
||||
}
|
||||
return proc;
|
||||
}
|
||||
|
||||
void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate) {
|
||||
struct timespec now;
|
||||
|
||||
// in pause mode only gather global data for meters (CPU/memory/...)
|
||||
if (pauseProcessUpdate) {
|
||||
ProcessList_goThroughEntries(this, true);
|
||||
@ -535,35 +615,35 @@ void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate) {
|
||||
this->runningTasks = 0;
|
||||
|
||||
|
||||
// set scanTs
|
||||
// set scan timestamp
|
||||
static bool firstScanDone = false;
|
||||
if (!firstScanDone) {
|
||||
this->scanTs = 0;
|
||||
if (firstScanDone) {
|
||||
Platform_gettime_monotonic(&this->monotonicMs);
|
||||
} else {
|
||||
this->monotonicMs = 0;
|
||||
firstScanDone = true;
|
||||
} else if (clock_gettime(CLOCK_MONOTONIC, &now) == 0) {
|
||||
this->scanTs = now.tv_sec;
|
||||
}
|
||||
|
||||
ProcessList_goThroughEntries(this, false);
|
||||
|
||||
for (int i = Vector_size(this->processes) - 1; i >= 0; i--) {
|
||||
Process* p = (Process*) Vector_get(this->processes, i);
|
||||
if (p->tombTs > 0) {
|
||||
Process_makeCommandStr(p);
|
||||
|
||||
if (p->tombStampMs > 0) {
|
||||
// remove tombed process
|
||||
if (this->scanTs >= p->tombTs) {
|
||||
if (this->monotonicMs >= p->tombStampMs) {
|
||||
ProcessList_remove(this, p);
|
||||
}
|
||||
} else if (p->updated == false) {
|
||||
// process no longer exists
|
||||
if (this->settings->highlightChanges && p->wasShown) {
|
||||
// mark tombed
|
||||
p->tombTs = this->scanTs + this->settings->highlightDelaySecs;
|
||||
p->tombStampMs = this->monotonicMs + 1000 * this->settings->highlightDelaySecs;
|
||||
} else {
|
||||
// immediately remove
|
||||
ProcessList_remove(this, p);
|
||||
}
|
||||
} else {
|
||||
p->updated = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,6 +37,9 @@ in the source distribution for its full text.
|
||||
#define MAX_READ 2048
|
||||
#endif
|
||||
|
||||
typedef unsigned long long int memory_t;
|
||||
#define MEMORY_MAX ULLONG_MAX
|
||||
|
||||
typedef struct ProcessList_ {
|
||||
const Settings* settings;
|
||||
|
||||
@ -45,6 +51,13 @@ typedef struct ProcessList_ {
|
||||
Hashtable* displayTreeSet;
|
||||
Hashtable* draftingTreeSet;
|
||||
|
||||
Hashtable* dynamicMeters; /* runtime-discovered meters */
|
||||
Hashtable* dynamicColumns; /* runtime-discovered Columns */
|
||||
|
||||
struct timeval realtime; /* time of the current sample */
|
||||
uint64_t realtimeMs; /* current time in milliseconds */
|
||||
uint64_t monotonicMs; /* same, but from monotonic clock */
|
||||
|
||||
Panel* panel;
|
||||
int following;
|
||||
uid_t userId;
|
||||
@ -56,44 +69,44 @@ typedef struct ProcessList_ {
|
||||
bool topologyOk;
|
||||
#endif
|
||||
|
||||
int totalTasks;
|
||||
int runningTasks;
|
||||
int userlandThreads;
|
||||
int kernelThreads;
|
||||
unsigned int totalTasks;
|
||||
unsigned int runningTasks;
|
||||
unsigned int userlandThreads;
|
||||
unsigned int kernelThreads;
|
||||
|
||||
unsigned long long int totalMem;
|
||||
unsigned long long int usedMem;
|
||||
unsigned long long int buffersMem;
|
||||
unsigned long long int cachedMem;
|
||||
unsigned long long int totalSwap;
|
||||
unsigned long long int usedSwap;
|
||||
unsigned long long int freeSwap;
|
||||
memory_t totalMem;
|
||||
memory_t usedMem;
|
||||
memory_t buffersMem;
|
||||
memory_t cachedMem;
|
||||
memory_t sharedMem;
|
||||
memory_t availableMem;
|
||||
|
||||
int cpuCount;
|
||||
memory_t totalSwap;
|
||||
memory_t usedSwap;
|
||||
memory_t cachedSwap;
|
||||
|
||||
time_t scanTs;
|
||||
unsigned int activeCPUs;
|
||||
unsigned int existingCPUs;
|
||||
} ProcessList;
|
||||
|
||||
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId);
|
||||
/* Implemented by platforms */
|
||||
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* dynamicColumns, Hashtable* pidMatchList, uid_t userId);
|
||||
void ProcessList_delete(ProcessList* pl);
|
||||
void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate);
|
||||
bool ProcessList_isCPUonline(const ProcessList* super, unsigned int id);
|
||||
|
||||
|
||||
ProcessList* ProcessList_init(ProcessList* this, const ObjectClass* klass, UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId);
|
||||
ProcessList* ProcessList_init(ProcessList* this, const ObjectClass* klass, UsersTable* usersTable, Hashtable* dynamicMeters, Hashtable* dynamicColumns, Hashtable* pidMatchList, uid_t userId);
|
||||
|
||||
void ProcessList_done(ProcessList* this);
|
||||
|
||||
void ProcessList_setPanel(ProcessList* this, Panel* panel);
|
||||
|
||||
void ProcessList_printHeader(ProcessList* this, RichString* header);
|
||||
void ProcessList_printHeader(const ProcessList* this, RichString* header);
|
||||
|
||||
void ProcessList_add(ProcessList* this, Process* p);
|
||||
|
||||
void ProcessList_remove(ProcessList* this, Process* p);
|
||||
|
||||
Process* ProcessList_get(ProcessList* this, int idx);
|
||||
|
||||
int ProcessList_size(ProcessList* this);
|
||||
void ProcessList_remove(ProcessList* this, const Process* p);
|
||||
|
||||
void ProcessList_sort(ProcessList* this);
|
||||
|
||||
@ -101,10 +114,16 @@ ProcessField ProcessList_keyAt(const ProcessList* this, int at);
|
||||
|
||||
void ProcessList_expandTree(ProcessList* this);
|
||||
|
||||
void ProcessList_collapseAllBranches(ProcessList* this);
|
||||
|
||||
void ProcessList_rebuildPanel(ProcessList* this);
|
||||
|
||||
Process* ProcessList_getProcess(ProcessList* this, pid_t pid, bool* preExisting, Process_New constructor);
|
||||
|
||||
void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate);
|
||||
|
||||
static inline Process* ProcessList_findProcess(ProcessList* this, pid_t pid) {
|
||||
return (Process*) Hashtable_get(this->processTable, pid);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -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>
|
||||
|
149
README
149
README
@ -1,11 +1,12 @@
|
||||
# [](https://htop.dev)
|
||||
# [](https://htop.dev)
|
||||
|
||||
[](https://github.com/htop-dev/htop/actions)
|
||||
[](https://scan.coverity.com/projects/21665)
|
||||
[](https://groups.io/g/htop)
|
||||
[](https://webchat.freenode.net/#htop)
|
||||
[](https://github.com/htop-dev/htop/releases/latest)
|
||||
[](https://bintray.com/htop/source/htop/_latestVersion)
|
||||
[](https://web.libera.chat/#htop)
|
||||
[](https://github.com/htop-dev/htop/releases/latest)
|
||||
[](https://repology.org/project/htop/versions)
|
||||
[](COPYING?raw=true)
|
||||
|
||||

|
||||
|
||||
@ -14,57 +15,163 @@
|
||||
`htop` is a cross-platform interactive process viewer.
|
||||
|
||||
`htop` allows scrolling the list of processes vertically and horizontally to see their full command lines and related information like memory and CPU consumption.
|
||||
Also system wide information, like load average or swap usage, is shown.
|
||||
|
||||
The information displayed is configurable through a graphical setup and can be sorted and filtered interactively.
|
||||
|
||||
Tasks related to processes (e.g. killing and renicing) can be done without entering their PIDs.
|
||||
|
||||
Running `htop` requires `ncurses` libraries (typically named libncursesw*).
|
||||
Running `htop` requires `ncurses` libraries, typically named libncurses(w).
|
||||
|
||||
For more information and details on how to contribute to `htop` visit [htop.dev](https://htop.dev).
|
||||
`htop` is written in C.
|
||||
|
||||
For more information and details visit [htop.dev](https://htop.dev).
|
||||
|
||||
## Build instructions
|
||||
|
||||
This program is distributed as a standard GNU autotools-based package.
|
||||
### Prerequisite
|
||||
List of build-time dependencies:
|
||||
* standard GNU autotools-based C toolchain
|
||||
- C99 compliant compiler
|
||||
- `autoconf`
|
||||
- `autotools`
|
||||
* `ncurses`
|
||||
|
||||
Compiling `htop` requires the header files for `ncurses` (libncursesw*-dev). Install these and other required packages for C development from your package manager.
|
||||
**Note about `ncurses`:**
|
||||
> `htop` requires `ncurses` 6.0. Be aware the appropriate package is sometimes still called libncurses5 (on Debian/Ubuntu). Also `ncurses` usually comes in two flavours:
|
||||
>* With Unicode support.
|
||||
>* Without Unicode support.
|
||||
>
|
||||
> This is also something that is reflected in the package name on Debian/Ubuntu (via the additional 'w' - 'w'ide character support).
|
||||
|
||||
Then, when compiling from a [release tarball](https://bintray.com/htop/source/htop), run:
|
||||
List of additional build-time dependencies (based on feature flags):
|
||||
* `sensors`
|
||||
* `hwloc`
|
||||
* `libcap` (v2.21 or later)
|
||||
* `libnl-3`
|
||||
|
||||
Install these and other required packages for C development from your package manager.
|
||||
|
||||
**Debian/Ubuntu**
|
||||
~~~ shell
|
||||
./configure && make
|
||||
sudo apt install libncursesw5-dev autotools-dev autoconf build-essential
|
||||
~~~
|
||||
|
||||
Alternatively, for compiling sources downloaded from the Git repository (`git clone` or downloads from [Github releases](https://github.com/htop-dev/htop/releases/)),
|
||||
install the header files for `ncurses` (libncursesw*-dev) and other required development packages from your distribution's package manager. Then run:
|
||||
**Fedora/RHEL**
|
||||
~~~ shell
|
||||
sudo dnf install ncurses-devel automake autoconf gcc
|
||||
~~~
|
||||
|
||||
### Compile from source:
|
||||
To compile from source, download from the Git repository (`git clone` or downloads from [GitHub releases](https://github.com/htop-dev/htop/releases/)), then run:
|
||||
~~~ shell
|
||||
./autogen.sh && ./configure && make
|
||||
~~~
|
||||
|
||||
By default `make install` will install into `/usr/local`, for changing the path use `./configure --prefix=/some/path`.
|
||||
### Install
|
||||
To install on the local system run `make install`. By default `make install` installs into `/usr/local`. To change this path use `./configure --prefix=/some/path`.
|
||||
|
||||
See the manual page (`man htop`) or the on-line help ('F1' or 'h' inside `htop`) for a list of supported key commands.
|
||||
### Build Options
|
||||
|
||||
`htop` has several build-time options to enable/disable additional features.
|
||||
|
||||
#### Generic
|
||||
|
||||
* `--enable-unicode`:
|
||||
enable Unicode support
|
||||
- dependency: *libncursesw*
|
||||
- default: *yes*
|
||||
* `--enable-affinity`:
|
||||
enable `sched_setaffinity(2)` and `sched_getaffinity(2)` for affinity support; conflicts with hwloc
|
||||
- default: *check*
|
||||
* `--enable-hwloc`:
|
||||
enable hwloc support for CPU affinity; disables affinity support
|
||||
- dependency: *libhwloc*
|
||||
- default: *no*
|
||||
* `--enable-static`:
|
||||
build a static htop binary; hwloc and delay accounting are not supported
|
||||
- default: *no*
|
||||
* `--enable-debug`:
|
||||
Enable asserts and internal sanity checks; implies a performance penalty
|
||||
- default: *no*
|
||||
|
||||
#### Performance Co-Pilot
|
||||
|
||||
* `--enable-pcp`:
|
||||
enable Performance Co-Pilot support via a new pcp-htop utility
|
||||
- dependency: *libpcp*
|
||||
- default: *no*
|
||||
|
||||
#### Linux
|
||||
|
||||
* `--enable-sensors`:
|
||||
enable libsensors(3) support for reading temperature data
|
||||
- dependencies: *libsensors-dev*(build-time), at runtime *libsensors* is loaded via `dlopen(3)` if available
|
||||
- default: *check*
|
||||
* `--enable-capabilities`:
|
||||
enable Linux capabilities support
|
||||
- dependency: *libcap*
|
||||
- default: *check*
|
||||
* `--with-proc`:
|
||||
location of a Linux-compatible proc filesystem
|
||||
- default: */proc*
|
||||
* `--enable-openvz`:
|
||||
enable OpenVZ support
|
||||
- default: *no*
|
||||
* `--enable-vserver`:
|
||||
enable VServer support
|
||||
- default: *no*
|
||||
* `--enable-ancient-vserver`:
|
||||
enable ancient VServer support (implies `--enable-vserver`)
|
||||
- default: *no*
|
||||
* `--enable-delayacct`:
|
||||
enable Linux delay accounting support
|
||||
- dependencies: *pkg-config*(build-time), *libnl-3* and *libnl-genl-3*
|
||||
- default: *check*
|
||||
|
||||
|
||||
## Runtime dependencies:
|
||||
`htop` has a set of fixed minimum runtime dependencies, which is kept as minimal as possible:
|
||||
* `ncurses` libraries for terminal handling (wide character support).
|
||||
|
||||
### Runtime optional dependencies:
|
||||
`htop` has a set of fixed optional dependencies, depending on build/configure option used:
|
||||
|
||||
#### Linux
|
||||
* `libdl`, if not building a static binary, is always required when support for optional dependencies (i.e. `libsensors`, `libsystemd`) is present.
|
||||
* `libcap`, user-space interfaces to POSIX 1003.1e capabilities, is always required when `--enable-capabilities` was used to configure `htop`.
|
||||
* `libsensors`, readout of temperatures and CPU speeds, is optional even when `--enable-sensors` was used to configure `htop`.
|
||||
* `libsystemd` is optional when `--enable-static` was not used to configure `htop`. If building statically and `libsystemd` is not found by `configure`, support for the systemd meter is disabled entirely.
|
||||
|
||||
`htop` checks for the availability of the actual runtime libraries as `htop` runs.
|
||||
|
||||
#### BSD
|
||||
On most BSD systems `kvm` is a requirement to read kernel information.
|
||||
|
||||
More information on required and optional dependencies can be found in [configure.ac](configure.ac).
|
||||
|
||||
## Usage
|
||||
See the manual page (`man htop`) or the help menu (**F1** or **h** inside `htop`) for a list of supported key commands.
|
||||
|
||||
## Support
|
||||
|
||||
If you have trouble running `htop` please consult your Operating System / Linux distribution documentation for getting support and filing bugs.
|
||||
If you have trouble running `htop` please consult your operating system / Linux distribution documentation for getting support and filing bugs.
|
||||
|
||||
## Bugs, development feedback
|
||||
|
||||
We have a [development mailing list](https://htop.dev/mailinglist.html). Feel free to subscribe for release announcements or asking questions on the development of htop.
|
||||
We have a [development mailing list](https://htop.dev/mailinglist.html). Feel free to subscribe for release announcements or asking questions on the development of `htop`.
|
||||
|
||||
You can also join our IRC channel #htop on freenode and talk to the developers there.
|
||||
You can also join our IRC channel [#htop on Libera.Chat](https://web.libera.chat/#htop) and talk to the developers there.
|
||||
|
||||
If you have found an issue with the source of htop, please check whether this has already been reported in our [Github issue tracker](https://github.com/htop-dev/htop/issues).
|
||||
If not, please file a new issue describing the problem you have found, the location in the source code you are referring to and a possible fix.
|
||||
If you have found an issue within the source of `htop`, please check whether this has already been reported in our [GitHub issue tracker](https://github.com/htop-dev/htop/issues).
|
||||
If not, please file a new issue describing the problem you have found, the potential location in the source code you are referring to and a possible fix if available.
|
||||
|
||||
## History
|
||||
|
||||
`htop` was invented, developed and maintained by Hisham Muhammad from 2004 to 2019. His [legacy repository](https://github.com/hishamhm/htop/) has been archived to preserve the history.
|
||||
`htop` was invented, developed and maintained by [Hisham Muhammad](https://hisham.hm/) from 2004 to 2019. His [legacy repository](https://github.com/hishamhm/htop/) has been archived to preserve the history.
|
||||
|
||||
In 2020 a [team](https://github.com/orgs/htop-dev/people) took over the development amicably and continues to maintain `htop` collaboratively.
|
||||
|
||||
## License
|
||||
|
||||
GNU General Public License, version 2 (GPL-2.0)
|
||||
GNU General Public License, version 2 (GPL-2.0) or, at your option, any later version.
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user