├── .gitignore ├── LICENSE ├── Makefile ├── README.md └── xfce-hkmon.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | .settings/ 2 | .cproject 3 | .project 4 | xfce-hkmon 5 | *.o 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | 341 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CXX ?= g++ 2 | CXXFLAGS += -std=c++0x -O3 -Wall -Wextra -pedantic -march=native 3 | LIBS := -lrt 4 | BIN := xfce-hkmon 5 | 6 | $(BIN): $(BIN).o 7 | $(CXX) -o $@ $^ $(CFLAGS) $(LIBS) 8 | strip $@ 9 | 10 | %.o: %.cpp 11 | $(CXX) -c -o $@ $< $(CXXFLAGS) 12 | 13 | .PHONY: clean 14 | 15 | clean: 16 | rm -f *.o $(BIN) 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Hacker's Monitor for XFCE 2 | 3 | ### Screenshot and doc 4 | 5 | See the project [web page](https://lightful.github.io/xfce-hkmon/). 6 | 7 | ### Installation 8 | 9 | 1. Download [xfce-hkmon.cpp](xfce-hkmon.cpp) and compile it (you only need gcc or clang installed): 10 | ```bash 11 | g++ -std=c++0x -O3 -lrt xfce-hkmon.cpp -o xfce-hkmon 12 | ``` 13 | 2. Place the executable somewhere (e.g. /usr/local/bin) 14 | 3. Add a XFCE Generic Monitor Applet (comes with most distros) with these settings: no label, 1 second period, *Bitstream Vera Sans Mono* font (recommended) and the following command: 15 | ``` 16 | /usr/local/bin/xfce-hkmon NET CPU TEMP IO RAM 17 | ``` 18 | -------------------------------------------------------------------------------- /xfce-hkmon.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Hacker's Monitor for XFCE Generic Monitor applet 3 | * Copyright (C) 2015-2018 Ciriaco Garcia de Celis 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | */ 19 | 20 | // g++ -std=c++0x -O3 -lrt xfce-hkmon.cpp -o xfce-hkmon (gcc >= 4.6 or clang++) 21 | // Recommended 1 second period and "Bitstream Vera Sans Mono" font on the applet 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | #define APP_VERSION "2.1" 43 | 44 | #define VA_STR(x) dynamic_cast(std::ostringstream().flush() << x).str() 45 | 46 | auto constexpr MB_i = 1000000LL; 47 | auto constexpr MB_f = 1000000.0; 48 | auto constexpr GB_i = 1000000000LL; 49 | auto constexpr GB_f = 1000000000.0; 50 | auto constexpr TB_i = 1000000000000LL; 51 | auto constexpr TB_f = 1000000000000.0; // only C++14 has a readable alternative 52 | 53 | void abortApp(const char* reason) 54 | { 55 | std::cout << "ERROR " << errno << ":\n" << (reason? reason : "") << "" 56 | << "" << strerror(errno) << ""; 57 | exit(2); 58 | } 59 | 60 | bool readFile(const char* inputFile, std::vector& buffer, bool mustExist = true) 61 | { 62 | int fd = open(inputFile, O_RDONLY); 63 | if (fd < 0) 64 | { 65 | if (mustExist) abortApp(inputFile); 66 | return false; 67 | } 68 | else 69 | { 70 | buffer.resize(4000); 71 | for (std::size_t offset = 0;;) 72 | { 73 | int bytes = ::read(fd, &buffer[offset], buffer.size() - offset - 1); 74 | if (bytes < 0) abortApp(inputFile); 75 | offset += bytes; 76 | if (offset + 1 == buffer.size()) buffer.resize(buffer.size() * 2); 77 | else if (bytes == 0) 78 | { 79 | close(fd); 80 | buffer[offset] = 0; 81 | buffer.resize(offset); 82 | return true; 83 | } 84 | } 85 | } 86 | } 87 | 88 | bool writeFile(const char* outputFile, const std::ostringstream& data) 89 | { 90 | bool success = false; 91 | int fd = open(outputFile, O_CREAT | O_WRONLY | O_TRUNC, 0640); 92 | if (fd >= 0) 93 | { 94 | const std::string& buffer = data.str(); 95 | if (write(fd, buffer.c_str(), buffer.length()) == (ssize_t) buffer.length()) success = true; 96 | close(fd); 97 | } 98 | return success; 99 | } 100 | 101 | template std::ostream& operator<<(std::ostream& out, const std::map& container) 102 | { 103 | for (const auto& item : container) out << item.first << '|' << item.second << '\t'; // write into storage 104 | return out << '\n'; 105 | } 106 | 107 | template bool fromString(const std::string& from, T& to) { return bool(std::istringstream(from) >> to); } 108 | 109 | template <> bool fromString(const std::string& from, std::string& to) { to = from; return true; } 110 | 111 | template std::istream& operator>>(std::istream& in, std::map& container) 112 | { 113 | while (!in.eof() && !in.fail() && (in.peek() != '\n')) // read from storage 114 | { 115 | std::string skey; 116 | if (std::getline(in, skey, '|')) 117 | { 118 | K key; 119 | V value; 120 | if (fromString(skey, key) && (in >> value)) 121 | { 122 | container.insert( { key, value } ); 123 | in.ignore(); // tab 124 | } 125 | } 126 | } 127 | return in.ignore(); 128 | } 129 | 130 | struct CPU 131 | { 132 | typedef int16_t Number; // -1 for all cores summary and 0,1,2,... for each core 133 | 134 | struct Core 135 | { 136 | int64_t user, nice, system, idle, iowait, irq, softirq, steal, guest, guestnice; 137 | uint64_t freq_hz; 138 | int64_t cpuUsed() const { return user + nice + system + irq + softirq + guest + guestnice; } 139 | int64_t cpuTotal() const { return cpuUsed() + idle + iowait + steal; } 140 | Core operator-(const Core& that) const 141 | { 142 | int64_t roundUserFix = user == that.user-1? 1 : 0; // compensate for physical host & virtual 143 | int64_t roundNiceFix = nice == that.nice-1? 1 : 0; // guests counters evil rounding 144 | return 145 | { 146 | user - that.user + roundUserFix, nice - that.nice + roundNiceFix, system - that.system, 147 | idle - that.idle, iowait - that.iowait, irq - that.irq, softirq - that.softirq, 148 | steal - that.steal, guest - that.guest + roundUserFix, guestnice - that.guestnice + roundNiceFix, 149 | (freq_hz + that.freq_hz) / 2 // <-- a more useful abstraction 150 | }; 151 | } 152 | }; 153 | 154 | std::map cores; 155 | 156 | void readProc() 157 | { 158 | std::vector buffer; 159 | readFile("/proc/stat", buffer); 160 | std::istringstream cpustat(&buffer[0]); 161 | std::string name; 162 | Number number; 163 | while (cpustat >> name) 164 | { 165 | if (name.find("cpu") == 0) 166 | { 167 | name.erase(0,3); 168 | if (name.empty()) number = -1; else if (!fromString(name, number)) continue; 169 | Core& core = cores[number]; 170 | cpustat >> core.user >> core.nice >> core.system >> core.idle 171 | >> core.iowait >> core.irq >> core.softirq; 172 | if (cpustat.peek() != '\n') cpustat >> core.steal; else core.steal = 0; // caution with old kernels 173 | if (cpustat.peek() != '\n') cpustat >> core.guest; else core.guest = 0; 174 | if (cpustat.peek() != '\n') cpustat >> core.guestnice; else core.guestnice = 0; 175 | core.freq_hz = 0; 176 | core.user -= core.guest; // adjust for the already accounted values (may cause evil rounding 177 | core.nice -= core.guestnice; // effects, though) 178 | } 179 | cpustat.ignore(buffer.size(), '\n'); 180 | } 181 | readFile("/proc/cpuinfo", buffer); 182 | std::istringstream cpuinfo(&buffer[0]); 183 | std::string key; 184 | std::string skip; 185 | uint64_t sum_freq = 0; 186 | number = -1; 187 | while (cpuinfo >> key) 188 | { 189 | if (key == "processor") cpuinfo >> skip >> number; 190 | else if ((key == "cpu") && (number >= 0)) 191 | { 192 | if ((cpuinfo >> skip) && (skip == "MHz")) 193 | { 194 | double mhz; 195 | cpuinfo >> skip >> mhz; 196 | cores[number].freq_hz = uint64_t(mhz * MB_i); 197 | sum_freq += cores[number].freq_hz; 198 | number = -1; 199 | } 200 | } 201 | cpuinfo.ignore(buffer.size(), '\n'); 202 | } 203 | auto allcpu = cores.find(-1); 204 | if (allcpu != cores.end()) allcpu->second.freq_hz = sum_freq; 205 | } 206 | }; 207 | 208 | std::ostream& operator<<(std::ostream& out, const CPU::Core& core) // write into storage 209 | { 210 | return out << core.user << ' ' << core.nice << ' ' << core.system << ' ' << core.idle << ' ' 211 | << core.iowait << ' ' << core.irq << ' ' << core.softirq << ' ' 212 | << core.steal << ' ' << core.guest << ' ' << core.guestnice << ' ' << core.freq_hz; 213 | } 214 | 215 | std::istream& operator>>(std::istream& in, CPU::Core& core) // read from storage 216 | { 217 | return in >> core.user >> core.nice >> core.system >> core.idle >> core.iowait >> core.irq 218 | >> core.softirq >> core.steal >> core.guest >> core.guestnice >> core.freq_hz; 219 | } 220 | 221 | struct Memory 222 | { 223 | struct RAM 224 | { 225 | uint64_t total; 226 | uint64_t available; 227 | uint64_t free; 228 | uint64_t shared; 229 | uint64_t buffers; 230 | uint64_t cached; 231 | uint64_t swapTotal; 232 | uint64_t swapFree; 233 | }; 234 | 235 | RAM ram; 236 | 237 | void readProc() 238 | { 239 | std::vector buffer; 240 | readFile("/proc/meminfo", buffer); 241 | std::istringstream meminfo(&buffer[0]); 242 | bool hasAvailable = false; 243 | std::string key; 244 | for (int count = 0; meminfo >> key;) 245 | { 246 | if (key == "MemTotal:") { count++; meminfo >> ram.total; } 247 | else if (key == "MemFree:") { count++; meminfo >> ram.free; } 248 | else if (key == "MemAvailable:") { count++; meminfo >> ram.available; hasAvailable = true; } 249 | else if (key == "Buffers:") { count++; meminfo >> ram.buffers; } 250 | else if (key == "Cached:") { count++; meminfo >> ram.cached; } 251 | else if (key == "SwapTotal:") { count++; meminfo >> ram.swapTotal; } 252 | else if (key == "SwapFree:") { count++; meminfo >> ram.swapFree; } 253 | else if (key == "Shmem:") { count++; meminfo >> ram.shared; } 254 | if (count == 7 + (hasAvailable? 1:0)) break; 255 | meminfo.ignore(buffer.size(), '\n'); 256 | } 257 | if (!hasAvailable) ram.available = ram.free + ram.buffers + ram.cached; // pre-2014 kernels 258 | } 259 | }; 260 | 261 | std::ostream& operator<<(std::ostream& out, const Memory::RAM& ram) 262 | { 263 | return out << ram.total << ' ' << ram.available << ' ' << ram.free << ' ' << ram.shared << ' ' 264 | << ram.buffers << ' ' << ram.cached << ' ' << ram.swapTotal << ' ' << ram.swapFree << '\n'; 265 | } 266 | 267 | std::istream& operator>>(std::istream& in, Memory::RAM& ram) 268 | { 269 | return in >> ram.total >> ram.available >> ram.free >> ram.shared 270 | >> ram.buffers >> ram.cached >> ram.swapTotal >> ram.swapFree; 271 | } 272 | 273 | struct IO 274 | { 275 | typedef std::string Name; 276 | 277 | struct Device 278 | { 279 | uint64_t bytesRead; 280 | uint64_t bytesWritten; 281 | uint32_t ioMsecs; 282 | uint64_t bytesSize; 283 | }; 284 | 285 | struct Bandwidth { double bytesPerSecond; }; 286 | 287 | std::map devices; 288 | 289 | void readProc() 290 | { 291 | std::vector buffer; 292 | readFile("/proc/diskstats", buffer); 293 | std::string name, prev; 294 | for (std::istringstream diskinfo(&buffer[0]);;) // search physical devices 295 | { 296 | uint64_t skip; 297 | if (!(diskinfo >> skip >> skip >> name)) break; 298 | if (!name.empty() 299 | && (prev.empty() || (name.find(prev) != 0)) // skip partitions 300 | && (name.find("dm") != 0)) // skip device mapper 301 | { 302 | prev = name; 303 | Device& device = devices[name]; 304 | uint64_t sectorsRd, sectorsWr; 305 | diskinfo >> skip >> skip >> sectorsRd 306 | >> skip >> skip >> skip >> sectorsWr 307 | >> skip >> skip >> device.ioMsecs; 308 | device.bytesRead = sectorsRd*512; 309 | device.bytesWritten = sectorsWr*512; 310 | device.bytesSize = 0; 311 | } 312 | diskinfo.ignore(buffer.size(), '\n'); 313 | } 314 | readFile("/proc/partitions", buffer); 315 | std::istringstream partinfo(&buffer[0]); 316 | uint64_t blocks; 317 | std::string skip; 318 | bool hasData = false; 319 | while (!hasData && std::getline(partinfo, skip)) if (skip.empty()) hasData = true; 320 | if (hasData) while (partinfo >> skip >> skip >> blocks >> name) 321 | { 322 | auto pdev = devices.find(name); 323 | if (pdev != devices.end()) pdev->second.bytesSize = blocks * 1024; 324 | partinfo.ignore(buffer.size(), '\n'); 325 | } 326 | } 327 | }; 328 | 329 | std::ostream& operator<<(std::ostream& out, const IO::Device& dev) 330 | { 331 | return out << dev.bytesRead << ' ' << dev.bytesWritten << ' ' << dev.ioMsecs << ' ' << dev.bytesSize; 332 | } 333 | 334 | std::istream& operator>>(std::istream& in, IO::Device& dev) 335 | { 336 | return in >> dev.bytesRead >> dev.bytesWritten >> dev.ioMsecs >> dev.bytesSize; 337 | } 338 | 339 | std::ostream& operator<<(std::ostream& out, const IO::Bandwidth& data) 340 | { 341 | if (data.bytesPerSecond < MB_i) 342 | return out << std::fixed << std::setprecision(1) << data.bytesPerSecond / MB_f << " MB/s"; 343 | if (data.bytesPerSecond < GB_i) 344 | return out << int64_t(data.bytesPerSecond / MB_f) << " MB/s"; 345 | return out << std::fixed << std::setprecision(3) << data.bytesPerSecond / GB_f << " GB/s"; 346 | } 347 | 348 | struct Network 349 | { 350 | typedef std::string Name; 351 | 352 | struct Interface 353 | { 354 | uint64_t bytesRecv; 355 | uint64_t bytesSent; 356 | uint64_t traffic() const { return bytesRecv + bytesSent; } 357 | }; 358 | 359 | struct Bandwidth 360 | { 361 | enum class Unit { bit, byte } unit; 362 | int64_t perSecond; 363 | }; 364 | 365 | std::map interfaces; 366 | 367 | void readProc() 368 | { 369 | std::vector buffer; 370 | readFile("/proc/net/dev", buffer); 371 | std::istringstream netinfo(&buffer[0]); 372 | netinfo.ignore(buffer.size(), '\n'); 373 | for (;;) 374 | { 375 | netinfo.ignore(buffer.size(), '\n'); 376 | std::string name; 377 | if (!std::getline(netinfo, name, ':')) break; // also handles kernels not having a space after ':' 378 | while (!name.empty() && (name.front() == ' ')) name.erase(name.begin()); // ltrim 379 | Interface& interface = interfaces[name]; 380 | uint64_t skip; 381 | netinfo >> interface.bytesRecv 382 | >> skip >> skip >> skip >> skip >> skip >> skip >> skip 383 | >> interface.bytesSent; 384 | } 385 | } 386 | }; 387 | 388 | std::ostream& operator<<(std::ostream& out, const Network::Interface& ifz) 389 | { 390 | return out << ifz.bytesRecv << ' ' << ifz.bytesSent; 391 | } 392 | 393 | std::istream& operator>>(std::istream& in, Network::Interface& ifz) 394 | { 395 | return in >> ifz.bytesRecv >> ifz.bytesSent; 396 | } 397 | 398 | std::ostream& operator<<(std::ostream& out, const Network::Bandwidth& speed) 399 | { 400 | char unit = (speed.unit == Network::Bandwidth::Unit::bit)? 'b' : 'B'; 401 | if (speed.perSecond < MB_i) return out << speed.perSecond / 1000 << " K" << unit << "ps"; 402 | return out << std::fixed << std::setprecision(3) << speed.perSecond / MB_f << " M" << unit << "ps"; 403 | } 404 | 405 | struct Health 406 | { 407 | typedef std::string Name; 408 | 409 | struct Thermometer 410 | { 411 | int32_t tempMilliCelsius; 412 | }; 413 | 414 | std::map thermometers; 415 | 416 | void readProc() 417 | { 418 | std::string coretemp; 419 | for (int hwmon = 0; hwmon < 256; hwmon++) 420 | { 421 | std::string base = VA_STR("/sys/class/hwmon/hwmon" << hwmon); 422 | std::vector buffer; 423 | if (!readFile(VA_STR(base << "/name").c_str(), buffer, false)) // pre-3.15 kernel? 424 | { 425 | base.append("/device"); 426 | if (!readFile(VA_STR(base << "/name").c_str(), buffer, false)) break; 427 | } 428 | std::istringstream sname(&buffer[0]); 429 | std::string name; 430 | if ((sname >> name) && (name == "coretemp")) 431 | { 432 | coretemp = base; 433 | break; 434 | } 435 | } 436 | 437 | if (!coretemp.empty()) for (int ic = 1; ic < 64; ic++) 438 | { 439 | std::vector buffer; 440 | if (!readFile(VA_STR(coretemp << "/temp" << ic << "_label").c_str(), buffer, false)) 441 | { 442 | if (thermometers.empty()) continue; else break; // Atom CPU may start at 2 (!?) 443 | } 444 | std::istringstream sname(&buffer[0]); 445 | std::string name; 446 | if (!std::getline(sname, name) || name.empty()) break; 447 | if (!readFile(VA_STR(coretemp << "/temp" << ic << "_input").c_str(), buffer, false)) break; 448 | std::istringstream temp(&buffer[0]); 449 | int32_t tempMilliCelsius; 450 | if (!(temp >> tempMilliCelsius)) break; 451 | thermometers[name].tempMilliCelsius = tempMilliCelsius; 452 | } 453 | } 454 | }; 455 | 456 | std::ostream& operator<<(std::ostream& out, const Health::Thermometer& thm) 457 | { 458 | return out << thm.tempMilliCelsius; 459 | } 460 | 461 | std::istream& operator>>(std::istream& in, Health::Thermometer& thm) 462 | { 463 | return in >> thm.tempMilliCelsius; 464 | } 465 | 466 | struct DataSize { uint64_t bytes; }; 467 | 468 | std::ostream& operator<<(std::ostream& out, const DataSize& data) 469 | { 470 | if (data.bytes < 5000) return out << "0 MB"; 471 | if (data.bytes < 10*MB_i) return out << std::fixed << std::setprecision(2) << data.bytes / MB_f << " MB"; 472 | if (data.bytes < 100*MB_i) return out << std::fixed << std::setprecision(1) << data.bytes / MB_f << " MB"; 473 | if (data.bytes < GB_i) return out << data.bytes / MB_i << " MB"; 474 | if (data.bytes < 10*GB_i) return out << std::fixed << std::setprecision(2) << data.bytes / GB_f << " GB"; 475 | if (data.bytes < 100*GB_i) return out << std::fixed << std::setprecision(1) << data.bytes / GB_f << " GB"; 476 | if (data.bytes < TB_i) return out << data.bytes / GB_i << " GB"; 477 | uint64_t tbx100 = data.bytes / (TB_i / 100); 478 | auto decimals = (tbx100 > 9999) || (tbx100 % 100 == 0)? 0 : (tbx100 % 10 == 0)? 1 : 2; // pretty disk sizes 479 | return out << std::fixed << std::setprecision(decimals) << data.bytes / TB_f << " TB"; 480 | } 481 | 482 | template struct Padded { uint64_t max; T value; }; 483 | 484 | template std::ostream& operator<<(std::ostream& out, const Padded& data) 485 | { 486 | if (!std::isnan(data.value)) for (T div = data.max;; div /= 10) // avoid infinite loop with NaN numbers 487 | { 488 | if ((data.value >= div) && (data.value >= 1)) break; 489 | if ((data.value < 1) && (div <= 1)) break; 490 | out << " "; // two spaces in place of each missing digit (same width in XFCE Generic Monitor applet) 491 | } 492 | return out << data.value; 493 | } 494 | 495 | int main(int argc, char** argv) 496 | { 497 | if (argc < 2) 498 | { 499 | std::cerr << "usage: " << argv[0] << " [NET|] [CPU] [TEMP] [IO] [RAM]" << std::endl; 500 | return 1; 501 | } 502 | 503 | std::shared_ptr new_CPU, old_CPU; 504 | std::shared_ptr new_Memory; 505 | std::shared_ptr new_IO, old_IO; 506 | std::shared_ptr new_Network, old_Network; 507 | Network::Bandwidth::Unit netSpeedUnit = Network::Bandwidth::Unit::bit; 508 | std::shared_ptr new_Health; 509 | std::string selectedNetworkInterface; 510 | 511 | bool singleLine = false; 512 | int posRam = 0; 513 | int posTemp = 0; 514 | for (int i = 1; i < argc; i++) 515 | { 516 | std::string arg(argv[i]); 517 | if ((arg == "LINE")) singleLine = true; 518 | else if ((arg == "CPU")) new_CPU.reset(new CPU()); 519 | else if ((arg == "RAM")) posRam = i, new_Memory.reset(new Memory()); 520 | else if ((arg == "IO")) new_IO.reset(new IO()); 521 | else if ((arg == "NET")) new_Network.reset(new Network()); 522 | else if ((arg == "NET8")) new_Network.reset(new Network()), netSpeedUnit = Network::Bandwidth::Unit::byte; 523 | else if ((arg == "TEMP")) posTemp = i, new_Health.reset(new Health()); 524 | else 525 | { 526 | new_Network.reset(new Network()); 527 | selectedNetworkInterface = argv[i]; 528 | } 529 | } 530 | 531 | timespec tp; 532 | if (clock_gettime(CLOCK_MONOTONIC, &tp) != 0) abortApp("clock_gettime"); 533 | uint64_t nowIs = tp.tv_sec * GB_i + tp.tv_nsec; 534 | int64_t nsecsElapsed = 0; 535 | 536 | std::ostringstream newState; 537 | newState << APP_VERSION << " " << nowIs << "\n"; 538 | if (new_CPU) { new_CPU->readProc(); newState << "CPU|" << new_CPU->cores; } 539 | if (new_Memory) { new_Memory->readProc(); } 540 | if (new_IO) { new_IO->readProc(); newState << "IO|" << new_IO->devices; } 541 | if (new_Network) { new_Network->readProc(); newState << "Network|" << new_Network->interfaces; } 542 | if (new_Health) { new_Health->readProc(); } 543 | 544 | for (int locTry = 0;; locTry++) // read the previous state from disk and store the new state 545 | { 546 | std::vector oldStateData; 547 | std::ostringstream stateFileName; 548 | 549 | if (locTry == 0) 550 | stateFileName << "/run/user/" << getuid() << "/xfce-hkmon.dat"; 551 | else if (locTry == 1) 552 | stateFileName << "/tmp/xfce-hkmon." << getuid() << ".dat"; 553 | else 554 | abortApp("can't write tmpfile"); 555 | 556 | if (readFile(stateFileName.str().c_str(), oldStateData, false)) 557 | { 558 | std::istringstream oldState(&oldStateData[0]); 559 | std::string version; 560 | uint64_t previouslyWas; 561 | oldState >> version >> previouslyWas; 562 | nsecsElapsed = nowIs - previouslyWas; 563 | oldState.ignore(oldStateData.size(), '\n'); 564 | if (version == APP_VERSION) 565 | { 566 | std::string category; 567 | while (std::getline(oldState, category, '|')) 568 | { 569 | if (category == "CPU") { old_CPU.reset(new CPU()); oldState >> old_CPU->cores; } 570 | else if (category == "IO") { old_IO.reset(new IO()); oldState >> old_IO->devices; } 571 | else if (category == "Network") 572 | { 573 | old_Network.reset(new Network()); oldState >> old_Network->interfaces; 574 | } 575 | else oldState.ignore(oldStateData.size(), '\n'); 576 | } 577 | } 578 | } 579 | 580 | if (writeFile(stateFileName.str().c_str(), newState)) break; 581 | } 582 | 583 | std::ostringstream reportStd, reportDetail; 584 | double secsElapsed = nsecsElapsed / GB_f; 585 | 586 | if (new_Network && old_Network && nsecsElapsed) // NET report 587 | { 588 | if (selectedNetworkInterface.empty()) 589 | { 590 | int64_t maxBandwidth = -1; 591 | uint64_t selectedTraffic = 0; 592 | for (auto itn = new_Network->interfaces.cbegin(); itn != new_Network->interfaces.cend(); ++itn) 593 | { 594 | if (itn->first == "lo") continue; 595 | auto ito = old_Network->interfaces.find(itn->first); 596 | if (ito == old_Network->interfaces.end()) continue; 597 | int64_t transferred = itn->second.bytesRecv - ito->second.bytesRecv; 598 | transferred += itn->second.bytesSent - ito->second.bytesSent; 599 | if (transferred < maxBandwidth) continue; 600 | if ((transferred > maxBandwidth) || (itn->second.traffic() > selectedTraffic)) 601 | { 602 | maxBandwidth = transferred; 603 | selectedTraffic = itn->second.traffic(); 604 | selectedNetworkInterface = itn->first; 605 | } 606 | } 607 | } 608 | 609 | for (auto itn = new_Network->interfaces.cbegin(); itn != new_Network->interfaces.cend(); ++itn) 610 | { 611 | auto ito = old_Network->interfaces.find(itn->first); 612 | if (ito == old_Network->interfaces.end()) continue; 613 | const Network::Interface& nif = itn->second; 614 | const Network::Interface& oif = ito->second; 615 | bool isSelectedInterface = itn->first == selectedNetworkInterface; 616 | if (!nif.traffic() && !isSelectedInterface) continue; 617 | 618 | auto dumpNet = [&](const char* iconIdle, const char* iconBusy, uint64_t newBytes, uint64_t oldBytes) 619 | { 620 | int64_t delta = newBytes - oldBytes; 621 | int64_t speed = (netSpeedUnit == Network::Bandwidth::Unit::byte? 1 : 8) * delta / secsElapsed; 622 | const char* icon = delta? iconBusy : iconIdle; 623 | reportDetail << " " << icon << " " << DataSize { newBytes }; 624 | if (speed > 0) reportDetail << " - " << Network::Bandwidth { netSpeedUnit, speed }; 625 | reportDetail << " \n"; 626 | if (isSelectedInterface) 627 | reportStd << std::setw(6) << Network::Bandwidth { netSpeedUnit, speed } << " " << icon 628 | << (singleLine? " " : " \n"); 629 | }; 630 | 631 | reportDetail << " " << itn->first << ": "; 632 | if (isSelectedInterface) reportDetail << "\u2713"; // "check mark" character 633 | reportDetail << "\n"; 634 | dumpNet("\u25B3", "\u25B2", nif.bytesSent, oif.bytesSent); // white/black up pointing triangles 635 | dumpNet("\u25BD", "\u25BC", nif.bytesRecv, oif.bytesRecv); // down pointing triangles 636 | } 637 | } 638 | 639 | if (new_CPU && old_CPU) // CPU report 640 | { 641 | struct CpuStat { CPU::Number number; double percent; double ghz; }; 642 | std::multimap rankByGhzUsage; 643 | double cum_weighted_ghz = 0; 644 | for (auto itn = new_CPU->cores.cbegin(); itn != new_CPU->cores.cend(); ++itn) 645 | { 646 | if (itn->first < 0) continue; 647 | auto ito = old_CPU->cores.find(itn->first); 648 | if (ito == old_CPU->cores.end()) continue; 649 | CPU::Core diff = itn->second - ito->second; 650 | if (diff.cpuTotal() == 0) continue; 651 | double unityUsage = 1.0 * diff.cpuUsed() / diff.cpuTotal(); 652 | double ghz = diff.freq_hz / GB_f; 653 | double ghzUsage = ghz * unityUsage; 654 | cum_weighted_ghz += ghzUsage; 655 | rankByGhzUsage.insert({ ghzUsage, CpuStat { itn->first, 100.0 * unityUsage, ghz } }); 656 | } 657 | 658 | auto allnew = new_CPU->cores.find(-1); 659 | auto allold = old_CPU->cores.find(-1); 660 | if ((allnew != new_CPU->cores.end()) && (allold != old_CPU->cores.end())) 661 | { 662 | const CPU::Core& ncpu = allnew->second; 663 | const CPU::Core& ocpu = allold->second; 664 | CPU::Core diff = ncpu - ocpu; 665 | auto cpuTotal = diff.cpuTotal(); 666 | if (cpuTotal > 0) 667 | { 668 | auto cpuTotalSinceBoot = ncpu.cpuTotal(); 669 | double usagePercent = 100.0 * diff.cpuUsed() / cpuTotal; 670 | 671 | auto dumpPercent = [&](const char* title, int64_t user_hz, int64_t user_hz__sinceBoot) 672 | { 673 | reportDetail << " " << Padded { 100, 100.0 * user_hz / cpuTotal } << "% " << title 674 | << " (" << 100.0 * user_hz__sinceBoot / cpuTotalSinceBoot << "%) \n"; 675 | }; 676 | 677 | reportStd << std::setw(6) << std::fixed << std::setprecision(1) << usagePercent << "%"; 678 | 679 | reportDetail << " CPU \u2699 " << std::fixed << std::setprecision(2) << usagePercent << "% \u2248 "; 680 | 681 | if (cum_weighted_ghz < 1) 682 | reportDetail << uint64_t(cum_weighted_ghz * 1000) << " MHz:\n" << std::setprecision(2); 683 | else 684 | reportDetail << std::setprecision(1) << cum_weighted_ghz << " GHz:\n" << std::setprecision(2); 685 | 686 | dumpPercent("user", diff.user, ncpu.user); 687 | dumpPercent("nice", diff.nice, ncpu.nice); 688 | dumpPercent("system", diff.system, ncpu.system); 689 | dumpPercent("idle", diff.idle, ncpu.idle); 690 | dumpPercent("iowait", diff.iowait, ncpu.iowait); 691 | if (ncpu.irq) dumpPercent("irq", diff.irq, ncpu.irq); 692 | if (ncpu.softirq) dumpPercent("softirq", diff.softirq, ncpu.softirq); 693 | if (ncpu.steal) dumpPercent("steal", diff.steal, ncpu.steal); 694 | if (ncpu.guest) dumpPercent("guest", diff.guest, ncpu.guest); 695 | if (ncpu.guestnice) dumpPercent("guest nice", diff.guestnice, ncpu.guestnice); 696 | 697 | int maxCpu = 8; 698 | for (auto itc = rankByGhzUsage.crbegin(); maxCpu-- && (itc != rankByGhzUsage.crend()); ++itc) 699 | { 700 | reportDetail << " " << std::fixed 701 | << std::setprecision(2) << Padded { 100, itc->second.percent } << "% cpu " 702 | << Padded { uint64_t(new_CPU->cores.size() > 10? 10 : 1), itc->second.number } 703 | << " @" << std::setprecision(3) << Padded { 10, itc->second.ghz } << " GHz \n"; 704 | } 705 | } 706 | } 707 | } 708 | 709 | if (new_Memory) // RAM report 710 | { 711 | if (new_CPU && (!posTemp || (posRam < posTemp))) 712 | reportStd << " " << new_Memory->ram.available/1024 << "M" << (singleLine? " " : "\n"); 713 | 714 | reportDetail << " Memory " << new_Memory->ram.total/1024 << " MiB:\n" 715 | << Padded { 1000000, new_Memory->ram.available/1024 } << " MiB available \n" 716 | << Padded { 1000000, (new_Memory->ram.cached+new_Memory->ram.buffers)/1024 } 717 | << " MiB cache/buff \n"; 718 | 719 | if (new_Memory->ram.shared) 720 | reportDetail << Padded { 1000000, new_Memory->ram.shared/1024 } << " MiB shared \n"; 721 | 722 | if (new_Memory->ram.swapTotal) 723 | reportDetail << Padded { 1000000, (new_Memory->ram.swapTotal-new_Memory->ram.swapFree)/1024 } 724 | << " MiB swap of " << new_Memory->ram.swapTotal/1024 << " \n"; 725 | } 726 | 727 | if (new_IO && old_IO && nsecsElapsed) // IO report 728 | { 729 | for (auto nitd = new_IO->devices.cbegin(); nitd != new_IO->devices.cend(); ++nitd) 730 | { 731 | const IO::Device& device = nitd->second; 732 | auto prevdev = old_IO->devices.find(nitd->first); 733 | if ((device.bytesRead || device.bytesWritten) && (prevdev != old_IO->devices.end())) 734 | { 735 | reportDetail << " " << nitd->first << " \u26C1 " << DataSize { device.bytesSize } << ":\n"; 736 | 737 | auto dumpIO = [&](const char* iconIdle, const char* iconBusy, uint64_t newBytes, uint64_t oldBytes) 738 | { 739 | auto transferred = newBytes - oldBytes; 740 | reportDetail << " " << (transferred? iconBusy : iconIdle) << " " << DataSize { newBytes }; 741 | if (transferred) reportDetail << " - " << IO::Bandwidth { transferred / secsElapsed }; 742 | reportDetail << " \n"; 743 | }; 744 | 745 | dumpIO("\u25B3", "\u25B2", device.bytesWritten, prevdev->second.bytesWritten); 746 | dumpIO("\u25BD", "\u25BC", device.bytesRead, prevdev->second.bytesRead); 747 | } 748 | } 749 | } 750 | 751 | if (new_Health) // TEMP report 752 | { 753 | struct ThermalStat 754 | { 755 | ThermalStat() : min(std::numeric_limits::max()), 756 | max(std::numeric_limits::min()), 757 | avg(0), count(0) 758 | {} 759 | int32_t min; 760 | int32_t max; 761 | int32_t avg; 762 | std::size_t count; 763 | Health::Name firstName; 764 | }; 765 | 766 | std::map statByCategory; 767 | int32_t maxAbsTemp = std::numeric_limits::min(); 768 | for (const auto& itt : new_Health->thermometers) 769 | { 770 | std::string key = itt.first; 771 | auto catEnd = key.find(" "); 772 | if (catEnd != std::string::npos) key.erase(catEnd); 773 | auto its = statByCategory.find(key); 774 | if (its == statByCategory.end()) 775 | { 776 | its = statByCategory.insert( { key, ThermalStat() } ).first; 777 | its->second.firstName = itt.first; 778 | } 779 | if (itt.second.tempMilliCelsius < its->second.min) its->second.min = itt.second.tempMilliCelsius; 780 | if (itt.second.tempMilliCelsius > its->second.max) its->second.max = itt.second.tempMilliCelsius; 781 | if (itt.second.tempMilliCelsius > maxAbsTemp) maxAbsTemp = itt.second.tempMilliCelsius; 782 | auto prevTotal = its->second.avg * its->second.count; 783 | its->second.count++; 784 | its->second.avg = (prevTotal + itt.second.tempMilliCelsius) / its->second.count; 785 | } 786 | 787 | if (new_CPU && (maxAbsTemp >= 0) && (!posRam || (posTemp < posRam))) 788 | reportStd << std::setw(4) << maxAbsTemp / 1000 << "ºC" << (singleLine? " " : "\n"); 789 | 790 | if (!statByCategory.empty()) reportDetail << " Temperature: \n"; 791 | 792 | for (auto its = statByCategory.crbegin(); its != statByCategory.crend(); ++its) 793 | { 794 | if (its->second.count == 1) 795 | reportDetail << " " << its->second.firstName << ": " << its->second.max / 1000 << "ºC \n"; 796 | else 797 | reportDetail << " \u2206" << its->second.max / 1000 798 | << "ºC \u2207" << its->second.min / 1000 799 | << "ºC \u222B" << its->second.avg / 1000 800 | << "ºC (" << its->second.count << " " << its->first << ") \n"; 801 | } 802 | } 803 | 804 | std::string sReportStd = reportStd.str(); 805 | if (!sReportStd.empty() && (sReportStd.back() == '\n')) sReportStd.erase(sReportStd.end()-1); 806 | 807 | if (sReportStd.empty()) sReportStd = "Hacker's\nMonitor"; // dummy message (allow the user to right-click) 808 | 809 | std::string sReportDetail = reportDetail.str(); 810 | if (!sReportDetail.empty() && (sReportDetail.back() == '\n')) sReportDetail.erase(sReportDetail.end()-1); 811 | 812 | std::cout << "" << sReportStd << "" << sReportDetail << ""; 813 | return 0; 814 | } 815 | --------------------------------------------------------------------------------