From 5d94c0a7d44a2255b81815a52a7056a94a39842d Mon Sep 17 00:00:00 2001 From: nippy Date: Sat, 18 Apr 2026 13:49:56 +0200 Subject: update Raveos themes --- .../installed/Vitals@CoreCoding.com/LICENSE | 339 +++++ .../installed/Vitals@CoreCoding.com/extension.js | 656 +++++++++ .../Vitals@CoreCoding.com/helpers/file.js | 82 ++ .../Vitals@CoreCoding.com/helpers/subprocess.js | 54 + .../icons/gnome/battery-symbolic.svg | 1 + .../icons/gnome/cpu-symbolic.svg | 1 + .../icons/gnome/fan-symbolic.svg | 1 + .../icons/gnome/gpu-symbolic.svg | 1 + .../icons/gnome/memory-symbolic.svg | 1 + .../icons/gnome/network-download-symbolic.svg | 2 + .../icons/gnome/network-symbolic.svg | 2 + .../icons/gnome/network-upload-symbolic.svg | 2 + .../icons/gnome/storage-symbolic.svg | 1 + .../icons/gnome/system-symbolic.svg | 1 + .../icons/gnome/temperature-symbolic.svg | 1 + .../icons/gnome/voltage-symbolic.svg | 1 + .../icons/original/battery-symbolic.svg | 8 + .../icons/original/cpu-symbolic.svg | 1 + .../icons/original/fan-symbolic.svg | 133 ++ .../icons/original/gpu-symbolic.svg | 15 + .../icons/original/memory-symbolic.svg | 1 + .../icons/original/network-download-symbolic.svg | 1 + .../icons/original/network-symbolic.svg | 1 + .../icons/original/network-upload-symbolic.svg | 1 + .../icons/original/storage-symbolic.svg | 3 + .../icons/original/system-symbolic.svg | 3 + .../icons/original/temperature-symbolic.svg | 45 + .../icons/original/voltage-symbolic.svg | 125 ++ .../locale/ar/LC_MESSAGES/vitals.mo | Bin 0 -> 13793 bytes .../locale/be/LC_MESSAGES/vitals.mo | Bin 0 -> 10985 bytes .../locale/ca/LC_MESSAGES/vitals.mo | Bin 0 -> 3343 bytes .../locale/cs/LC_MESSAGES/vitals.mo | Bin 0 -> 12216 bytes .../locale/de/LC_MESSAGES/vitals.mo | Bin 0 -> 11776 bytes .../locale/es/LC_MESSAGES/vitals.mo | Bin 0 -> 5518 bytes .../locale/fi_FI/LC_MESSAGES/vitals.mo | Bin 0 -> 4004 bytes .../locale/fr/LC_MESSAGES/vitals.mo | Bin 0 -> 12096 bytes .../locale/it/LC_MESSAGES/vitals.mo | Bin 0 -> 3030 bytes .../locale/my_MM/LC_MESSAGES/vitals.mo | Bin 0 -> 17140 bytes .../locale/nl/LC_MESSAGES/vitals.mo | Bin 0 -> 6939 bytes .../locale/oc/LC_MESSAGES/vitals.mo | Bin 0 -> 3817 bytes .../locale/pl/LC_MESSAGES/vitals.mo | Bin 0 -> 3501 bytes .../locale/pt/LC_MESSAGES/vitals.mo | Bin 0 -> 3935 bytes .../locale/pt_BR/LC_MESSAGES/vitals.mo | Bin 0 -> 8396 bytes .../locale/ru/LC_MESSAGES/vitals.mo | Bin 0 -> 10711 bytes .../locale/sk/LC_MESSAGES/vitals.mo | Bin 0 -> 8559 bytes .../locale/tr/LC_MESSAGES/vitals.mo | Bin 0 -> 8416 bytes .../locale/uk/LC_MESSAGES/vitals.mo | Bin 0 -> 15510 bytes .../locale/zh_CN/LC_MESSAGES/vitals.mo | Bin 0 -> 7900 bytes .../installed/Vitals@CoreCoding.com/menuItem.js | 77 ++ .../installed/Vitals@CoreCoding.com/metadata.json | 21 + .../installed/Vitals@CoreCoding.com/prefs.js | 138 ++ .../installed/Vitals@CoreCoding.com/prefs.ui | 1394 ++++++++++++++++++++ .../schemas/gschemas.compiled | Bin 0 -> 1972 bytes .../org.gnome.shell.extensions.vitals.gschema.xml | 155 +++ .../installed/Vitals@CoreCoding.com/sensors.js | 1072 +++++++++++++++ .../installed/Vitals@CoreCoding.com/stylesheet.css | 19 + .../installed/Vitals@CoreCoding.com/values.js | 358 +++++ 57 files changed, 4717 insertions(+) create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/LICENSE create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/extension.js create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/helpers/file.js create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/helpers/subprocess.js create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/battery-symbolic.svg create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/cpu-symbolic.svg create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/fan-symbolic.svg create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/gpu-symbolic.svg create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/memory-symbolic.svg create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/network-download-symbolic.svg create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/network-symbolic.svg create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/network-upload-symbolic.svg create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/storage-symbolic.svg create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/system-symbolic.svg create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/temperature-symbolic.svg create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/voltage-symbolic.svg create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/battery-symbolic.svg create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/cpu-symbolic.svg create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/fan-symbolic.svg create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/gpu-symbolic.svg create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/memory-symbolic.svg create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/network-download-symbolic.svg create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/network-symbolic.svg create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/network-upload-symbolic.svg create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/storage-symbolic.svg create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/system-symbolic.svg create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/temperature-symbolic.svg create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/voltage-symbolic.svg create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/ar/LC_MESSAGES/vitals.mo create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/be/LC_MESSAGES/vitals.mo create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/ca/LC_MESSAGES/vitals.mo create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/cs/LC_MESSAGES/vitals.mo create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/de/LC_MESSAGES/vitals.mo create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/es/LC_MESSAGES/vitals.mo create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/fi_FI/LC_MESSAGES/vitals.mo create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/fr/LC_MESSAGES/vitals.mo create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/it/LC_MESSAGES/vitals.mo create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/my_MM/LC_MESSAGES/vitals.mo create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/nl/LC_MESSAGES/vitals.mo create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/oc/LC_MESSAGES/vitals.mo create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/pl/LC_MESSAGES/vitals.mo create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/pt/LC_MESSAGES/vitals.mo create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/pt_BR/LC_MESSAGES/vitals.mo create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/ru/LC_MESSAGES/vitals.mo create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/sk/LC_MESSAGES/vitals.mo create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/tr/LC_MESSAGES/vitals.mo create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/uk/LC_MESSAGES/vitals.mo create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/zh_CN/LC_MESSAGES/vitals.mo create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/menuItem.js create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/metadata.json create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/prefs.js create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/prefs.ui create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/schemas/gschemas.compiled create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/schemas/org.gnome.shell.extensions.vitals.gschema.xml create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/sensors.js create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/stylesheet.css create mode 100644 raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/values.js (limited to 'raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com') diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/LICENSE b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/LICENSE new file mode 100644 index 0000000..d7f1051 --- /dev/null +++ b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/LICENSE @@ -0,0 +1,339 @@ +GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +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 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 +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +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 + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + 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 +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +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 +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +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 +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + 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 +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +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. + + {description} + Copyright (C) {year} {fullname} + + 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 + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + 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., + 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) 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. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + {signature of Ty Coon}, 1 April 1989 + Ty Coon, President of Vice + +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 Lesser General +Public License instead of this License. diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/extension.js b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/extension.js new file mode 100644 index 0000000..642c4d7 --- /dev/null +++ b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/extension.js @@ -0,0 +1,656 @@ +import Clutter from 'gi://Clutter'; +import Gio from 'gi://Gio'; +import GLib from 'gi://GLib'; +import GObject from 'gi://GObject'; +import St from 'gi://St' + +import * as PanelMenu from 'resource:///org/gnome/shell/ui/panelMenu.js'; +import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js'; +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; +import * as Util from 'resource:///org/gnome/shell/misc/util.js'; + +import * as Sensors from './sensors.js'; + +import {Extension, gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js'; + +import * as MessageTray from 'resource:///org/gnome/shell/ui/messageTray.js'; +import * as Values from './values.js'; +import * as Config from 'resource:///org/gnome/shell/misc/config.js'; +import * as MenuItem from './menuItem.js'; + +let vitalsMenu; + +var VitalsMenuButton = GObject.registerClass({ + GTypeName: 'VitalsMenuButton', +}, class VitalsMenuButton extends PanelMenu.Button { + _init(extensionObject) { + super._init(Clutter.ActorAlign.FILL); + + this._extensionObject = extensionObject; + this._settings = extensionObject.getSettings(); + + this._sensorIcons = { + 'temperature' : { 'icon': 'temperature-symbolic.svg' }, + 'voltage' : { 'icon': 'voltage-symbolic.svg' }, + 'fan' : { 'icon': 'fan-symbolic.svg' }, + 'memory' : { 'icon': 'memory-symbolic.svg' }, + 'processor' : { 'icon': 'cpu-symbolic.svg' }, + 'system' : { 'icon': 'system-symbolic.svg' }, + 'network' : { 'icon': 'network-symbolic.svg', + 'icon-rx': 'network-download-symbolic.svg', + 'icon-tx': 'network-upload-symbolic.svg' }, + 'storage' : { 'icon': 'storage-symbolic.svg' }, + 'battery' : { 'icon': 'battery-symbolic.svg' }, + 'gpu' : { 'icon': 'gpu-symbolic.svg' } + } + + // list with the prefixes for the according themes, the index of each + // item must match the index on the combo box + this._sensorsIconPathPrefix = ['/icons/original/', '/icons/gnome/']; + + this._warnings = []; + this._sensorMenuItems = {}; + this._hotLabels = {}; + this._hotItems = {}; + this._groups = {}; + this._widths = {}; + this._numGpus = 1; + this._newGpuDetected = false; + this._newGpuDetectedCount = 0; + this._last_query = new Date().getTime(); + + this._sensors = new Sensors.Sensors(this._settings, this._sensorIcons); + this._values = new Values.Values(this._settings, this._sensorIcons); + this._menuLayout = new St.BoxLayout({ + vertical: false, + clip_to_allocation: true, + x_align: Clutter.ActorAlign.START, + y_align: Clutter.ActorAlign.CENTER, + reactive: true, + x_expand: true, + style_class: 'vitals-panel-menu' + }); + + this._drawMenu(); + this.add_child(this._menuLayout); + this._settingChangedSignals = []; + this._refreshTimeoutId = null; + + this._addSettingChangedSignal('update-time', this._updateTimeSettingChanged.bind(this)); + this._addSettingChangedSignal('position-in-panel', this._positionInPanelChanged.bind(this)); + this._addSettingChangedSignal('menu-centered', this._positionInPanelChanged.bind(this)); + this._addSettingChangedSignal('icon-style', this._iconStyleChanged.bind(this)); + + let settings = [ 'use-higher-precision', 'alphabetize', 'hide-zeros', 'fixed-widths', 'hide-icons', 'unit', 'memory-measurement', 'include-public-ip', 'network-speed-format', 'storage-measurement', 'include-static-info', 'include-static-gpu-info' ]; + for (let setting of Object.values(settings)) + this._addSettingChangedSignal(setting, this._redrawMenu.bind(this)); + + // add signals for show- preference based categories + for (let sensor in this._sensorIcons) + this._addSettingChangedSignal('show-' + sensor, this._showHideSensorsChanged.bind(this)); + + this._initializeMenu(); + + // start off with fresh sensors + this._querySensors(); + + // start monitoring sensors + this._initializeTimer(); + } + + _initializeMenu() { + // display sensor categories + for (let sensor in this._sensorIcons) { + // groups associated sensors under accordion menu + if (sensor in this._groups) continue; + + //handle gpus separately. + if (sensor === 'gpu') continue; + + this._initializeMenuGroup(sensor, sensor); + } + + for (let i = 1; i <= this._numGpus; i++) + this._initializeMenuGroup('gpu#' + i, 'gpu', (this._numGpus > 1 ? ' ' + i : '')); + + // add separator + this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); + + let item = new PopupMenu.PopupBaseMenuItem({ + reactive: false, + style_class: 'vitals-menu-button-container' + }); + + let customButtonBox = new St.BoxLayout({ + style_class: 'vitals-button-box', + vertical: false, + clip_to_allocation: true, + x_align: Clutter.ActorAlign.CENTER, + y_align: Clutter.ActorAlign.CENTER, + reactive: true, + x_expand: true + }); + + // custom round refresh button + let refreshButton = this._createRoundButton('view-refresh-symbolic', _('Refresh')); + refreshButton.connect('clicked', (self) => { + // force refresh by clearing history + this._sensors.resetHistory(); + this._values.resetHistory(this._numGpus); + + // make sure timer fires at next full interval + this._updateTimeChanged(); + + // refresh sensors now + this._querySensors(); + }); + customButtonBox.add_child(refreshButton); + + // custom round monitor button + let monitorButton = this._createRoundButton('org.gnome.SystemMonitor-symbolic', _('System Monitor')); + monitorButton.connect('clicked', (self) => { + this.menu._getTopMenu().close(); + Util.spawn(this._settings.get_string('monitor-cmd').split(" ")); + }); + customButtonBox.add_child(monitorButton); + + // custom round preferences button + let prefsButton = this._createRoundButton('preferences-system-symbolic', _('Preferences')); + prefsButton.connect('clicked', (self) => { + this.menu._getTopMenu().close(); + this._extensionObject.openPreferences(); + }); + customButtonBox.add_child(prefsButton); + + // now add the buttons to the top bar + item.actor.add_child(customButtonBox); + + // add buttons + this.menu.addMenuItem(item); + + // query sensors on menu open + this._menuStateChangeId = this.menu.connect('open-state-changed', (self, isMenuOpen) => { + if (isMenuOpen) { + // make sure timer fires at next full interval + this._updateTimeChanged(); + + // refresh sensors now + this._querySensors(); + } + }); + } + + _initializeMenuGroup(groupName, optionName, menuSuffix = '', position = -1) { + this._groups[groupName] = new PopupMenu.PopupSubMenuMenuItem(_(this._ucFirst(groupName) + menuSuffix), true); + this._groups[groupName].icon.gicon = Gio.icon_new_for_string(this._sensorIconPath(groupName)); + + // hide menu items that user has requested to not include + if (!this._settings.get_boolean('show-' + optionName)) + this._groups[groupName].actor.hide(); + + if (!this._groups[groupName].status) { + this._groups[groupName].status = this._defaultLabel(); + this._groups[groupName].actor.insert_child_at_index(this._groups[groupName].status, 4); + this._groups[groupName].status.text = _('No Data'); + } + + if(position == -1) this.menu.addMenuItem(this._groups[groupName]); + else this.menu.addMenuItem(this._groups[groupName], position); + } + + _createRoundButton(iconName) { + let button = new St.Button({ + style_class: 'message-list-clear-button button vitals-button-action' + }); + + button.child = new St.Icon({ + icon_name: iconName + }); + + return button; + } + + _removeMissingHotSensors(hotSensors) { + for (let i = hotSensors.length - 1; i >= 0; i--) { + let sensor = hotSensors[i]; + + // make sure default icon (if any) stays visible + if (sensor == '_default_icon_') continue; + + // removes sensors that are no longer available + if (!this._sensorMenuItems[sensor]) { + hotSensors.splice(i, 1); + this._removeHotItem(sensor); + } + } + + return hotSensors; + } + + _saveHotSensors(hotSensors) { + // removes any sensors that may not currently be available + hotSensors = this._removeMissingHotSensors(hotSensors); + + this._settings.set_strv('hot-sensors', hotSensors.filter( + function(item, pos) { + return hotSensors.indexOf(item) == pos; + } + )); + } + + _initializeTimer() { + // used to query sensors and update display + let update_time = this._settings.get_int('update-time'); + this._refreshTimeoutId = GLib.timeout_add_seconds( + GLib.PRIORITY_DEFAULT, + update_time, + (self) => { + // always query sensors (for panel display when hot) + this._querySensors(); + return GLib.SOURCE_CONTINUE; + } + ); + } + + _createHotItem(key, value) { + let item = new St.BoxLayout({ + style_class: 'vitals-panel-item', + }); + this._hotItems[key] = item; + this._menuLayout.add_child(item); + + if (!this._settings.get_boolean('hide-icons') || key == '_default_icon_') { + let icon = this._defaultIcon(key); + item.add_child(icon); + } + + // don't add a label when no sensors are in the panel + if (key == '_default_icon_') return; + + let label = new St.Label({ + style_class: 'vitals-panel-label', + text: (value)?value:'\u2026', // ... + y_expand: true, + y_align: Clutter.ActorAlign.CENTER + }); + // attempt to prevent ellipsizes + label.get_clutter_text().ellipsize = 0; + // keep track of label for removal later + this._hotLabels[key] = label; + // prevent "called on the widget" "which is not in the stage" errors by adding before width below + item.add_child(label); + + // support for fixed widths #55, save label (text) width + this._widths[key] = label.get_clutter_text().width; + } + + _showHideSensorsChanged(self, sensor) { + this._sensors.resetHistory(); + + const sensorName = sensor.substr(5); + if(sensorName === 'gpu') { + for(let i = 1; i <= this._numGpus; i++) + this._groups[sensorName + '#' + i].visible = this._settings.get_boolean(sensor); + } else + this._groups[sensorName].visible = this._settings.get_boolean(sensor); + } + + _positionInPanelChanged() { + this.container.get_parent().remove_child(this.container); + let position = this._positionInPanel(); + + // allows easily addressable boxes + let boxes = { + left: Main.panel._leftBox, + center: Main.panel._centerBox, + right: Main.panel._rightBox + }; + + // update position when changed from preferences + boxes[position[0]].insert_child_at_index(this.container, position[1]); + } + + _redrawDetailsMenuIcons() { + // updates the icons on the 'details' menu, the one + // you have to click to appear + this._sensors.resetHistory(); + for (const sensor in this._sensorIcons) { + if (sensor == "gpu") continue; + this._groups[sensor].icon.gicon = Gio.icon_new_for_string(this._sensorIconPath(sensor)); + } + + // gpu's are indexed differently, handle them here + const gpuKeys = Object.keys(this._groups).filter(key => key.startsWith("gpu#")); + gpuKeys.forEach((gpuKey) => { + this._groups[gpuKey].icon.gicon = Gio.icon_new_for_string(this._sensorIconPath("gpu")); + }); + } + + _iconStyleChanged() { + this._redrawDetailsMenuIcons(); + this._redrawMenu(); + } + + _removeHotItems(){ + for (let key in this._hotItems) { + this._removeHotItem(key); + } + } + + _removeHotItem(key) { + if (key in this._hotItems) { + this._hotItems[key].destroy(); + delete this._hotItems[key]; + delete this._hotLabels[key]; + delete this._widths[key]; + } + } + + _redrawMenu() { + this._removeHotItems(); + + for (let key in this._sensorMenuItems) { + if (key.includes('-group')) continue; + this._sensorMenuItems[key].destroy(); + delete this._sensorMenuItems[key]; + } + + this._drawMenu(); + this._sensors.resetHistory(); + this._values.resetHistory(this._numGpus); + this._querySensors(); + } + + _drawMenu() { + // grab list of selected menubar icons + let hotSensors = this._settings.get_strv('hot-sensors'); + for (let key of Object.values(hotSensors)) { + // fixes issue #225 which started when _max_ was moved to the end + if (key == '__max_network-download__') key = '__network-rx_max__'; + if (key == '__max_network-upload__') key = '__network-tx_max__'; + + this._createHotItem(key); + } + } + + _destroyTimer() { + // invalidate and reinitialize timer + if (this._refreshTimeoutId != null) { + GLib.Source.remove(this._refreshTimeoutId); + this._refreshTimeoutId = null; + } + } + + _updateTimeSettingChanged() { + this._destroyTimer(); + this._initializeTimer(); + } + + _updateTimeChanged() { + this._destroyTimer(); + this._initializeTimer(); + } + + _addSettingChangedSignal(key, callback) { + this._settingChangedSignals.push(this._settings.connect('changed::' + key, callback)); + } + + _updateDisplay(label, value, type, key) { + // update sensor value in menubar + if (this._hotLabels[key]) { + this._hotLabels[key].set_text(value); + + // support for fixed widths #55 + if (this._settings.get_boolean('fixed-widths')) { + // grab text box width and see if new text is wider than old text + let width2 = this._hotLabels[key].get_clutter_text().width; + if (width2 > this._widths[key]) { + this._hotLabels[key].set_width(width2); + this._widths[key] = width2; + } + } + } + + // have we added this sensor before? + let item = this._sensorMenuItems[key]; + if (item) { + // update sensor value in the group + item.value = value; + } else if (type.includes('-group')) { + // update text next to group header + let group = type.split('-')[0]; + if (this._groups[group]) { + this._groups[group].status.text = value; + this._sensorMenuItems[type] = this._groups[group]; + } + } else { + // add item to group for the first time + let sensor = { 'label': label, 'value': value, 'type': type } + this._appendMenuItem(sensor, key); + } + } + + _appendMenuItem(sensor, key) { + let split = sensor.type.split('-'); + let type = split[0]; + let icon = (split.length == 2)?'icon-' + split[1]:'icon'; + let gicon = Gio.icon_new_for_string(this._sensorIconPath(type, icon)); + + let item = new MenuItem.MenuItem(gicon, key, sensor.label, sensor.value, this._hotLabels[key]); + item.connect('toggle', (self) => { + let hotSensors = this._settings.get_strv('hot-sensors'); + + if (self.checked) { + // add selected sensor to panel + hotSensors.push(self.key); + this._createHotItem(self.key, self.value); + } else { + // remove selected sensor from panel + hotSensors.splice(hotSensors.indexOf(self.key), 1); + this._removeHotItem(self.key); + } + + if (hotSensors.length <= 0) { + // add generic icon to panel when no sensors are selected + hotSensors.push('_default_icon_'); + this._createHotItem('_default_icon_'); + } else { + let defIconPos = hotSensors.indexOf('_default_icon_'); + if (defIconPos >= 0) { + // remove generic icon from panel when sensors are selected + hotSensors.splice(defIconPos, 1); + this._removeHotItem('_default_icon_'); + } + } + + // this code is called asynchronously - make sure to save it for next round + this._saveHotSensors(hotSensors); + }); + + this._sensorMenuItems[key] = item; + let i = Object.keys(this._sensorMenuItems[key]).length; + + // alphabetize the sensors for these categories + if (this._settings.get_boolean('alphabetize')) { + let menuItems = this._groups[type].menu._getMenuItems(); + for (i = 0; i < menuItems.length; i++) + // use natural sort order for system load, etc + if (menuItems[i].label.localeCompare(item.label, undefined, { numeric: true, sensitivity: 'base' }) > 0) + break; + } + + this._groups[type].menu.addMenuItem(item, i); + } + + _defaultLabel() { + return new St.Label({ + y_expand: true, + y_align: Clutter.ActorAlign.CENTER + }); + } + + _defaultIcon(key) { + let split = key.replaceAll('_', ' ').trim().split(' ')[0].split('-'); + let type = split[0]; + + let icon = new St.Icon({ + style_class: 'system-status-icon vitals-panel-icon-' + type, + reactive: true + }); + + // second condition prevents crash due to issue #225, which started when _max_ was moved to the end + // don't use the default system icon if the type is a gpu; use the universal gpu icon instead + if (type == 'default' || (!(type in this._sensorIcons) && !type.startsWith('gpu'))) { + icon.gicon = Gio.icon_new_for_string(this._sensorIconPath('system')); + } else { // support for hide icons #80 + let iconObj = (split.length == 2)?'icon-' + split[1]:'icon'; + icon.gicon = Gio.icon_new_for_string(this._sensorIconPath(type, iconObj)); + } + + return icon; + } + + _sensorIconPath(sensor, icon = 'icon') { + // If the sensor is a numbered gpu, use the gpu icon. Otherwise use whatever icon associated with the sensor name. + let sensorKey = sensor; + if(sensor.startsWith('gpu')) sensorKey = 'gpu'; + + const iconPathPrefixIndex = this._settings.get_int('icon-style'); + return this._extensionObject.path + this._sensorsIconPathPrefix[iconPathPrefixIndex] + this._sensorIcons[sensorKey][icon]; + } + + _ucFirst(string) { + if(string.startsWith('gpu')) return 'Graphics'; + return string.charAt(0).toUpperCase() + string.slice(1); + } + + _positionInPanel() { + let alignment = ''; + let gravity = 0; + let arrow_pos = 0; + + switch (this._settings.get_int('position-in-panel')) { + case 0: // left + alignment = 'left'; + gravity = -1; + arrow_pos = 1; + break; + case 1: // center + alignment = 'center'; + gravity = -1; + arrow_pos = 0.5; + break; + case 2: // right + alignment = 'right'; + gravity = 0; + arrow_pos = 0; + break; + case 3: // far left + alignment = 'left'; + gravity = 0; + arrow_pos = 1; + break; + case 4: // far right + alignment = 'right'; + gravity = -1; + arrow_pos = 0; + break; + } + + let centered = this._settings.get_boolean('menu-centered') + if (centered) arrow_pos = 0.5; + + // set arrow position when initializing and moving vitals + this.menu._arrowAlignment = arrow_pos; + + return [alignment, gravity]; + } + + _querySensors() { + // figure out last run time + let now = new Date().getTime(); + let dwell = (now - this._last_query) / 1000; + this._last_query = now; + + this._sensors.query((label, value, type, format) => { + const typeKey = type.replace('-group', ''); + let key = '_' + typeKey + '_' + label.replace(' ', '_').toLowerCase() + '_'; + + // if a sensor is disabled, gray it out + if (key in this._sensorMenuItems) { + this._sensorMenuItems[key].setSensitive((value!='disabled')); + + // don't continue below, last known value is shown + if (value == 'disabled') return; + } + + // add/initialize any gpu groups that we haven't added yet + if(typeKey.startsWith('gpu') && typeKey !== 'gpu#1') { + const split = typeKey.split('#'); + if(split.length == 2 && this._numGpus < parseInt(split[1])) { + // occasionally two lines from nvidia-smi will be read at once + // so we only actually update the number of gpus if we have recieved multiple lines at least 3 times in a row + // i.e. we make sure that mutiple queries have detected a new gpu back-to-back + if(this._newGpuDetectedCount < 2) { + this._newGpuDetected = true; + return; + } + + this._numGpus = parseInt(split[1]); + this._newGpuDetectedCount = 0; + this._newGpuDetected = false; + // change label for gpu 1 from "Graphics" to "Graphics 1" since we have multiple gpus now + this._groups['gpu#1'].label.text = this._ucFirst('gpu#1') + ' 1'; + for(let i = 2; i <= this._numGpus; i++) + if(!('gpu#' + i in this._groups)) + this._initializeMenuGroup('gpu#' + i, 'gpu', ' ' + i, Object.keys(this._groups).length); + } + } + + let items = this._values.returnIfDifferent(dwell, label, value, type, format, key); + for (let item of Object.values(items)) + this._updateDisplay(_(item[0]), item[1], item[2], item[3]); + }, dwell); + + //if a new gpu has been detected during the last query, then increment the amount of times we've detected a new gpu + if(this._newGpuDetected) this._newGpuDetectedCount++; + else this._newGpuDetectedCount = 0; + this._newGpuDetected = false; + + if (this._warnings.length > 0) { + this._notify('Vitals', this._warnings.join("\n"), 'folder-symbolic'); + this._warnings = []; + } + } + + _notify(msg, details, icon) { + let source = new MessageTray.Source('MyApp Information', icon); + Main.messageTray.add(source); + let notification = new MessageTray.Notification(source, msg, details); + notification.setTransient(true); + source.notify(notification); + } + + destroy() { + this._destroyTimer(); + this._sensors.destroy(); + + for (let signal of Object.values(this._settingChangedSignals)) + this._settings.disconnect(signal); + + super.destroy(); + } +}); + +export default class VitalsExtension extends Extension { + enable() { + vitalsMenu = new VitalsMenuButton(this); + let position = vitalsMenu._positionInPanel(); + Main.panel.addToStatusArea('vitalsMenu', vitalsMenu, position[1], position[0]); + } + + disable() { + vitalsMenu.destroy(); + vitalsMenu = null; + } +} diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/helpers/file.js b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/helpers/file.js new file mode 100644 index 0000000..a7211b9 --- /dev/null +++ b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/helpers/file.js @@ -0,0 +1,82 @@ +import Gio from 'gi://Gio'; +import GLib from 'gi://GLib' + +// convert Uint8Array into a literal string +function convertUint8ArrayToString(contents) { + const decoder = new TextDecoder('utf-8'); + return decoder.decode(contents).trim(); +} + +export function File(path) { + if (path.indexOf('https://') == -1) + this.file = Gio.File.new_for_path(path); + else + this.file = Gio.File.new_for_uri(path); +} + +File.prototype.read = function(delimiter = '', strip_header = false) { + return new Promise((resolve, reject) => { + try { + this.file.load_contents_async(null, function(file, res) { + try { + // grab contents of file or website + let contents = file.load_contents_finish(res)[1]; + + // convert contents to string + contents = convertUint8ArrayToString(contents); + + // split contents by delimiter if passed in + if (delimiter) contents = contents.split(delimiter); + + // optionally strip header when converting to a list + if (strip_header) contents.shift(); + + // return results + resolve(contents); + } catch (e) { + reject(e.message); + } + }); + } catch (e) { + reject(e.message); + } + }); +}; + +File.prototype.list = function() { + return new Promise((resolve, reject) => { + let max_items = 125, results = []; + + try { + this.file.enumerate_children_async(Gio.FILE_ATTRIBUTE_STANDARD_NAME, Gio.FileQueryInfoFlags.NONE, GLib.PRIORITY_LOW, null, function(file, res) { + try { + let enumerator = file.enumerate_children_finish(res); + + let callback = function(enumerator, res) { + try { + let files = enumerator.next_files_finish(res); + for (let i = 0; i < files.length; i++) { + results.push(files[i].get_attribute_as_string(Gio.FILE_ATTRIBUTE_STANDARD_NAME)); + } + + if (files.length == 0) { + enumerator.close_async(GLib.PRIORITY_LOW, null, function(){}); + resolve(results); + } else { + enumerator.next_files_async(max_items, GLib.PRIORITY_LOW, null, callback); + } + } catch (e) { + reject(e.message); + } + }; + + enumerator.next_files_async(max_items, GLib.PRIORITY_LOW, null, callback); + } catch (e) { + reject(e.message); + } + }); + } catch (e) { + reject(e.message); + } + }); +}; diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/helpers/subprocess.js b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/helpers/subprocess.js new file mode 100644 index 0000000..df0bf49 --- /dev/null +++ b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/helpers/subprocess.js @@ -0,0 +1,54 @@ +import GLib from 'gi://GLib'; +import Gio from 'gi://Gio'; + +// convert Uint8Array into a literal string +function convertUint8ArrayToString(contents) { + const decoder = new TextDecoder('utf-8'); + return decoder.decode(contents).trim(); +} + +export function SubProcess(command) { + this.sub_process = Gio.Subprocess.new(command, Gio.SubprocessFlags.STDOUT_PIPE); + this.stdout = this.sub_process.get_stdout_pipe(); +} + +SubProcess.prototype.read = function(delimiter = '') { + return new Promise((resolve, reject) => { + this.stdout.read_bytes_async(512, GLib.PRIORITY_LOW, null, function(stdout, res) { + try { + let read_bytes = stdout.read_bytes_finish(res).get_data(); + + // convert contents to string + let read_str = convertUint8ArrayToString(read_bytes); + + // split read_str by delimiter if passed in + if (delimiter) { + if (read_str == '') + read_str = []; // EOF, ''.split(delimiter) would return [''] + else + read_str = read_str.split(delimiter); + } + + // return results + resolve(read_str); + } catch (e) { + if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.PENDING)) { + // previous read attempt is still waiting for something from stdout + // ignore second attempt, return empty data (like EOF) + if (delimiter) resolve([]); + else resolve(''); + } else { + reject(e.message); + } + } + }); + }); +}; + +SubProcess.prototype.terminate = function() { + const SIGINT = 2; + this.sub_process.send_signal(SIGINT); + this.sub_process = null; + this.stdout.close_async(GLib.PRIORITY_LOW, null, null); + this.stdout = null; +}; diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/battery-symbolic.svg b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/battery-symbolic.svg new file mode 100644 index 0000000..28a9887 --- /dev/null +++ b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/battery-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/cpu-symbolic.svg b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/cpu-symbolic.svg new file mode 100644 index 0000000..31c397b --- /dev/null +++ b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/cpu-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/fan-symbolic.svg b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/fan-symbolic.svg new file mode 100644 index 0000000..234f797 --- /dev/null +++ b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/fan-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/gpu-symbolic.svg b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/gpu-symbolic.svg new file mode 100644 index 0000000..b59beda --- /dev/null +++ b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/gpu-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/memory-symbolic.svg b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/memory-symbolic.svg new file mode 100644 index 0000000..7fb2200 --- /dev/null +++ b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/memory-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/network-download-symbolic.svg b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/network-download-symbolic.svg new file mode 100644 index 0000000..c95cc12 --- /dev/null +++ b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/network-download-symbolic.svg @@ -0,0 +1,2 @@ + + diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/network-symbolic.svg b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/network-symbolic.svg new file mode 100644 index 0000000..ac7a6ee --- /dev/null +++ b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/network-symbolic.svg @@ -0,0 +1,2 @@ + + diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/network-upload-symbolic.svg b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/network-upload-symbolic.svg new file mode 100644 index 0000000..9ca1f68 --- /dev/null +++ b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/network-upload-symbolic.svg @@ -0,0 +1,2 @@ + + diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/storage-symbolic.svg b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/storage-symbolic.svg new file mode 100644 index 0000000..b0d4753 --- /dev/null +++ b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/storage-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/system-symbolic.svg b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/system-symbolic.svg new file mode 100644 index 0000000..a041518 --- /dev/null +++ b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/system-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/temperature-symbolic.svg b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/temperature-symbolic.svg new file mode 100644 index 0000000..ed976bf --- /dev/null +++ b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/temperature-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/voltage-symbolic.svg b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/voltage-symbolic.svg new file mode 100644 index 0000000..c786442 --- /dev/null +++ b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/voltage-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/battery-symbolic.svg b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/battery-symbolic.svg new file mode 100644 index 0000000..70df9ee --- /dev/null +++ b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/battery-symbolic.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/cpu-symbolic.svg b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/cpu-symbolic.svg new file mode 100644 index 0000000..19969e4 --- /dev/null +++ b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/cpu-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/fan-symbolic.svg b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/fan-symbolic.svg new file mode 100644 index 0000000..a584b74 --- /dev/null +++ b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/fan-symbolic.svg @@ -0,0 +1,133 @@ + + + + + + + + image/svg+xml + + Gnome Symbolic Icon Theme + + + + + + + Gnome Symbolic Icon Theme + + + + + + + + + + + + diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/gpu-symbolic.svg b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/gpu-symbolic.svg new file mode 100644 index 0000000..d5cf157 --- /dev/null +++ b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/gpu-symbolic.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/memory-symbolic.svg b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/memory-symbolic.svg new file mode 100644 index 0000000..13ca4bc --- /dev/null +++ b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/memory-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/network-download-symbolic.svg b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/network-download-symbolic.svg new file mode 100644 index 0000000..5cab840 --- /dev/null +++ b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/network-download-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/network-symbolic.svg b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/network-symbolic.svg new file mode 100644 index 0000000..0483417 --- /dev/null +++ b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/network-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/network-upload-symbolic.svg b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/network-upload-symbolic.svg new file mode 100644 index 0000000..e2c5a08 --- /dev/null +++ b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/network-upload-symbolic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/storage-symbolic.svg b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/storage-symbolic.svg new file mode 100644 index 0000000..9832999 --- /dev/null +++ b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/storage-symbolic.svg @@ -0,0 +1,3 @@ + + + diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/system-symbolic.svg b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/system-symbolic.svg new file mode 100644 index 0000000..909f530 --- /dev/null +++ b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/system-symbolic.svg @@ -0,0 +1,3 @@ + + + diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/temperature-symbolic.svg b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/temperature-symbolic.svg new file mode 100644 index 0000000..c58beca --- /dev/null +++ b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/temperature-symbolic.svg @@ -0,0 +1,45 @@ + +image/svg+xml \ No newline at end of file diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/voltage-symbolic.svg b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/voltage-symbolic.svg new file mode 100644 index 0000000..b2e7eff --- /dev/null +++ b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/voltage-symbolic.svg @@ -0,0 +1,125 @@ + + + + + + + + image/svg+xml + + Gnome Symbolic Icon Theme + + + + + + + Gnome Symbolic Icon Theme + + + + + + + + + + + diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/ar/LC_MESSAGES/vitals.mo b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/ar/LC_MESSAGES/vitals.mo new file mode 100644 index 0000000..5d1d5ac Binary files /dev/null and b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/ar/LC_MESSAGES/vitals.mo differ diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/be/LC_MESSAGES/vitals.mo b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/be/LC_MESSAGES/vitals.mo new file mode 100644 index 0000000..1001254 Binary files /dev/null and b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/be/LC_MESSAGES/vitals.mo differ diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/ca/LC_MESSAGES/vitals.mo b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/ca/LC_MESSAGES/vitals.mo new file mode 100644 index 0000000..5a18109 Binary files /dev/null and b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/ca/LC_MESSAGES/vitals.mo differ diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/cs/LC_MESSAGES/vitals.mo b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/cs/LC_MESSAGES/vitals.mo new file mode 100644 index 0000000..689742c Binary files /dev/null and b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/cs/LC_MESSAGES/vitals.mo differ diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/de/LC_MESSAGES/vitals.mo b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/de/LC_MESSAGES/vitals.mo new file mode 100644 index 0000000..f374b84 Binary files /dev/null and b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/de/LC_MESSAGES/vitals.mo differ diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/es/LC_MESSAGES/vitals.mo b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/es/LC_MESSAGES/vitals.mo new file mode 100644 index 0000000..fbdb72a Binary files /dev/null and b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/es/LC_MESSAGES/vitals.mo differ diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/fi_FI/LC_MESSAGES/vitals.mo b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/fi_FI/LC_MESSAGES/vitals.mo new file mode 100644 index 0000000..38454e3 Binary files /dev/null and b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/fi_FI/LC_MESSAGES/vitals.mo differ diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/fr/LC_MESSAGES/vitals.mo b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/fr/LC_MESSAGES/vitals.mo new file mode 100644 index 0000000..1260f3d Binary files /dev/null and b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/fr/LC_MESSAGES/vitals.mo differ diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/it/LC_MESSAGES/vitals.mo b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/it/LC_MESSAGES/vitals.mo new file mode 100644 index 0000000..f37507d Binary files /dev/null and b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/it/LC_MESSAGES/vitals.mo differ diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/my_MM/LC_MESSAGES/vitals.mo b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/my_MM/LC_MESSAGES/vitals.mo new file mode 100644 index 0000000..e194797 Binary files /dev/null and b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/my_MM/LC_MESSAGES/vitals.mo differ diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/nl/LC_MESSAGES/vitals.mo b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/nl/LC_MESSAGES/vitals.mo new file mode 100644 index 0000000..2a90349 Binary files /dev/null and b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/nl/LC_MESSAGES/vitals.mo differ diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/oc/LC_MESSAGES/vitals.mo b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/oc/LC_MESSAGES/vitals.mo new file mode 100644 index 0000000..ef097f7 Binary files /dev/null and b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/oc/LC_MESSAGES/vitals.mo differ diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/pl/LC_MESSAGES/vitals.mo b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/pl/LC_MESSAGES/vitals.mo new file mode 100644 index 0000000..b42daa4 Binary files /dev/null and b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/pl/LC_MESSAGES/vitals.mo differ diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/pt/LC_MESSAGES/vitals.mo b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/pt/LC_MESSAGES/vitals.mo new file mode 100644 index 0000000..ebf4c11 Binary files /dev/null and b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/pt/LC_MESSAGES/vitals.mo differ diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/pt_BR/LC_MESSAGES/vitals.mo b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/pt_BR/LC_MESSAGES/vitals.mo new file mode 100644 index 0000000..986cf6d Binary files /dev/null and b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/pt_BR/LC_MESSAGES/vitals.mo differ diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/ru/LC_MESSAGES/vitals.mo b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/ru/LC_MESSAGES/vitals.mo new file mode 100644 index 0000000..1f58435 Binary files /dev/null and b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/ru/LC_MESSAGES/vitals.mo differ diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/sk/LC_MESSAGES/vitals.mo b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/sk/LC_MESSAGES/vitals.mo new file mode 100644 index 0000000..73a62ea Binary files /dev/null and b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/sk/LC_MESSAGES/vitals.mo differ diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/tr/LC_MESSAGES/vitals.mo b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/tr/LC_MESSAGES/vitals.mo new file mode 100644 index 0000000..eee9edd Binary files /dev/null and b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/tr/LC_MESSAGES/vitals.mo differ diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/uk/LC_MESSAGES/vitals.mo b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/uk/LC_MESSAGES/vitals.mo new file mode 100644 index 0000000..9f11b14 Binary files /dev/null and b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/uk/LC_MESSAGES/vitals.mo differ diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/zh_CN/LC_MESSAGES/vitals.mo b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/zh_CN/LC_MESSAGES/vitals.mo new file mode 100644 index 0000000..3618dc3 Binary files /dev/null and b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/zh_CN/LC_MESSAGES/vitals.mo differ diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/menuItem.js b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/menuItem.js new file mode 100644 index 0000000..cf112ab --- /dev/null +++ b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/menuItem.js @@ -0,0 +1,77 @@ +import Clutter from 'gi://Clutter'; +import GObject from 'gi://GObject'; +import St from 'gi://St' +import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js'; + +export const MenuItem = GObject.registerClass({ + + Signals: { + 'toggle': { param_types: [Clutter.Event.$gtype] }, + }, + +}, class MenuItem extends PopupMenu.PopupBaseMenuItem { + + _init(icon, key, label, value, checked) { + super._init({ reactive: true }); + + this._checked = checked; + this._updateOrnament(); + + this._key = key; + this._gIcon = icon; + + // add icon + this.add_child(new St.Icon({ style_class: 'popup-menu-icon', gicon : this._gIcon })); + + // add label + this._labelActor = new St.Label({ text: label }); + this.add_child(this._labelActor); + + // add value + this._valueLabel = new St.Label({ text: value }); + this._valueLabel.set_x_align(Clutter.ActorAlign.END); + this._valueLabel.set_x_expand(true); + this._valueLabel.set_y_expand(true); + this.add_child(this._valueLabel); + + this.actor._delegate = this; + } + + get checked() { + return this._checked; + } + + get key() { + return this._key; + } + + get gicon() { + return this._gIcon; + } + + set value(value) { + this._valueLabel.text = value; + } + + get value() { + return this._valueLabel.text; + } + + // prevents menu from being closed + activate(event) { + this._checked = !this._checked; + this._updateOrnament(); + this.emit('toggle', event); + } + + _updateOrnament() { + if (this._checked) + this.setOrnament(PopupMenu.Ornament.CHECK); + else + this.setOrnament(PopupMenu.Ornament.NONE); + } + + get label() { + return this._labelActor.text; + } +}); diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/metadata.json b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/metadata.json new file mode 100644 index 0000000..bb2e767 --- /dev/null +++ b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/metadata.json @@ -0,0 +1,21 @@ +{ + "_generated": "Generated by SweetTooth, do not edit", + "description": "A glimpse into your computer's temperature, voltage, fan speed, memory usage, processor load, system resources, network speed and storage stats. This is a one stop shop to monitor all of your vital sensors. Uses asynchronous polling to provide a smooth user experience. Feature requests or bugs? Please use GitHub.", + "donations": { + "paypal": "corecoding" + }, + "gettext-domain": "vitals", + "name": "Vitals", + "settings-schema": "org.gnome.shell.extensions.vitals", + "shell-version": [ + "45", + "46", + "47", + "48", + "49", + "50" + ], + "url": "https://github.com/corecoding/Vitals", + "uuid": "Vitals@CoreCoding.com", + "version": 75 +} \ No newline at end of file diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/prefs.js b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/prefs.js new file mode 100644 index 0000000..07916bd --- /dev/null +++ b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/prefs.js @@ -0,0 +1,138 @@ +import Adw from 'gi://Adw'; +import Gio from 'gi://Gio'; +import GObject from 'gi://GObject'; +import Gtk from 'gi://Gtk'; +import {ExtensionPreferences, gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js'; + +/* + if (sensor == 'show-storage' && this._settings.get_boolean(sensor)) { + + let val = true; + + try { + let GTop = imports.gi.GTop; + } catch (e) { + val = false; + } + + let now = new Date().getTime(); + this._notify("Vitals", "Please run sudo apt install gir1.2-gtop-2.0", 'folder-symbolic'); + + } +*/ + +const Settings = new GObject.Class({ + Name: 'Vitals.Settings', + + _init: function(extensionObject, params) { + this._extensionObject = extensionObject + this.parent(params); + + this._settings = extensionObject.getSettings(); + + this.builder = new Gtk.Builder(); + this.builder.set_translation_domain(this._extensionObject.metadata['gettext-domain']); + this.builder.add_from_file(this._extensionObject.path + '/prefs.ui'); + this.widget = this.builder.get_object('prefs-container'); + + this._bind_settings(); + }, + + // Bind the gtk window to the schema settings + _bind_settings: function() { + let widget; + + // process sensor toggles + let sensors = [ 'show-temperature', 'show-voltage', 'show-fan', + 'show-memory', 'show-processor', 'show-system', + 'show-network', 'show-storage', 'use-higher-precision', + 'alphabetize', 'hide-zeros', 'include-public-ip', + 'show-battery', 'fixed-widths', 'hide-icons', + 'menu-centered', 'include-static-info', + 'show-gpu', 'include-static-gpu-info' ]; + + for (let key in sensors) { + let sensor = sensors[key]; + + widget = this.builder.get_object(sensor); + widget.set_active(this._settings.get_boolean(sensor)); + widget.connect('state-set', (_, val) => { + this._settings.set_boolean(sensor, val); + }); + } + + // process individual drop down sensor preferences + sensors = [ 'position-in-panel', 'unit', 'network-speed-format', 'memory-measurement', 'storage-measurement', 'battery-slot', 'icon-style' ]; + for (let key in sensors) { + let sensor = sensors[key]; + + widget = this.builder.get_object(sensor); + widget.set_active(this._settings.get_int(sensor)); + widget.connect('changed', (widget) => { + this._settings.set_int(sensor, widget.get_active()); + }); + } + + this._settings.bind('update-time', this.builder.get_object('update-time'), 'value', Gio.SettingsBindFlags.DEFAULT); + + // process individual text entry sensor preferences + sensors = [ 'storage-path', 'monitor-cmd' ]; + for (let key in sensors) { + let sensor = sensors[key]; + + widget = this.builder.get_object(sensor); + widget.set_text(this._settings.get_string(sensor)); + + widget.connect('changed', (widget) => { + let text = widget.get_text(); + if (!text) text = widget.get_placeholder_text(); + this._settings.set_string(sensor, text); + }); + } + + // makes individual sensor preference boxes appear + sensors = [ 'temperature', 'network', 'storage', 'memory', 'battery', 'system', 'processor', 'gpu' ]; + for (let key in sensors) { + let sensor = sensors[key]; + + // create dialog for intelligent autohide advanced settings + this.builder.get_object(sensor + '-prefs').connect('clicked', () => { + let transientObj = this.widget.get_root(); + let title = sensor.charAt(0).toUpperCase() + sensor.slice(1); + let dialog = new Gtk.Dialog({ title: _(title) + ' ' + _('Preferences'), + transient_for: transientObj, + use_header_bar: false, + modal: true }); + + let box = this.builder.get_object(sensor + '_prefs'); + dialog.get_content_area().append(box); + dialog.connect('response', (dialog, id) => { + // remove the settings box so it doesn't get destroyed; + dialog.get_content_area().remove(box); + dialog.destroy(); + return; + }); + + dialog.show(); + }); + } + } +}); + + +export default class VitalsPrefs extends ExtensionPreferences { + fillPreferencesWindow(window) { + window._settings = this.getSettings(); + + let settings = new Settings(this); + let widget = settings.widget; + + const page = new Adw.PreferencesPage(); + const group = new Adw.PreferencesGroup({}); + group.add(widget); + page.add(group); + window.add(page); + window.set_default_size(widget.width, widget.height); + widget.show(); + } +} diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/prefs.ui b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/prefs.ui new file mode 100644 index 0000000..950c1b0 --- /dev/null +++ b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/prefs.ui @@ -0,0 +1,1394 @@ + + + + + 1 + 60 + 1 + 10 + + + 0 + 12 + 12 + 6 + 6 + vertical + + + 0 + + + 0 + none + + + 100 + 1 + 0 + + + 0 + 6 + 6 + 6 + 6 + 12 + + + 1 + 0 + start + Include public IP address + + + + + end + 5 + + + + + + + + + 100 + 0 + + + 0 + 6 + 6 + 6 + 6 + + + 1 + 0 + start + Format + + + + + 0 + 0 + + Bytes + Bits + + + + + + + + + + + + + + 0 + 0 + 6 + 6 + 6 + 6 + vertical + 2 + + + 0 + 1 + 1 + 1 + + + 0 + 12 + 9 + 12 + 12 + vertical + 12 + + + 0 + start + baseline + General + + + + + + + + 0 + + + 0 + none + + + 100 + 0 + + + 0 + 6 + 6 + + + 1 + 0 + start + 5 + 5 + Seconds between updates + + + + + 5 + 5 + 0 + 2 + update-time-params + 1 + 1 + 1 + if-valid + + + + + + + + + 100 + 0 + 0 + + + 0 + 6 + 6 + + + 1 + 0 + start + 5 + 5 + Position in panel + + + + + 0 + 5 + 0 + + Left + Center + Right + Far Left + Far Right + + + + + + + + + + 100 + 0 + + + 0 + 6 + 6 + + + 1 + 0 + start + 5 + 5 + Use higher precision + + + + + end + 5 + + + + + + + + + 100 + 0 + + + 0 + 6 + 6 + + + 1 + 0 + start + 5 + 5 + Alphabetize sensors + + + + + end + 5 + + + + + + + + + 100 + 0 + + + 0 + 6 + 6 + + + 1 + 0 + start + 5 + 5 + Hide zero values + + + + + end + 5 + + + + + + + + + 100 + 0 + + + 0 + 6 + 6 + + + 1 + 0 + start + 5 + 5 + Use fixed widths + + + + + end + 5 + + + + + + + + + 100 + 0 + + + 0 + 6 + 6 + + + 1 + 0 + start + 5 + 5 + Hide icons in top bar + + + + + end + 5 + + + + + + + + + 100 + 0 + + + 0 + 6 + 6 + + + 1 + 0 + start + 5 + 5 + Menu always centered + + + + + end + 5 + + + + + + + + + 100 + 0 + 0 + + + 0 + 6 + 6 + + + 1 + 0 + start + 5 + 5 + Icon style + + + + + 0 + 5 + 0 + + Original + GNOME + + + + + + + + + + + + + + + + + + + 0 + 6 + 9 + 12 + 12 + vertical + 12 + + + 0 + start + baseline + Sensors + + + + + + + + 0 + + + 0 + + + 100 + 0 + + + 0 + center + 6 + 6 + + + 1 + 0 + start + 5 + 5 + Monitor temperature + + + + + 0 + 6 + + + 1 + center + center + + + 0 + emblem-system-symbolic + + + + + + + + end + center + 5 + + + + + + + + + + + 100 + 0 + + + 0 + center + 6 + 6 + + + 1 + 0 + start + 5 + 5 + Monitor voltage + + + + + end + 5 + + + + + + + + + 100 + 0 + + + 0 + center + 6 + 6 + + + 1 + 0 + start + 5 + 5 + Monitor fan + + + + + end + 5 + + + + + + + + + 100 + 0 + + + 0 + center + 6 + 6 + + + 1 + 0 + start + 5 + 5 + Monitor memory + + + + + 0 + 6 + + + 1 + center + center + + + 0 + emblem-system-symbolic + + + + + + + + end + center + 5 + + + + + + + + + + + 100 + 0 + + + 0 + center + 6 + 6 + + + 1 + 0 + start + 5 + 5 + Monitor processor + + + + + 0 + 6 + + + 1 + center + center + + + 0 + emblem-system-symbolic + + + + + + + + end + center + 5 + + + + + + + + + + + 100 + 0 + + + 0 + center + 6 + 6 + + + 1 + 0 + start + 5 + 5 + Monitor system + + + + + 0 + 6 + + + 1 + center + center + + + 0 + emblem-system-symbolic + + + + + + + + end + center + 5 + + + + + + + + + + + 100 + 0 + + + 0 + center + 6 + 6 + + + 1 + 0 + start + 5 + 5 + Monitor network + + + + + 0 + 6 + + + 1 + center + center + + + 0 + emblem-system-symbolic + + + + + + + + end + center + 5 + + + + + + + + + + + 100 + 0 + + + 0 + center + 6 + 6 + + + 1 + 0 + start + 5 + 5 + Monitor storage + + + + + 0 + 6 + + + 1 + center + center + + + 0 + emblem-system-symbolic + + + + + + + + end + center + 5 + + + + + + + + + + + 100 + 0 + + + 0 + center + 6 + 6 + + + 1 + 0 + start + 5 + 5 + Monitor battery + + + + + 0 + 6 + + + 1 + center + center + + + 0 + emblem-system-symbolic + + + + + + + + end + center + 5 + + + + + + + + + + + 100 + 0 + + + 0 + center + 6 + 6 + + + 1 + 0 + start + 5 + 5 + Monitor GPU (beta) + + + + + 0 + 6 + + + 1 + center + center + + + 0 + emblem-system-symbolic + + + + + + + + end + center + 5 + + + + + + + + + + + + + + + + + + + + + + start + 0 + 12 + 9 + 12 + About + + + + + + + + 0 + 6 + 6 + + + 0 + start + baseline + 5 + 5 + 5 + 5 + Feature requests or bugs? Please visit <a href="https://github.com/corecoding/Vitals/issues">GitHub</a>. No warranty, expressed or implied. <a href="https://corecoding.com/donate.php">Donate</a> if you found this useful. + 1 + 1 + 0 + + + + + + + + + + 1 + 12 + 12 + 6 + 6 + vertical + + + 1 + + + 1 + none + + + 100 + 1 + 0 + + + 1 + 6 + 6 + 6 + 6 + 12 + + + 1 + 1 + start + Path + + + + + / + 1 + 1 + 24 + + + + + + + + + 100 + 0 + + + 0 + center + 6 + 6 + 6 + 6 + + + 1 + 0 + start + Measurement + + + + + 0 + 0 + + Binary + Decimal + + + + + + + + + + + + + + 0 + 12 + 12 + 6 + 6 + vertical + + + 0 + + + 0 + none + + + 100 + 1 + 0 + + + 0 + 6 + 6 + 6 + 6 + 12 + + + 1 + 0 + start + Unit + + + + + 0 + 0 + end + + °C + °F + + + + + + + + + + + + + + 0 + 12 + 12 + 6 + 6 + vertical + + + 0 + + + 0 + none + + + 100 + 1 + 0 + + + 0 + 6 + 6 + 6 + 6 + 12 + + + 1 + 0 + start + Measurement + + + + + 0 + 0 + + Binary + Decimal + + + + + + + + + + + + + + 0 + 12 + 12 + 6 + 6 + vertical + + + 0 + + + 0 + none + + + 100 + 1 + 0 + + + 0 + 6 + 6 + 6 + 6 + 12 + + + 1 + 0 + start + Display Battery + + + + + 0 + 0 + + BAT0 + BAT1 + BAT2 + BATT + CMB0 + CMB1 + CMB2 + macsmc-battery + + + + + + + + + + + + + + 1 + 12 + 12 + 6 + 6 + vertical + + + 1 + + + 1 + none + + + 100 + 1 + 0 + + + 1 + 6 + 6 + 6 + 6 + 12 + + + 1 + 1 + start + Monitor command + + + + + gnome-system-monitor + 1 + 1 + 24 + + + + + + + + + + + + + 0 + 12 + 12 + 6 + 6 + vertical + + + 0 + + + 0 + none + + + 100 + 1 + 0 + + + 0 + 6 + 6 + 6 + 6 + 12 + + + 1 + 0 + start + Include static info + + + + + end + 5 + + + + + + + + + + + + + 0 + 12 + 12 + 6 + 6 + vertical + + + 0 + + + 0 + none + + + 100 + 1 + 0 + + + 0 + 6 + 6 + 6 + 6 + 12 + + + 1 + 0 + start + Include static GPU info + + + + + end + 5 + + + + + + + + + + + + diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/schemas/gschemas.compiled b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/schemas/gschemas.compiled new file mode 100644 index 0000000..936fdb3 Binary files /dev/null and b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/schemas/gschemas.compiled differ diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/schemas/org.gnome.shell.extensions.vitals.gschema.xml b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/schemas/org.gnome.shell.extensions.vitals.gschema.xml new file mode 100644 index 0000000..7a0decd --- /dev/null +++ b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/schemas/org.gnome.shell.extensions.vitals.gschema.xml @@ -0,0 +1,155 @@ + + + + + ['_memory_usage_', '_system_load_1m_', '__network-rx_max__'] + Sensors to show in panel + List of sensors to be shown in the panel + + + 5 + Seconds between updates + Delay between sensor polling + + + 2 + Position in panel + Position in Panel ('left', 'center', 'right') + + + false + Use higher precision + Show one extra digit after decimal + + + true + Alphabetize sensors + Display sensors in alphabetical order + + + false + Hide zero values + Hide data from sensors that are invalid + + + true + Monitor temperature + Display temperature of various components + + + 0 + Temperature unit + The unit ('centigrade' or 'fahrenheit') the extension should display the temperature in + + + true + Monitor voltage + Display voltage of various components + + + true + Monitor fan + Display fan rotation per minute + + + true + Monitor memory + Display memory information + + + true + Monitor processor + Display processor information + + + true + Monitor system + Display system information + + + true + Monitor storage + Display storage information + + + true + Monitor network + Display network information + + + true + Include public IP address + Display public IP address of internet connection + + + 0 + Network speed format + Should speed display in bits or bytes? + + + "/" + Storage path + Storage path for monitoring + + + false + Monitor battery + Monitor battery health + + + 1 + Memory measurement + Can use gigabyte or gibibyte for memory + + + 1 + Storage measurement + Can use gigabyte or gibibyte for storage + + + 0 + Battery slot to monitor + Which numerical battery slot should vitals monitor + + + true + Use fixed widths in top bar + Keep sensors in top bar from jumping around + + + false + Hide icons in top bar + Keep top bar clean by only showing sensor values + + + false + Make the menu centered + Center the menu to the icon regardless of the position in the panel + + + "gnome-system-monitor" + System Monitor command + The command run when system monitor button is clicked + + + false + Include processor static information + Display processor static information that doesn't change + + + false + Monitor GPU + Display GPU information (requires the nvidia-smi tool) + + + false + Include GPU static information + Display GPU static information that doesn't change + + + 0 + Icon styles + Set the style for the displayed sensor icons ('original', 'updated') + + + diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/sensors.js b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/sensors.js new file mode 100644 index 0000000..be8df5d --- /dev/null +++ b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/sensors.js @@ -0,0 +1,1072 @@ +/* + Copyright (c) 2018, Chris Monahan + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the GNOME nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +import GLib from 'gi://GLib'; +import GObject from 'gi://GObject'; +import * as SubProcessModule from './helpers/subprocess.js'; +import * as FileModule from './helpers/file.js'; +import { gettext as _ } from 'resource:///org/gnome/shell/extensions/extension.js'; +import NM from 'gi://NM'; + +let GTop, hasGTop = true; +try { + ({default: GTop} = await import('gi://GTop')); +} catch (err) { + log(err); + hasGTop = false; +}; + +export const Sensors = GObject.registerClass({ + GTypeName: 'Sensors', +}, class Sensors extends GObject.Object { + _init(settings, sensorIcons) { + this._settings = settings; + this._sensorIcons = sensorIcons; + + this.resetHistory(); + + this._last_processor = { 'core': {}, 'speed': [] }; + + this._settingChangedSignals = []; + this._addSettingChangedSignal('show-gpu', this._reconfigureNvidiaSmiProcess.bind(this)); + this._addSettingChangedSignal('update-time', this._reconfigureNvidiaSmiProcess.bind(this)); + //this._addSettingChangedSignal('include-static-gpu-info', this._reconfigureNvidiaSmiProcess.bind(this)); + + this._gpu_drm_vendors = null; + this._gpu_drm_indices = null; + this._nvidia_smi_process = null; + this._nvidia_labels = []; + this._bad_split_count = 0; + + this._frameMonitorSignalId = 0; + this._frameMonitorLastTime = 0; + this._frameMonitorFrameCount = 0; + this._frameMonitorAccTime = 0; + this._frameMonitorCurrentHz = 0; + + if (hasGTop) { + this.storage = new GTop.glibtop_fsusage(); + this._storageDevice = ''; + this._findStorageDevice(); + + this._lastRead = 0; + this._lastWrite = 0; + } + } + + _addSettingChangedSignal(key, callback) { + this._settingChangedSignals.push(this._settings.connect('changed::' + key, callback)); + } + + _refreshIPAddress(callback) { + // check IP address + new FileModule.File('https://ipv4.corecoding.com').read().then(contents => { + let obj = JSON.parse(contents); + this._returnValue(callback, 'Public IP', obj['IPv4'], 'network', 'string'); + }).catch(err => { }); + } + + _findStorageDevice() { + new FileModule.File('/proc/mounts').read("\n").then(lines => { + for (let line of lines) { + let loadArray = line.trim().split(/\s+/); + if (loadArray[1] == this._settings.get_string('storage-path')) { + this._storageDevice = loadArray[0]; + break; + } + } + }).catch(err => { }); + } + + query(callback, dwell) { + if (!this._hardware_detected) { + // we could set _hardware_detected in discoverHardwareMonitors, but by + // doing it here, we guarantee avoidance of race conditions + this._hardware_detected = true; + this._discoverHardwareMonitors(callback); + } + + for (let sensor in this._sensorIcons) { + if (this._settings.get_boolean('show-' + sensor)) { + if (sensor == 'temperature' || sensor == 'voltage' || sensor == 'fan') { + // for temp, volt, fan, we have a shared handler + this._queryTempVoltFan(callback, sensor); + } else { + // directly call queryFunction below + let method = '_query' + sensor[0].toUpperCase() + sensor.slice(1); + this[method](callback, dwell); + } + } + } + } + + _queryTempVoltFan(callback, type) { + for (let label in this._tempVoltFanSensors[type]) { + let sensor = this._tempVoltFanSensors[type][label]; + + new FileModule.File(sensor['path']).read().then(value => { + this._returnValue(callback, label, value, type, sensor['format']); + }).catch(err => { + this._returnValue(callback, label, 'disabled', type, sensor['format']); + }); + } + } + + _queryMemory(callback) { + // check memory info + new FileModule.File('/proc/meminfo').read().then(lines => { + let values = '', total = 0, avail = 0, swapTotal = 0, swapFree = 0, cached = 0, memFree = 0; + + if (values = lines.match(/MemTotal:(\s+)(\d+) kB/)) total = values[2]; + if (values = lines.match(/MemAvailable:(\s+)(\d+) kB/)) avail = values[2]; + if (values = lines.match(/SwapTotal:(\s+)(\d+) kB/)) swapTotal = values[2]; + if (values = lines.match(/SwapFree:(\s+)(\d+) kB/)) swapFree = values[2]; + if (values = lines.match(/Cached:(\s+)(\d+) kB/)) cached = values[2]; + if (values = lines.match(/MemFree:(\s+)(\d+) kB/)) memFree = values[2]; + + let used = total - avail + let utilized = used / total; + let swapUsed = swapTotal - swapFree + let swapUtilized = swapUsed / swapTotal; + + this._returnValue(callback, 'Usage', utilized, 'memory', 'percent'); + this._returnValue(callback, 'memory', utilized, 'memory-group', 'percent'); + this._returnValue(callback, 'Physical', total, 'memory', 'memory'); + this._returnValue(callback, 'Available', avail, 'memory', 'memory'); + this._returnValue(callback, 'Allocated', used, 'memory', 'memory'); + this._returnValue(callback, 'Cached', cached, 'memory', 'memory'); + this._returnValue(callback, 'Free', memFree, 'memory', 'memory'); + this._returnValue(callback, 'Swap Total', swapTotal, 'memory', 'memory'); + this._returnValue(callback, 'Swap Free', swapFree, 'memory', 'memory'); + this._returnValue(callback, 'Swap Used', swapUsed, 'memory', 'memory'); + this._returnValue(callback, 'Swap Usage', swapUtilized, 'memory', 'percent'); + }).catch(err => { }); + } + + _queryProcessor(callback, dwell) { + let columns = ['user', 'nice', 'system', 'idle', 'iowait', 'irq', 'softirq', 'steal', 'guest', 'guest_nice']; + + // check processor usage + new FileModule.File('/proc/stat').read("\n").then(lines => { + let statistics = {}; + + for (let line of lines) { + let reverse_data = line.match(/^(cpu\d*\s)(.+)/); + if (reverse_data) { + let cpu = reverse_data[1].trim(); + + if (!(cpu in statistics)) + statistics[cpu] = {}; + + if (!(cpu in this._last_processor['core'])) + this._last_processor['core'][cpu] = 0; + + let stats = reverse_data[2].trim().split(' ').reverse(); + for (let column of columns) + statistics[cpu][column] = parseInt(stats.pop()); + } + } + + let cores = Object.keys(statistics).length - 1; + + for (let cpu in statistics) { + let total = statistics[cpu]['user'] + statistics[cpu]['nice'] + statistics[cpu]['system']; + + // make sure we have data to report + if (this._last_processor['core'][cpu] > 0) { + let delta = (total - this._last_processor['core'][cpu]) / dwell; + + // /proc/stat provides overall usage for us under the 'cpu' heading + if (cpu == 'cpu') { + delta = delta / cores; + this._returnValue(callback, 'processor', delta / 100, 'processor-group', 'percent'); + this._returnValue(callback, 'Usage', delta / 100, 'processor', 'percent'); + } else { + this._returnValue(callback, _('Core %d').format(cpu.substr(3)), delta / 100, 'processor', 'percent'); + } + } + + this._last_processor['core'][cpu] = total; + } + + // if frequency scaling is enabled, gather cpu-freq values + if (!this._processor_uses_cpu_info) { + for (let core = 0; core <= cores; core++) { + new FileModule.File('/sys/devices/system/cpu/cpu' + core + '/cpufreq/scaling_cur_freq').read().then(value => { + this._last_processor['speed'][core] = parseInt(value); + }).catch(err => { }); + } + } + }).catch(err => { }); + + // if frequency scaling is disabled, use cpuinfo for speed + if (this._processor_uses_cpu_info) { + // grab CPU frequency + new FileModule.File('/proc/cpuinfo').read("\n").then(lines => { + let freqs = []; + for (let line of lines) { + // grab megahertz + let value = line.match(/^cpu MHz(\s+): ([+-]?\d+(\.\d+)?)/); + if (value) freqs.push(parseFloat(value[2])); + } + + let sum = freqs.reduce((a, b) => a + b); + let hertz = (sum / freqs.length) * 1000 * 1000; + this._returnValue(callback, 'Frequency', hertz, 'processor', 'hertz'); + + let max_hertz = freqs.reduce((a, b) => Math.max(a, b)) * 1000 * 1000; + this._returnValue(callback, 'Max frequency', max_hertz, 'processor', 'hertz'); + let min_hertz = freqs.reduce((a, b) => Math.min(a, b)) * 1000 * 1000; + this._returnValue(callback, 'Min frequency', min_hertz, 'processor', 'hertz'); + }).catch(err => { }); + // if frequency scaling is enabled, cpu-freq reports + } else if (Object.values(this._last_processor['speed']).length > 0) { + let sum = this._last_processor['speed'].reduce((a, b) => a + b); + let hertz = (sum / this._last_processor['speed'].length) * 1000; + this._returnValue(callback, 'Frequency', hertz, 'processor', 'hertz'); + let max_hertz = this._last_processor['speed'].reduce((a, b) => Math.max(a, b)) * 1000; + this._returnValue(callback, 'Max frequency', max_hertz, 'processor', 'hertz'); + let min_hertz = this._last_processor['speed'].reduce((a, b) => Math.min(a, b)) * 1000; + this._returnValue(callback, 'Min frequency', min_hertz, 'processor', 'hertz'); + } + } + + _querySystem(callback) { + // check load average + new FileModule.File('/proc/sys/fs/file-nr').read("\t").then(loadArray => { + this._returnValue(callback, 'Open Files', loadArray[0], 'system', 'string'); + }).catch(err => { }); + + // check load average + new FileModule.File('/proc/loadavg').read(' ').then(loadArray => { + let proc = loadArray[3].split('/'); + + this._returnValue(callback, 'Load 1m', loadArray[0], 'system', 'load'); + this._returnValue(callback, 'system', loadArray[0], 'system-group', 'load'); + this._returnValue(callback, 'Load 5m', loadArray[1], 'system', 'load'); + this._returnValue(callback, 'Load 15m', loadArray[2], 'system', 'load'); + this._returnValue(callback, 'Threads Active', proc[0], 'system', 'string'); + this._returnValue(callback, 'Threads Total', proc[1], 'system', 'string'); + }).catch(err => { }); + + // check uptime + new FileModule.File('/proc/uptime').read(' ').then(upArray => { + this._returnValue(callback, 'Uptime', upArray[0], 'system', 'uptime'); + + let cores = Object.keys(this._last_processor['core']).length - 1; + if (cores > 0) + this._returnValue(callback, 'Process Time', upArray[0] - upArray[1] / cores, 'processor', 'uptime'); + }).catch(err => { }); + } + + _queryNetwork(callback, dwell) { + // check network speed + let directions = ['tx', 'rx']; + let netbase = '/sys/class/net/'; + + new FileModule.File(netbase).list().then(interfaces => { + for (let iface of interfaces) { + for (let direction of directions) { + // lo tx and rx are the same + if (iface == 'lo' && direction == 'rx') continue; + + new FileModule.File(netbase + iface + '/statistics/' + direction + '_bytes').read().then(value => { + // issue #217 - don't include 'lo' traffic in Maximum calculations in values.js + // by not using network-rx or network-tx + let name = iface + ((iface == 'lo')?'':' ' + direction); + + let type = 'network' + ((iface=='lo')?'':'-' + direction); + this._returnValue(callback, name, value, type, 'storage'); + }).catch(err => { }); + } + } + }).catch(err => { }); + + // some may not want public ip checking + if (this._settings.get_boolean('include-public-ip')) { + // check the public ip every hour or when waking from sleep + if (this._next_public_ip_check <= 0) { + this._next_public_ip_check = 3600; + + this._refreshIPAddress(callback); + } + + this._next_public_ip_check -= dwell; + } + + // wireless interface statistics + new FileModule.File('/proc/net/wireless').read("\n", true).then(lines => { + // wireless has two headers - first is stripped in helper function + lines.shift(); + + // if multiple wireless device, we use the last one + for (let line of lines) { + let netArray = line.trim().split(/\s+/); + let quality_pct = netArray[2].substr(0, netArray[2].length-1) / 70; + let signal = netArray[3].substr(0, netArray[3].length-1); + + this._returnValue(callback, 'WiFi Link Quality', quality_pct, 'network', 'percent'); + this._returnValue(callback, 'WiFi Signal Level', signal, 'network', 'string'); + } + }).catch(err => { }); + } + + _queryStorage(callback, dwell) { + // display zfs arc status, if available + new FileModule.File('/proc/spl/kstat/zfs/arcstats').read().then(lines => { + let values = '', target = 0, maximum = 0, current = 0; + + if (values = lines.match(/c(\s+)(\d+)(\s+)(\d+)/)) target = values[4]; + if (values = lines.match(/c_max(\s+)(\d+)(\s+)(\d+)/)) maximum = values[4]; + if (values = lines.match(/size(\s+)(\d+)(\s+)(\d+)/)) current = values[4]; + + // ZFS statistics + this._returnValue(callback, 'ARC Target', target, 'storage', 'storage'); + this._returnValue(callback, 'ARC Maximum', maximum, 'storage', 'storage'); + this._returnValue(callback, 'ARC Current', current, 'storage', 'storage'); + }).catch(err => { }); + + // check disk performance stats + new FileModule.File('/proc/diskstats').read("\n").then(lines => { + for (let line of lines) { + let loadArray = line.trim().split(/\s+/); + if ('/dev/' + loadArray[2] == this._storageDevice) { + var read = (loadArray[5] * 512); + var write = (loadArray[9] * 512); + this._returnValue(callback, 'Read total', read, 'storage', 'storage'); + this._returnValue(callback, 'Write total', write, 'storage', 'storage'); + this._returnValue(callback, 'Read rate', (read - this._lastRead) / dwell, 'storage', 'storage'); + this._returnValue(callback, 'Write rate', (write - this._lastWrite) / dwell, 'storage', 'storage'); + this._lastRead = read; + this._lastWrite = write; + break; + } + } + }).catch(err => { }); + + // skip rest of stats if gtop not available + if (!hasGTop) return; + + GTop.glibtop_get_fsusage(this.storage, this._settings.get_string('storage-path')); + + let total = this.storage.blocks * this.storage.block_size; + let avail = this.storage.bavail * this.storage.block_size; + let free = this.storage.bfree * this.storage.block_size; + let used = total - free; + let reserved = (total - avail) - used; + let freePercent = 0; + let usedPercent = 0; + if (total > 0) { + freePercent = Math.round((free / total) * 100); + usedPercent = Math.round((used / total) * 100); + } + + this._returnValue(callback, 'Total', total, 'storage', 'storage'); + this._returnValue(callback, 'Used', used, 'storage', 'storage'); + this._returnValue(callback, 'Reserved', reserved, 'storage', 'storage'); + this._returnValue(callback, 'Free', avail, 'storage', 'storage'); + this._returnValue(callback, 'Used %', usedPercent + '%', 'storage', 'string'); + this._returnValue(callback, 'Free %', freePercent + '%', 'storage', 'string'); + this._returnValue(callback, 'storage', avail, 'storage-group', 'storage'); + } + + _queryBattery(callback) { + let battery_slot = this._settings.get_int('battery-slot'); + + // create a mapping of indices to battery paths (from prefs.ui) + const BATTERY_PATHS = { + 0: 'BAT0', + 1: 'BAT1', + 2: 'BAT2', + 3: 'BATT', + 4: 'CMB0', + 5: 'CMB1', + 6: 'CMB2', + 7: 'macsmc-battery' + }; + + // uevent has all necessary fields, no need to read individual files + let battery_path = '/sys/class/power_supply/' + BATTERY_PATHS[battery_slot] + '/uevent'; + new FileModule.File(battery_path).read("\n").then(lines => { + let output = {}; + for (let line of lines) { + let split = line.split('='); + output[split[0].replace('POWER_SUPPLY_', '')] = split[1]; + } + + if ('STATUS' in output) { + this._returnValue(callback, 'State', output['STATUS'], 'battery', ''); + } + + if ('CYCLE_COUNT' in output) { + this._returnValue(callback, 'Cycles', output['CYCLE_COUNT'], 'battery', ''); + } + + if ('VOLTAGE_NOW' in output) { + this._returnValue(callback, 'Voltage', output['VOLTAGE_NOW'] / 1000, 'battery', 'in'); + } + + if ('CAPACITY_LEVEL' in output) { + this._returnValue(callback, 'Level', output['CAPACITY_LEVEL'], 'battery', ''); + } + + if ('CAPACITY' in output) { + this._returnValue(callback, 'Percentage', output['CAPACITY'] / 100, 'battery', 'percent'); + } + + if ('VOLTAGE_NOW' in output && 'CURRENT_NOW' in output && (!('POWER_NOW' in output))) { + output['POWER_NOW'] = (output['VOLTAGE_NOW'] * output['CURRENT_NOW']) / 1000000; + } + + if ('POWER_NOW' in output) { + const powerValue = ( + parseFloat(output['POWER_NOW']) * (output['STATUS'] === 'Discharging' ? -1 : 1) + ); + this._returnValue(callback, 'Power Rate', powerValue, 'battery', 'watt'); + this._returnValue(callback, 'battery', powerValue, 'battery-group', 'watt'); + } + + if ('CHARGE_FULL' in output && 'VOLTAGE_MIN_DESIGN' in output && (!('ENERGY_FULL' in output))) { + output['ENERGY_FULL'] = (output['CHARGE_FULL'] * output['VOLTAGE_MIN_DESIGN']) / 1000000; + } + + if ('ENERGY_FULL' in output) { + this._returnValue(callback, 'Energy (full)', output['ENERGY_FULL'], 'battery', 'watt-hour'); + } + + if ('CHARGE_FULL_DESIGN' in output && 'VOLTAGE_MIN_DESIGN' in output && (!('ENERGY_FULL_DESIGN' in output))) { + output['ENERGY_FULL_DESIGN'] = (output['CHARGE_FULL_DESIGN'] * output['VOLTAGE_MIN_DESIGN']) / 1000000; + } + + if ('ENERGY_FULL_DESIGN' in output) { + this._returnValue(callback, 'Energy (design)', output['ENERGY_FULL_DESIGN'], 'battery', 'watt-hour'); + + if ('ENERGY_FULL' in output) { + this._returnValue(callback, 'Capacity', (output['ENERGY_FULL'] / output['ENERGY_FULL_DESIGN']), 'battery', 'percent'); + } + } + + if ('VOLTAGE_MIN_DESIGN' in output && 'CHARGE_NOW' in output && (!('ENERGY_NOW' in output))) { + output['ENERGY_NOW'] = (output['VOLTAGE_MIN_DESIGN'] * output['CHARGE_NOW']) / 1000000; + } + + if ('ENERGY_NOW' in output) { + this._returnValue(callback, 'Energy (now)', output['ENERGY_NOW'], 'battery', 'watt-hour'); + } + + if ('ENERGY_FULL' in output && 'ENERGY_NOW' in output && 'POWER_NOW' in output && output['POWER_NOW'] !== 0 && 'STATUS' in output && (output['STATUS'] == 'Charging' || output['STATUS'] == 'Discharging')) { + + let timeLeft = 0; + + // two different formulas depending on if we are charging or discharging + if (output['STATUS'] == 'Charging') { + timeLeft = ((output['ENERGY_FULL'] - output['ENERGY_NOW']) / output['POWER_NOW']); + } else { + timeLeft = (output['ENERGY_NOW'] / Math.abs(output['POWER_NOW'])); + } + + // don't process Infinity values + if (timeLeft !== Infinity) { + if (this._battery_charge_status != output['STATUS']) { + // clears history due to state change + this._battery_time_left_history = []; + + // clear time left history when laptop goes in and out of charging + this._battery_charge_status = output['STATUS']; + } + + // add latest time left estimate to our history + this._battery_time_left_history.push(parseInt(timeLeft * 3600)); + + // keep track of last 15 time left estimates by erasing the first + if (this._battery_time_left_history.length > 10) + this._battery_time_left_history.shift(); + + // sum up and create average of our time left history + let sum = this._battery_time_left_history.reduce((a, b) => a + b); + let avg = sum / this._battery_time_left_history.length; + + // use time left history to update screen + this._returnValue(callback, 'Time left', parseInt(avg), 'battery', 'runtime'); + } + } else { + this._returnValue(callback, 'Time left', output['STATUS'], 'battery', ''); + } + }).catch(err => { }); + } + + _initFrameMonitor() { + if (this._frameMonitorSignalId) return; + this._frameMonitorLastTime = 0; + this._frameMonitorFrameCount = 0; + this._frameMonitorAccTime = 0; + this._frameMonitorCurrentHz = 0; + this._frameMonitorSignalId = global.stage.connect('after-paint', () => { + this._onAfterPaint(); + }); + } + + _destroyFrameMonitor() { + if (this._frameMonitorSignalId) { + global.stage.disconnect(this._frameMonitorSignalId); + this._frameMonitorSignalId = 0; + } + this._frameMonitorLastTime = 0; + this._frameMonitorCurrentHz = 0; + } + + _onAfterPaint() { + const now = GLib.get_monotonic_time(); + + if (this._frameMonitorLastTime === 0) { + this._frameMonitorLastTime = now; + return; + } + + const delta = now - this._frameMonitorLastTime; + this._frameMonitorLastTime = now; + + this._frameMonitorFrameCount++; + this._frameMonitorAccTime += delta; + + if (this._frameMonitorAccTime >= 500000) { + this._frameMonitorCurrentHz = this._frameMonitorFrameCount / (this._frameMonitorAccTime / 1000000); + this._frameMonitorFrameCount = 0; + this._frameMonitorAccTime = 0; + } + } + + _queryGpu(callback) { + if (this._frameMonitorCurrentHz > 0) + this._returnValue(callback, 'Refresh Rate', this._frameMonitorCurrentHz, 'gpu#1', 'hertz'); + + if (!this._nvidia_smi_process) { + // no nvidia-smi, so we use sysfs DRM if any cards was discovered + if (!this._gpu_drm_indices){ + if (this._frameMonitorCurrentHz > 0) + this._returnValue(callback, 'Refresh Rate', this._frameMonitorCurrentHz, 'gpu#1-group', 'hertz'); + this._disableGpuLabels(callback); + return; + } else { + this._readGpuDrm(callback); + return; + } + } + + this._nvidia_smi_process.read('\n').then(lines => { + /// for debugging multi-gpu on systems with only one gpu + /// duplicates the first gpu's data 3 times, for 4 total gpus + ///if(lines.length == 0) return; + ///for(let _gpuNum = 1; _gpuNum <= 3; _gpuNum++) + /// lines.push(lines[0]); + + for (let i = 0; i < lines.length; i++) { + this._parseNvidiaSmiLine(callback, lines[i], i + 1, lines.length > 1); + } + + // if we've already updated the static info during the last parse, then stop doing so. + // this is so the _parseNvidiaSmiLine function won't return static info anymore + // and the nvidia-smi commmand won't be queried for static info either + if(!this._nvidia_static_returned) { + this._nvidia_static_returned = true; + //reconfigure the process to stop querying static info + this._reconfigureNvidiaSmiProcess(); + } + }).catch(err => { + this._disableGpuLabels(callback); + this._terminateNvidiaSmiProcess(); + }); + } + + _parseNvidiaSmiLine(callback, csv, gpuNum, multiGpu) { + const expectedSplitLength = 19; + let csv_split = csv.split(','); + + // occasionally the nvidia-smi command can get cut off before it can be fully read, thus the parse function only gets part of a line + // hence we count the number of bad splits and only terminate the process after a few bad splits in a row + // this prevents anomalous readings from terminating the process + if (csv_split.length < expectedSplitLength) { + this._bad_split_count++; + //if we've had 2 bad splits/reads in a row, try to restart the process + if (this._bad_split_count == 2) this._reconfigureNvidiaSmiProcess(); + //if we still get a bad read after that, then it's not an anomaly; terminate the process + else if (this._bad_split_count >= 3) this._terminateNvidiaSmiProcess(); + return; + } + this._bad_split_count = 0; + + let [ + label, + fan_speed_pct, + temp_gpu, temp_mem, + mem_total, mem_used, mem_reserved, mem_free, + util_gpu, util_mem, util_encoder, util_decoder, + clock_gpu, clock_mem, clock_encode_decode, + power, power_avg, + link_gen_current, link_width_current + ] = csv_split; + + const staticNames = [ + 'temp_limit', 'power_limit', + 'link_gen_max', 'link_width_max', + 'addressing_mode', + 'driver_version', 'vbios', 'serial', + 'domain_num', 'bus_num', 'device_num', 'device_id', 'sub_device_id' + ]; + let staticInfo = {}; + + // if we have queried static info this time around, populate our static info object + if(csv_split.length == (expectedSplitLength + staticNames.length)){ + for(let i = 0; i < staticNames.length; i++) { + //set the static info to a default (0) if it's undefined + const value = csv_split[expectedSplitLength + i]; + staticInfo[staticNames[i]] = (typeof value !== "undefined") ? value : 0; + } + } + + const typeName = 'gpu#' + gpuNum; + const globalLabel = 'GPU' + (multiGpu ? ' ' + gpuNum : ''); + const memTempValid = !isNaN(parseInt(temp_mem)); + + this._returnGpuValue(callback, 'Graphics', parseInt(util_gpu) * 0.01, typeName + '-group', 'percent'); + + this._returnGpuValue(callback, 'Name', label, typeName, ''); + + this._returnGpuValue(callback, globalLabel, parseInt(fan_speed_pct) * 0.01, 'fan', 'percent'); + this._returnGpuValue(callback, 'Fan', parseInt(fan_speed_pct) * 0.01, typeName, 'percent'); + + this._returnGpuValue(callback, globalLabel, parseInt(temp_gpu) * 1000, 'temperature', 'temp'); + this._returnGpuValue(callback, 'Temperature', parseInt(temp_gpu) * 1000, typeName, 'temp'); + this._returnGpuValue(callback, 'Memory Temperature', parseInt(temp_mem) * 1000, typeName, 'temp', memTempValid); + this._returnStaticGpuValue(callback, 'Temperature Limit', parseInt(staticInfo['temp_limit']) * 1000, typeName, 'temp'); + + this._returnGpuValue(callback, 'Memory Usage', parseInt(mem_used) / parseInt(mem_total), typeName, 'percent'); + this._returnGpuValue(callback, 'Memory Total', parseInt(mem_total) * 1000, typeName, 'memory'); + this._returnGpuValue(callback, 'Memory Used', parseInt(mem_used) * 1000, typeName, 'memory'); + this._returnGpuValue(callback, 'Memory Reserved', parseInt(mem_reserved) * 1000, typeName, 'memory'); + this._returnGpuValue(callback, 'Memory Free', parseInt(mem_free) * 1000, typeName, 'memory'); + + this._returnGpuValue(callback, 'Memory Utilization', parseInt(util_mem) * 0.01, typeName, 'percent'); + this._returnGpuValue(callback, 'Utilization', parseInt(util_gpu) * 0.01, typeName, 'percent'); + this._returnGpuValue(callback, 'Encoder Utilization', parseInt(util_encoder) * 0.01, typeName, 'percent'); + this._returnGpuValue(callback, 'Decoder Utilization', parseInt(util_decoder) * 0.01, typeName, 'percent'); + + this._returnGpuValue(callback, 'Frequency', parseInt(clock_gpu) * 1000 * 1000, typeName, 'hertz'); + this._returnGpuValue(callback, 'Memory Frequency', parseInt(clock_mem) * 1000 * 1000, typeName, 'hertz'); + this._returnGpuValue(callback, 'Encoder/Decoder Frequency', parseInt(clock_encode_decode) * 1000 * 1000, typeName, 'hertz'); + + //this._returnGpuValue(callback, 'Encoder Sessions', parseInt(encoder_sessions), typeName, 'string'); + + this._returnGpuValue(callback, 'Power', power, typeName, 'watt-gpu'); + this._returnGpuValue(callback, 'Average Power', power_avg, typeName, 'watt-gpu'); + this._returnStaticGpuValue(callback, 'Power Limit', parseInt(staticInfo['power_limit']), typeName, 'watt-gpu'); + + this._returnGpuValue(callback, 'Link Speed', link_gen_current + 'x' + link_width_current, typeName, 'pcie'); + this._returnStaticGpuValue(callback, 'Maximum Link Speed', staticInfo['link_gen_max'] + 'x' + staticInfo['link_width_max'], typeName, 'pcie'); + + this._returnStaticGpuValue(callback, 'Addressing Mode', staticInfo['addressing_mode'], typeName, 'string'); + + this._returnStaticGpuValue(callback, 'Driver Version', staticInfo['driver_version'], typeName, 'string'); + this._returnStaticGpuValue(callback, 'vBIOS Version', staticInfo['vbios'], typeName, 'string'); + this._returnStaticGpuValue(callback, 'Serial Number', staticInfo['serial'], typeName, 'string'); + + this._returnStaticGpuValue(callback, 'Domain Number', staticInfo['domain_num'], typeName, 'string'); + this._returnStaticGpuValue(callback, 'Bus Number', staticInfo['bus_num'], typeName, 'string'); + this._returnStaticGpuValue(callback, 'Device Number', staticInfo['device_num'], typeName, 'string'); + this._returnStaticGpuValue(callback, 'Device ID', staticInfo['device_id'], typeName, 'string'); + this._returnStaticGpuValue(callback, 'Sub Device ID', staticInfo['sub_device_id'], typeName, 'string'); + } + + _readGpuDrm(callback){ + const multiGpu = this._gpu_drm_indices.length > 1; + const unit = this._settings.get_int('memory-measurement') ? 1000 : 1024; + for (let z = 0; z < this._gpu_drm_indices.length; z++ ) { + let i = this._gpu_drm_indices[z]; + const typeName = 'gpu#' + i; + const vendor = this._gpu_drm_vendors[z]; + + // AMD + if(vendor === "0x1002") { + // read GPU usage and create group lebel for card + new FileModule.File('/sys/class/drm/card'+i+'/device/gpu_busy_percent').read().then(value => { + // create group + this._returnGpuValue(callback, 'Graphics', parseInt(value) * 0.01, typeName + '-group', 'percent'); + this._returnGpuValue(callback, 'Vendor', "AMD", typeName, 'string'); + this._returnGpuValue(callback, 'Usage', parseInt(value) * 0.01, typeName, 'percent'); + }).catch(err => { + // nothing to do, keep old value displayed + }); + new FileModule.File('/sys/class/drm/card'+i+'/device/mem_info_vram_used').read().then(value => { + this._returnGpuValue(callback, 'Memory Used', parseInt(value) / unit, typeName, 'memory'); + }).catch(err => { + // nothing to do, keep old value displayed + }); + new FileModule.File('/sys/class/drm/card'+i+'/device/mem_info_vram_total').read().then(value => { + this._returnGpuValue(callback, 'Memory Total', parseInt(value) / unit, typeName, 'memory'); + }).catch(err => { + // nothing to do, keep old value displayed + }); + } else { + // for other vendors only show basic card info + let vendorName = null; + switch (vendor){ + case '0x10DE': vendorName = 'NVIDIA'; break; // should be never used as nvidia-smi should be preferred + case '0x13B5': vendorName = 'ARM'; break; + case '0x5143': vendorName = 'Qualcomm'; break; + case '0x8086': vendorName = 'Intel'; break; + default: vendorName = "Unknown " + vendor; + } + this._returnGpuValue(callback, 'Graphics', vendorName, typeName + '-group', 'string'); + } + } + } + + _disableGpuLabels(callback) { + for (let labelObj of this._nvidia_labels) + this._returnValue(callback, labelObj.label, 'disabled', labelObj.type, labelObj.format); + } + + _returnStaticGpuValue(callback, label, value, type, format) { + //if we've already tried to return existing static info before or if the option isn't enabled, then do nothing. + if (this._nvidia_static_returned || !this._settings.get_boolean('include-static-gpu-info')) + return; + + //we don't need to disable static info labels, so just use ordinary returnValue function + this._returnValue(callback, label, value, type, format); + } + + _returnGpuValue(callback, label, value, type, format, display = true) { + if(!display) return; + + if(format !== "string" && (value === 'N/A' || value === '[N/A]' || isNaN(value))) return; + + let nvidiaLabel = {'label': label, 'type': type, 'format': format}; + if (!this._nvidia_labels.includes(nvidiaLabel)) + this._nvidia_labels.push(nvidiaLabel); + + this._returnValue(callback, label, value, type, format); + } + + _returnValue(callback, label, value, type, format) { + // don't return if value is not a number - will revisit later + //if (isNaN(value)) return; + callback(label, value, type, format); + } + + _discoverHardwareMonitors(callback) { + this._tempVoltFanSensors = { 'temperature': {}, 'voltage': {}, 'fan': {} }; + + let hwbase = '/sys/class/hwmon/'; + + // process sensor_types now so it is not called multiple times below + let sensor_types = {}; + + if (this._settings.get_boolean('show-temperature')) + sensor_types['temp'] = 'temperature'; + + if (this._settings.get_boolean('show-voltage')) + sensor_types['in'] = 'voltage'; + + if (this._settings.get_boolean('show-fan')) + sensor_types['fan'] = 'fan'; + + // a little informal, but this code has zero I/O block + new FileModule.File(hwbase).list().then(files => { + for (let file of files) { + // grab name of sensor + new FileModule.File(hwbase + file + '/name').read().then(name => { + // are we dealing with a CPU? + if (name == 'coretemp') { + // determine which processor (socket) we are dealing with + new FileModule.File(hwbase + file + '/temp1_label').read().then(prefix => { + this._processTempVoltFan(callback, sensor_types, prefix, hwbase + file, file); + }).catch(err => { + // this shouldn't be necessary, but just in case temp1_label doesn't exist + // attempt to fix #266 + this._processTempVoltFan(callback, sensor_types, name, hwbase + file, file); + }); + } else { + // not a CPU, process all other sensors + this._processTempVoltFan(callback, sensor_types, name, hwbase + file, file); + } + }).catch(err => { + new FileModule.File(hwbase + file + '/device/name').read().then(name => { + this._processTempVoltFan(callback, sensor_types, name, hwbase + file + '/device', file); + }).catch(err => { }); + }); + } + }).catch(err => { }); + + // does this system support cpu scaling? if so we will use it to grab Frequency and Boost below + new FileModule.File('/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq').read().then(value => { + this._processor_uses_cpu_info = false; + }).catch(err => { }); + + // is static CPU information enabled? + if (this._settings.get_boolean('include-static-info')) { + // grab static CPU information + new FileModule.File('/proc/cpuinfo').read("\n").then(lines => { + let vendor_id = ''; + let bogomips = ''; + let sockets = {}; + let cache = ''; + + for (let line of lines) { + let value = ''; + + // grab cpu vendor + if (value = line.match(/^vendor_id(\s+): (\w+.*)/)) vendor_id = value[2]; + + // grab bogomips + if (value = line.match(/^bogomips(\s+): (\d*\.?\d*)$/)) bogomips = value[2]; + + // grab processor count + if (value = line.match(/^physical id(\s+): (\d+)$/)) sockets[value[2]] = 1; + + // grab cache + if (value = line.match(/^cache size(\s+): (\d+) KB$/)) cache = value[2]; + } + + this._returnValue(callback, 'Vendor', vendor_id, 'processor', 'string'); + this._returnValue(callback, 'Bogomips', bogomips, 'processor', 'string'); + this._returnValue(callback, 'Sockets', Object.keys(sockets).length, 'processor', 'string'); + this._returnValue(callback, 'Cache', cache, 'processor', 'memory'); + }).catch(err => { }); + + // grab static CPU information + new FileModule.File('/proc/version').read(' ').then(kernelArray => { + this._returnValue(callback, 'Kernel', kernelArray[2], 'system', 'string'); + }).catch(err => { }); + } + + // Launch nvidia-smi subprocess if nvidia querying is enabled + this._reconfigureNvidiaSmiProcess(); + this._discoverGpuDrm(); + this._initFrameMonitor(); + } + + _discoverGpuDrm() { + // use DRM only if nvidia-smi is not used + if (this._settings.get_boolean('show-gpu') && this._nvidia_smi_process == null) { + // try to discover up to 10 cards starting from index 0 + for(let i = 0; i < 10 ; i++){ + new FileModule.File('/sys/class/drm/card'+i+'/device/vendor').read().then(value => { + if(!this._gpu_drm_indices){ + this._gpu_drm_indices = []; + this._gpu_drm_vendors = []; + } + this._gpu_drm_indices.push(i); + this._gpu_drm_vendors.push(value); + }).catch(err => { }); + } + } else { + this._gpu_drm_vendors = null; + this._gpu_drm_indices = null; + } + } + + // The nvidia-smi subprocess will keep running and print new sensor data to stdout every + // `update_time` seconds. _queryNvidiaSmi() will be called at roughly the same interval and + // read from the subprocess's stdout to get new sensor data. + + // Regarding "keeping main process & sub process in sync", there are two possible scenarios: + // - For some reason, nvidia-smi prints at a somewhat higher frequency than we call + // _queryNvidiaSmi() to read data. This is okay, eventually one call to _queryNvidiaSmi() + // will read two sensor data updates in a single call. + // - For some reason, _queryNvidiaSmi() is called at a somewhat higher frequency than + // nvidia-smi prints data. This is the more likely scenario with user actions triggering + // additional reads. This eventually triggers an "IO PENDING" error while attempting to + // read, because the previous async read is still waiting. To solve this, the subprocess + // module simply ignores PENDING errors. After ignoring the error, the earlier read will + // eventually return and sensor data will be updated, so this scenario is handled correctly. + + // Generally speaking, the call to _queryNvidiaSmi() and nvidia-smi's printing to stdout do + // not happen at the same time. So the async call in _queryNvidiaSmi() will usually have to + // wait up to `update_time` seconds before getting any results and reporting them through the + // callback. + _reconfigureNvidiaSmiProcess() { + if (this._settings.get_boolean('show-gpu')) { + this._terminateNvidiaSmiProcess(); + + try { + let update_time = this._settings.get_int('update-time'); + let query_interval = Math.max(update_time, 1); + let command = [ + 'nvidia-smi', + '--query-gpu=name,' + + 'fan.speed,' + + 'temperature.gpu,temperature.memory,' + + 'memory.total,memory.used,memory.reserved,memory.free,' + + 'utilization.gpu,utilization.memory,utilization.encoder,utilization.decoder,' + + 'clocks.gr,clocks.mem,clocks.video,' + + 'power.draw.instant,power.draw.average,' + + 'pcie.link.gen.gpucurrent,pcie.link.width.current,' + + (!this._nvidia_static_returned && this._settings.get_boolean('include-static-gpu-info') ? + 'temperature.gpu.tlimit,' + + 'power.limit,' + + 'pcie.link.gen.max,pcie.link.width.max,' + + 'addressing_mode,'+ + 'driver_version,vbios_version,serial,' + + 'pci.domain,pci.bus,pci.device,pci.device_id,pci.sub_device_id,' + : ''), + '--format=csv,noheader,nounits', + '-l', query_interval.toString() + ]; + + this._nvidia_smi_process = new SubProcessModule.SubProcess(command); + } catch(e) { + // proprietary nvidia driver not installed + this._terminateNvidiaSmiProcess(); + } + } else { + this._terminateNvidiaSmiProcess(); + } + } + + _terminateNvidiaSmiProcess() { + if (this._nvidia_smi_process) { + this._nvidia_smi_process.terminate(); + this._nvidia_smi_process = null; + } + } + + _processTempVoltFan(callback, sensor_types, name, path, file) { + let sensor_files = [ 'input', 'label' ]; + + // grab files from directory + new FileModule.File(path).list().then(files2 => { + let trisensors = {}; + + // loop over files from directory + for (let file2 of Object.values(files2)) { + // simple way of processing input and label (from above) + for (let key of Object.values(sensor_files)) { + // process toggled on sensors from extension preferences + for (let sensor_type in sensor_types) { + if (file2.substr(0, sensor_type.length) == sensor_type && file2.substr(-(key.length+1)) == '_' + key) { + let key2 = file + file2.substr(0, file2.indexOf('_')); + + if (!(key2 in trisensors)) { + trisensors[key2] = { + 'type': sensor_types[sensor_type], + 'format': sensor_type, + 'label': path + '/name' + }; + } + + trisensors[key2][key] = path + '/' + file2; + } + } + } + } + + for (let obj of Object.values(trisensors)) { + if (!('input' in obj)) + continue; + + new FileModule.File(obj['input']).read().then(value => { + let extra = (obj['label'].indexOf('_label')==-1) ? ' ' + obj['input'].substr(obj['input'].lastIndexOf('/')+1).split('_')[0] : ''; + + if (value > 0 || !this._settings.get_boolean('hide-zeros') || obj['type'] == 'fan') { + new FileModule.File(obj['label']).read().then(label => { + this._addTempVoltFan(callback, obj, name, label, extra, value); + }).catch(err => { + let tmpFile = obj['label'].substr(0, obj['label'].lastIndexOf('/')) + '/name'; + new FileModule.File(tmpFile).read().then(label => { + this._addTempVoltFan(callback, obj, name, label, extra, value); + }).catch(err => { }); + }); + } + }).catch(err => { }); + } + }).catch(err => { }); + } + + _addTempVoltFan(callback, obj, name, label, extra, value) { + // prepend module that provided sensor data + if (name != label) label = name + ' ' + label; + + //if (label == 'nvme Composite') label = 'NVMe'; + //if (label == 'nouveau') label = 'Nvidia'; + + label = label + extra; + + // in the future we will read /etc/sensors3.conf + if (label == 'acpitz temp1') label = 'ACPI Thermal Zone'; + if (label == 'pch_cannonlake temp1') label = 'Platform Controller Hub'; + if (label == 'iwlwifi_1 temp1') label = 'Wireless Adapter'; + if (label == 'Package id 0') label = 'Processor 0'; + if (label == 'Package id 1') label = 'Processor 1'; + label = label.replace('Package id', 'CPU'); + + let types = [ 'temperature', 'voltage', 'fan' ]; + for (let type of types) { + // check if this label already exists + if (label in this._tempVoltFanSensors[type]) { + for (let i = 2; i <= 9; i++) { + // append an incremented number to end + let new_label = label + ' ' + i; + + // if new label is available, use it + if (!(new_label in this._tempVoltFanSensors[type])) { + label = new_label; + break; + } + } + } + } + + // update screen on initial build to prevent delay on update + this._returnValue(callback, label, value, obj['type'], obj['format']); + + this._tempVoltFanSensors[obj['type']][label] = { + 'format': obj['format'], + 'path': obj['input'] + }; + } + + resetHistory() { + this._next_public_ip_check = 0; + this._hardware_detected = false; + this._nvidia_static_returned = false; + this._processor_uses_cpu_info = true; + this._battery_time_left_history = []; + this._battery_charge_status = ''; + this._nvidia_labels = []; + this._bad_split_count = 0; + this._frameMonitorLastTime = 0; + this._frameMonitorFrameCount = 0; + this._frameMonitorAccTime = 0; + } + + destroy() { + this._destroyFrameMonitor(); + this._terminateNvidiaSmiProcess(); + + for (let signal of Object.values(this._settingChangedSignals)) + this._settings.disconnect(signal); + } +}); diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/stylesheet.css b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/stylesheet.css new file mode 100644 index 0000000..4fcd985 --- /dev/null +++ b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/stylesheet.css @@ -0,0 +1,19 @@ +.vitals-icon { icon-size: 16px; } +.vitals-menu-button-container {} +.vitals-panel-item{spacing: 0;} +.vitals-panel-menu{spacing: 11px; padding: 3px; } +.vitals-panel-icon-default {} +.vitals-panel-icon-temperature { margin: 0 1px 0 0; padding: 0; } +.vitals-panel-icon-voltage { margin: 0 0 0 0; padding: 0; } +.vitals-panel-icon-fan { margin: 0 4px 0 0; padding: 0; } +.vitals-panel-icon-memory { margin: 0 2px 0 0; padding: 0; } +.vitals-panel-icon-processor { margin: 0 3px 0 0; padding: 0; } +.vitals-panel-icon-system { margin: 0 3px 0 0; padding: 0; } +.vitals-panel-icon-network { margin: 0 3px 0 0; padding: 0; } +.vitals-panel-icon-storage { margin: 0 2px 0 0; padding: 0; } +.vitals-panel-icon-battery { margin: 0 4px 0 0; padding: 0; } +.vitals-panel-label {} +.vitals-button-action { -st-icon-style: symbolic; border-radius: 32px; margin: 0px; min-height: 22px; min-width: 22px; padding: 10px; font-size: 100%; border: 1px solid transparent; } +.vitals-button-action:hover, .vitals-button-action:focus { border-color: #777; } +.vitals-button-action > StIcon { icon-size: 16px; } +.vitals-button-box { padding: 0px; spacing: 22px; } diff --git a/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/values.js b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/values.js new file mode 100644 index 0000000..f16778c --- /dev/null +++ b/raveos-theme/gnome/theme-data/extensions/installed/Vitals@CoreCoding.com/values.js @@ -0,0 +1,358 @@ +/* + Copyright (c) 2018, Chris Monahan + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the GNOME nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +import GLib from 'gi://GLib'; +import GObject from 'gi://GObject'; + +const cbFun = (d, c) => { + let bb = d[1] % c[0], + aa = (d[1] - bb) / c[0]; + aa = aa > 0 ? aa + c[1] : ''; + + return [d[0] + aa, bb]; +}; + +export const Values = GObject.registerClass({ + GTypeName: 'Values', +}, class Values extends GObject.Object { + + _init(settings, sensorIcons) { + this._settings = settings; + this._sensorIcons = sensorIcons; + + this._networkSpeedOffset = {}; + this._networkSpeeds = {}; + + this._history = {}; + //this._history2 = {}; + this.resetHistory(); + } + + _legible(value, sensorClass) { + let unit = 1000; + if (value === null) return 'N/A'; + let use_higher_precision = this._settings.get_boolean('use-higher-precision'); + let memory_measurement = this._settings.get_int('memory-measurement') + let storage_measurement = this._settings.get_int('storage-measurement') + let use_bps = (this._settings.get_int('network-speed-format') == 1); + + let format = ''; + let ending = ''; + let exp = 0; + + var decimal = [ 'B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB' ]; + var binary = [ 'B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB' ]; + var hertz = [ 'Hz', 'KHz', 'MHz', 'GHz', 'THz', 'PHz', 'EHz', 'ZHz' ]; + + switch (sensorClass) { + case 'percent': + format = (use_higher_precision)?'%.1f%s':'%d%s'; + value = value * 100; + if (value > 100) value = 100; + ending = '%'; + break; + case 'temp': + value = value / 1000; + ending = '°C'; + + // are we converting to fahrenheit? + if (this._settings.get_int('unit') == 1) { + value = ((9 / 5) * value + 32); + ending = '°F'; + } + + format = (use_higher_precision)?'%.1f%s':'%d%s'; + break; + case 'fan': + format = '%d %s'; + ending = 'RPM'; + break; + case 'in': // voltage + value = value / 1000; + format = ((value >= 0) ? '+' : '-') + ((use_higher_precision)?'%.2f %s':'%.1f %s'); + ending = 'V'; + break; + case 'hertz': + if (value > 0) { + exp = Math.floor(Math.log(value) / Math.log(unit)); + if (value >= Math.pow(unit, exp) * (unit - 0.05)) exp++; + value = parseFloat((value / Math.pow(unit, exp))); + } + + format = (use_higher_precision)?'%.2f %s':'%.1f %s'; + ending = hertz[exp]; + break; + case 'memory': + unit = (memory_measurement)?1000:1024; + + if (value > 0) { + value *= unit; + exp = Math.floor(Math.log(value) / Math.log(unit)); + if (value >= Math.pow(unit, exp) * (unit - 0.05)) exp++; + value = parseFloat((value / Math.pow(unit, exp))); + } + + format = (use_higher_precision)?'%.2f %s':'%.1f %s'; + + if (memory_measurement) + ending = decimal[exp]; + else + ending = binary[exp]; + + break; + case 'storage': + unit = (storage_measurement)?1000:1024; + + if (value > 0) { + exp = Math.floor(Math.log(value) / Math.log(unit)); + if (value >= Math.pow(unit, exp) * (unit - 0.05)) exp++; + value = parseFloat((value / Math.pow(unit, exp))); + } + + format = (use_higher_precision)?'%.2f %s':'%.1f %s'; + + if (storage_measurement) + ending = decimal[exp]; + else + ending = binary[exp]; + + break; + case 'speed': + if (value > 0) { + if (use_bps) value *= 8; + exp = Math.floor(Math.log(value) / Math.log(unit)); + if (value >= Math.pow(unit, exp) * (unit - 0.05)) exp++; + value = parseFloat((value / Math.pow(unit, exp))); + } + + format = (use_higher_precision)?'%.1f %s':'%.0f %s'; + + if (use_bps) { + ending = decimal[exp].replace('B', 'bps'); + } else { + ending = decimal[exp] + '/s'; + } + + break; + case 'runtime': + case 'uptime': + let scale = [24, 60, 60]; + let units = ['d ', 'h ', 'm ']; + + // show seconds on higher precision or if value under a minute + if (sensorClass != 'runtime' && (use_higher_precision || value < 60)) { + scale.push(1); + units.push('s '); + } + + let rslt = scale.map((d, i, a) => a.slice(i).reduce((d, c) => d * c)) + .map((d, i) => ([d, units[i]])) + .reduce(cbFun, ['', value]); + + value = rslt[0].trim(); + + format = '%s'; + break; + case 'milliamp': + format = (use_higher_precision)?'%.1f %s':'%d %s'; + value = value / 1000; + ending = 'mA'; + break; + case 'milliamp-hour': + format = (use_higher_precision)?'%.1f %s':'%d %s'; + value = value / 1000; + ending = 'mAh'; + break; + case 'watt': + format = ( + ((value > 0) ? '+' : '') + ((use_higher_precision)?'%.2f %s':'%.1f %s') + ); + value = value / 1000000; + ending = 'W'; + break; + case 'watt-gpu': + format = (use_higher_precision)?'%.2f %s':'%.1f %s'; + ending = 'W'; + break; + case 'watt-hour': + format = (use_higher_precision)?'%.2f %s':'%.1f %s'; + value = value / 1000000; + ending = 'Wh'; + break; + case 'load': + format = (use_higher_precision)?'%.2f %s':'%.1f %s'; + break; + case 'pcie': + let split = value.split('x'); + value = 'PCIe ' + parseInt(split[0]) + (split.length > 1 ? ' x' + parseInt(split[1]) : ''); + format = '%s'; + break; + default: + format = '%s'; + break; + } + + return format.format(value, ending).trim(); + } + + returnIfDifferent(dwell, label, value, type, format, key) { + let output = []; + + // make sure the keys exist + if (!(type in this._history)) this._history[type] = {}; + + // no sense in continuing when the raw value has not changed + if (type != 'network-rx' && type != 'network-tx' && + key in this._history[type] && this._history[type][key][1] == value) + return output; + + // is the value different from last time? + let legible = this._legible(value, format); + + // don't return early when dealing with network traffic + if (type != 'network-rx' && type != 'network-tx') { + // only update when we are coming through for the first time, or if a value has changed + if (key in this._history[type] && this._history[type][key][0] == legible) + return output; + + // add label as it was sent from sensors class + output.push([label, legible, type, key]); + } + + // save previous values to update screen on changes only + let previousValue = this._history[type][key]; + this._history[type][key] = [legible, value]; + + // process average, min and max values + if (type == 'temperature' || type == 'voltage' || type == 'fan') { + let vals = Object.values(this._history[type]).map(x => parseFloat(x[1])); + + // show value in group even if there is one value present + let sum = vals.reduce((a, b) => a + b); + let avg = this._legible(sum / vals.length, format); + output.push([type, avg, type + '-group', '']); + + // If only one value is present, don't display avg, min and max + if (vals.length > 1) { + output.push(['Average', avg, type, '__' + type + '_avg__']); + + // calculate Minimum value + let min = Math.min(...vals); + min = this._legible(min, format); + output.push(['Minimum', min, type, '__' + type + '_min__']); + + // calculate Maximum value + let max = Math.max(...vals); + max = this._legible(max, format); + output.push(['Maximum', max, type, '__' + type + '_max__']); + } + } else if (type == 'network-rx' || type == 'network-tx') { + let direction = type.split('-')[1]; + + // appends total upload and download for all interfaces for #216 + let vals = Object.values(this._history[type]).map(x => parseFloat(x[1])); + let sum = vals.reduce((partialSum, a) => partialSum + a, 0); + const memUnit = this._settings.get_int('memory-measurement') ? 1000 : 1024; + output.push(['Boot ' + direction, this._legible(sum, format), type, '__' + type + '_boot__']); + + // keeps track of session start point + if (!(key in this._networkSpeedOffset) || this._networkSpeedOffset[key] <= 0) + this._networkSpeedOffset[key] = sum; + + // outputs session upload and download for all interfaces for #234 + output.push(['Session ' + direction, this._legible(sum - this._networkSpeedOffset[key], format), type, '__' + type + '_ses__']); + + // calculate speed for this interface + let speed = (value - previousValue[1]) / dwell; + output.push([label, this._legible(speed, 'speed'), type, key]); + + // store speed for Device report + if (!(direction in this._networkSpeeds)) this._networkSpeeds[direction] = {}; + if (!(label in this._networkSpeeds[direction])) this._networkSpeeds[direction][label] = 0; + + // store value for next go around + if (value > 0 || (value == 0 && !this._settings.get_boolean('hide-zeros'))) + this._networkSpeeds[direction][label] = speed; + + // calculate total upload and download device speed + for (let direction in this._networkSpeeds) { + let sumNum = 0; + for (let iface in this._networkSpeeds[direction]) + sumNum += parseFloat(this._networkSpeeds[direction][iface]); + + let sum = this._legible(sumNum, 'speed'); + output.push(['Device ' + direction, sum, 'network-' + direction, '__network-' + direction + '_max__']); + // append download speed to group itself + if (direction == 'rx') output.push([type, sum, type + '-group', '']); + } + } + +/* + global.log('before', JSON.stringify(output)); + for (let i = output.length - 1; i >= 0; i--) { + let sensor = output[i]; + // sensor[0]=label, sensor[1]=value, sensor[2]=type, sensor[3]=key) + + //["CPU Core 5","46°C","temperature","_temperature_hwmon8temp7_"] + + // make sure the keys exist + if (!(sensor[2] in this._history2)) this._history2[sensor[2]] = {}; + + if (sensor[3] in this._history2[sensor[2]]) { + if (this._history2[sensor[2]][sensor[3]] == sensor[1]) { + output.splice(i, 1); + } + } + + this._history2[sensor[2]][sensor[3]] = sensor[1]; + } + + global.log(' after', JSON.stringify(output)); + global.log('***************************'); +*/ + + return output; + } + + resetHistory(numGpus) { + // don't call this._history = {}, as we want to keep network-rx and network-tx + // otherwise network history statistics will start over + for (let sensor in this._sensorIcons) { + //each gpu has it's own sensor name and thus must be handled separately + if(sensor === 'gpu') continue; + + this._history[sensor] = {}; + this._history[sensor + '-group'] = {}; + //this._history2[sensor] = {}; + //this._history2[sensor + '-group'] = {}; + } + + for(let i = 1; i <= numGpus; i++){ + this._history['gpu#' + i] = {}; + this._history['gpu#' + i + '-group'] = {}; + } + } +}); -- cgit v1.3