commit 64387e3e5f37adce05d6184907b1f11c04673649 Author: Surferlul Date: Sun Jul 4 00:06:52 2021 +0200 initial commit diff --git a/.build.yml b/.build.yml new file mode 100644 index 0000000..c58e847 --- /dev/null +++ b/.build.yml @@ -0,0 +1,9 @@ +image: alpine/edge +packages: + - luacheck +sources: + - https://github.com/vicious-widgets/vicious +tasks: + - check: | + cd vicious + luacheck --config=tools/luacheckrc . diff --git a/CHANGELOG.rst b/CHANGELOG.rst new file mode 100644 index 0000000..af8c84d --- /dev/null +++ b/CHANGELOG.rst @@ -0,0 +1,151 @@ +Changelog +========= + +Changes in 2.5.0 +---------------- + +Fixed: + +- ``vicious.call`` freezing awesome when used with asynchronous widget types + +Added: + +- ``vicious.call_async`` asynchronous analogous to ``vicious.call`` + +Moved: + +- Most of the documentation in READMEs to ``docs/`` +- ``Changes.md`` to ``CHANGELOG.rst`` +- ``CONTRIBUTING.md`` to ``CONTRIBUTING.rst`` +- Meta helpers to ``tools/`` + +Changes in 2.4.2 +---------------- + +Feature: [hwmontemp] Bring back sysfs path cache + +Changes in 2.4.1 +---------------- + +Fixed: + +- [pkg] Fallback the number of lines before packages listing to 0. + This fixes crashes on Arch, FreeBSD and Mandriva. +- [mdir] Remove trailing semicolon at the end of command. + +Changes in 2.4.0 +---------------- + +.. important:: + + ``volume`` now uses 🔉 and 🔈 instead of ♫ and ♩ to show mute state. + This BREAKS backward compatibility if users substitute custom symbols + from these default. + +Added: + +- notmuch_all, cpu_freebsd widget types. +- [cmus_all] Promote to ``widgets/``. +- [wifiiw_linux] Expose BSSID. +- [wifi_linux] Expose frequency and transmission power. +- ``spawn`` as a fallback for ``awful.spawn`` in case Vicious is used as + a stand-alone library. This wrapper, however, does NOT provide the facilities + to asynchronously spawn new processes. It also lacks a few features such as + parsing ``stderr`` and returning PID. +- ``helpers.setasyncall`` to avoid writing redundant workers for asynchronous + widget types. Note that these workers are only needed in case Vicious is used + as a stand-alone library. +- ``helpers.setcall`` for registering functions as widget types. +- ``headergen`` script for automatic generation of copyright notices. +- ``templates`` for the ease of adding new widget types. +- ``CONTRIBUTING.md`` which guide contributors through the steps + of filing an issue or submitting a patch. + +Fixed: + +- Deprecate the use of ``io.popen`` in following widgets: + + - wifi_linux, wifiiw_linux, hwmontemp_linux, hddtemp_linux + - bat_freebsd, mem_freebsd, net_freebsd, thermal_freebsd, uptime_freebsd, + - cpu_freebsd, cpufreq_freebsd, fanspeed_freebsd + - bat_openbsd + - volume, gmail, mdir, mpd, fs + +- [mpd] Lua 5.3 compatibility (for real this time); also correct a typo +- [mbox] Update the deprecated ``string.gfind`` to ``string.gmatch`` +- [pkg,weather,contrib/btc] Allow function call without Awesome +- [pkg] Use more updated front-ends for Debian/Ubuntu (apt) and Fedora (dnf) +- [os] Splitted os_all into os_linux and os_bsd (and refactored to async) +- Tweak ``.luacheckrc`` to suit functional style and soft-limit text width to 80 +- Update copyright headers for libraries and widget types + +Removed: + +- ``helpers.sysctl`` and ``helpers.sysctl_table`` were removed in favour of + ``helpers.sysctl_async``. + +Changes in 2.3.3 +---------------- + +Feature: Add battery widget type for OpenBSD + +Fixes: + +- [mpd] Lua 5.3 compatibility +- [bat_freebsd] Update battery state symbols + +Changes in 2.3.2 +---------------- + +Features: + +- Support stacked graphs +- [hwmontemp_linux] Provide name-based access to hwmon sensors via sysfs +- [mpd_all] Expose more informations and format time in [hh:]mm:ss + +Fixes: + +- Improve defaults and mechanism for data caching +- Escape XML entities in results by default +- [weather_all] Update NOAA link and use Awesome asynchronous API +- [mem_linux] Use MemAvailable to calculate free amount +- [mem_freebsd] Correct calculation and switch to swapinfo for swap +- [bat_freebsd] Add critical charging state +- [fs_all] Fix shell quoting of option arguments + +Moreover, ``.luacheckrc`` was added and ``README.md`` was refomatted +for the ease of development. + +Changes in 2.3.1 +---------------- + +Fixes: + +- widgets can be a function again (regression introduced in 2.3.0) + +Changes in 2.3.0 +---------------- + +Features: + +- add btc widget +- add cmus widget +- alsa mixer also accept multiple arguments + +Fixes: + +- pkg now uses non-blocking asynchronous api + +Changes in 2.2.0 +---------------- + +Notable changes: + +- moved development from git.sysphere.org/vicious to github.com/Mic92/vicious +- official freebsd support +- escape variables before passing to shell +- support for gear timers +- fix weather widget url +- add :lua:func:`vicious.call` method to obtain data outside of widgets + +For older versions please see ``git log``. diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst new file mode 100644 index 0000000..cc9ff3e --- /dev/null +++ b/CONTRIBUTING.rst @@ -0,0 +1,344 @@ +Contribution Guidelines +======================= + +Filing an Issue +--------------- + +* Ensure the bug was not already reported by searching GitHub Issues. +* If you're unable to find an open issue addressing the problem, + open a new one. Be sure to include a title and clear description, + as much relevant information as possible, such as Awesome errors, + and a config sample or an executable test case + (using Vicious as a stand-alone library) + demonstrating the expected behavior that is not occurring. + +Please re-read your issue once again to avoid a couple of common mistakes +(you can and should use this as a checklist): + +* Is the description of the issue itself sufficient? + Make sure that it's obvious + + * What the problem is + * How it could be fixed + * How your proposed solution would look like + +* Have you provide the versions of Vicious and related software? + We would like to how you installed Vicious, which OS you're using, + the version of the software or what kind of hardware you are trying + to get information from. +* Is the issue already documented? +* Does the issue involve one problem, and one problem only? + Some people seem to think there is a limit of issues they can or should open. + There is no limit of issues they can or should open. + While it may seem appealing to be able to dump all your issues + into one ticket, that means that someone who solves one of your issues + cannot mark the issue as closed. +* Is anyone going to need the feature? Only post features that you + (or an incapacitated friend you can personally talk to) require. + Do not post features because they seem like a good idea. + If they're really useful, they'll be requested by someone who requires them. + +Requesting for Merging a Patch +------------------------------ + +#. `Fork this repository`_ +#. Check out the source code with:: + + git clone https://github.com/YOUR_GITHUB_USERNAME/vicious.git + cd vicious + +#. Start working on your patch. If you want to add a new widget type, + see the ``templates`` directory for a more detailed guide. +#. Have a look at ``helpers.lua`` and ``spawn.lua`` + for possible helper functions. +#. Make sure your code follows the coding conventions below and check the code + with ``luacheck``. This *should fail* at first, but you can continually + re-run it until you're done:: + + luacheck --config tools/luacheckrc . + +#. Make sure your code works under all Lua versions claimed supported + by Vicious, namely 5.1, 5.2 and 5.3. +#. Update the copyright notices of the files you modified. Vicious is + collectively licensed under GPLv2+, and to protect the freedom of the users, + copyright holders need to be properly documented. +#. Try to note your changes under ``CHANGELOG.rst``. If you find it is + difficult to phrase the changes, you can leave it for us. +#. Add_ the changes, commit_ them and push_ the result, like this:: + + git add widgets/bar_baz.lua README.md + git commit -m '[bar_baz] Add widget type' + git add helpers.lua CHANGELOG.rst + git commit -m '[helpers] Fix foo' + git push + +#. Finally, `create a pull request`_. We'll then review and merge it. + +In any case, thank you very much for your contributions! + +Coding Conventions +------------------ + +This section introduces a guideline for writing idiomatic, robust +and future-proof widget type code. + +Whitespace in Expressions and Statements +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Avoid extraneous whitespace in the following situations: + +* Immediately inside parentheses or brackets. Braces, however, are exceptions + to this rule: + + .. code-block:: lua + + foo(bar[1], { baz = 2 }) -- yes + foo( bar[ 1 ], {baz = 2} ) -- no + +* Immediately before a comma, semicolon, or colon. +* Immediately before the open parenthesis, braces, quote, etc. + that starts the argument list of a function call; or the open bracket + that starts an indexing. In other words, prefer these: + + .. code-block:: lua + + foo(bar, baz) + foo{ bar, baz } + foo"bar" + foo[[bar]] + foo[bar] + +* Trailing at the end of line or (newline) at the end of file. + +Always surround these binary operators with a single space on either side: +assignment (``=``), comparisons, Booleans (``and``, ``or``, ``not``). +If operators with different priorities are used, consider adding whitespace +around the operators with the lowest priorities. Use your own judgment; +however, never use more than one space, and always have +the same amount of whitespace on both sides of a binary operator. + +Indentation +^^^^^^^^^^^ + +Use 4 *spaces* per indentation level. + +Continuation lines should align wrapped elements either vertically +inside parentheses, brackets and braces, or using a hanging indent +(the opening parenthesis of a parenthesized statement is the last +non-whitespace character of the line, with subsequent lines being indented +until the closing parenthesis), e.g. + +.. code-block:: lua + + -- Vertically aligned + long_function_call{ foo, bar, + baz } + + -- Hanging indentation + long_function_call( + foo, bar + baz) + +The closing brace or bracket on multi-line constructs may either line up under +the first character of the line that starts the construct, as in: + +.. code-block:: lua + + long_function_call{ + foo = 1, bar = 2, + baz = 3, + } + +In this case, and this case only, the trailing comma is acceptable +to avoid diff noises when more values are added, +but since Vicious often deal with system APIs which rarely ever change, +it's occasionally helpful to do so. + +Trailing right parentheses, however, are not allowed. + +Maximum Line Length +^^^^^^^^^^^^^^^^^^^ + +If possible, try to limit all *code* lines to a maximum +of 80 characters. In case you find some lines in your patch would be +more readable exceeding this limit, feel free to discuss with us. +Comments and long strings need not to follow this restriction however. + +As one might have noticed, the syntactic sugars ``f{}`` +(for ``f({})``) and ``f''`` +(or ``f""``/``f[[]]``, for ``f('')``) +are especially preferred to squeeze the line length to this limit. + +Blank Lines +^^^^^^^^^^^ + +Surround function definitions with a single blank line. Extra blank lines +may be used (sparingly) to separate groups of related functions. +Blank lines may be omitted between a bunch of related one-liners +(e.g. a set of dummy implementations). +Use blank lines in functions, sparingly, to indicate logical sections. + +Requiring Libraries +^^^^^^^^^^^^^^^^^^^ + +All standard libraries should be localized before used +for the matter of performance. + +``require``'s should always be put at the top of the source file, +just after the copyright header, and before module globals and constants, +and grouped in the following order: + +1. Standard libraries +2. Related third-party libraries +3. Local libraries + +For example, + +.. code-block:: lua + + local type = type + local table = { concat = table.concat, insert = table.insert } + + local awful = require("awful") + + local helpers = require("vicious.helpers") + +String Quotes +^^^^^^^^^^^^^ + +In Lua, single-quoted strings and double-quoted strings are the same, +so the choice is totally up to you, but please be consistent within a module. +When a string contains single or double quote characters, however, +use the other one to avoid backslashes in the string. It improves readability: + +.. code-block:: lua + + '"key": "value"' -- good + "\"key\": \"value\"" -- no good + +It is preferable to add a newline immediately after the opening long bracket: + +.. code-block:: lua + + foo = [[ + this is a really, + really, + really long text]] + +Naming Conventions +^^^^^^^^^^^^^^^^^^ + +Avoid using the characters ``l`` (lowercase letter el), +``O`` (uppercase letter oh), or ``I`` (uppercase letter eye) +as single character variable names. In some fonts, these characters +are indistinguishable from the 1's and 0's. + +Constants +""""""""" + +Constants are usually defined on a module level +and written in all capital letters with underscores separating words. +Examples include ``MAX_OVERFLOW`` and ``TOTAL``. + +Function and Variable Names +""""""""""""""""""""""""""" + +Function names should be lowercase, with words separated by underscores +as necessary to improve readability. + +Variable names follow the same convention as function names. + +When you find it difficult to give descriptive names, +use the functions and variable anonymously. + +Performance Tips +^^^^^^^^^^^^^^^^ + +Vicious is meant to be run as part of the Awesome window manager, +thus any little overhead may defect the responsiveness of the UI. +While Lua is famous for its performance, there are a few things +one can do to make use of all of its power. + +**Never** use global variables. This includes the standard libraries, +which, again, must be localized before use. Remember, every widget type +is to be called repeatedly every few seconds. + +Use closures when possible: + +* Define constants on the module level. +* Avoid re-fetching the values that are not not meant to change. + +However, declare a variable only when you need it, to avoid declaring it +without an initial value (and therefore you seldom forget to initialize it). +Moreover, you shorten the scope of the variable, which increases readability. + +Copyright Header +^^^^^^^^^^^^^^^^ + +Vicious is released under the GNU GNU General Public License +version 2 or later and each contributor holds the copyright +on their contributions. To make this collective control effective, +each source file must include a notice of the following format +denoting the name of all authors + +.. code-block:: lua + + -- + -- Copyright (C) <> + -- + -- This file is part of Vicious. + -- + -- Vicious 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. + -- + -- Vicious 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 Vicious. If not, see . + +Comments +^^^^^^^^ + +Comments that contradict the code are worse than no comments. +Always make a priority of keeping the comments up-to-date when the code changes! + +You should use two spaces after a sentence-ending period +in multi-sentence comments, except after the final sentence. + +Block Comments +"""""""""""""" + +Block comments generally apply to some (or all) code that follows them, +and are indented to the same level as that code. Each line of a block comment +starts with ``--`` and a single space, unless text inside the comment +is indented, or it is to comment out code. + +Paragraphs inside a block comment are separated by a line containing +``--`` only. The best example is the copyright notice in the section above. + +The ``--[[...]]`` style may only be used for commenting out source code. + +Inline Comments +""""""""""""""" + +An inline comment is a comment on the same line as a statement. +Inline comments should be separated by at least two spaces from the statement. +They should start with ``--`` and one single space. + +Influences +---------- + +These contributing guideline are heavily influenced by that of ``youtube-dl``, +PEP 8, Programming in Lua and the performance tips in Lua Programming Gems. + +.. _Fork this repository: https://github.com/vicious-widgets/vicious/fork +.. _Add: https://git-scm.com/docs/git-add +.. _commit: https://git-scm.com/docs/git-commit +.. _push: https://git-scm.com/docs/git-push +.. _create a pull request: + https://help.github.com/articles/creating-a-pull-request diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d511905 --- /dev/null +++ b/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. + + + Copyright (C) + + 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. + + , 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/README.md b/README.md new file mode 100644 index 0000000..8ffc64f --- /dev/null +++ b/README.md @@ -0,0 +1,30 @@ +# Vicious + +Vicious is a modular widget library for window managers, but mostly +catering to users of the *awesome* window manager. It was derived from +the old *Wicked* widget library, and has some of the old *Wicked* widget +types, a few of them rewritten, and a good number of new ones. + +Vicious widget types are a framework for creating your own +widgets. Vicious contains modules that gather data about your system, +and a few *awesome* helper functions that make it easier to register +timers, suspend widgets and so on. Vicious doesn't depend on any third party +Lua libraries, but may depend on additional system utilities (see widget +description). + +## Usage + +Please see our [online documentation] for detail instructions. +It is also available under the `docs` directory for offline reference. + +## Copying + +Vicious 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. + +Please refer to our documentation for the full [list of authors]. + +[online documentation]: https://vicious.rtfd.io +[list of authors]: https://vicious.rtfd.io/copying.html diff --git a/TODO b/TODO new file mode 100644 index 0000000..3eb3a3c --- /dev/null +++ b/TODO @@ -0,0 +1,35 @@ +#-*- mode: org -*- +#+TYP_TODO: TODO MAYBE CANCEL WAITING NEXT NOTE DONE +#+STARTUP: showall +#+STARTUP: hidestars + + +* Vicious +** TODO Implement intelligent multigraph support +** TODO Expand raid to grab data for all available devices +** TODO Consider commiting power drain support to bat.lua +** TODO Document contrib widgets in contrib/README +** TODO Complete the hddtemp fix + - In certain setups regexp does not match all devices + - The regexp catches the first device name, but last stats + - Shortening the match introduced new problems IIRC +** TODO Add fan speed to thermal.lua +** TODO Fix contrib/sensors for Ian + - > it does work and provides the lm_sensors + > information but only for one cpu core. +** TODO Return values of type number in NET and FS + - Note: tonumber() strips decimal points +** MAYBE Simplify scrolling using negative margins + fixed width +** TODO Try to simplify truncating with a fixed width +** NOTE Changelog header +--------------------------------------------------- +Full changelog is available online: + http://git.sysphere.org/vicious/log/?showmsg=1 +--------------------------------------------------- + + +* Git +** DONE Git hook post-update not executed on git push +** DONE Git post-update hook does not leave info/refs with correct permissions +** DONE Git persmission are incorrect since 1.6.5, does not honor umask +** TODO Git smart http transport with cgit diff --git a/contrib/ac_linux.lua b/contrib/ac_linux.lua new file mode 100644 index 0000000..7d13bfe --- /dev/null +++ b/contrib/ac_linux.lua @@ -0,0 +1,49 @@ +-- contrib/ac_linux.lua +-- Copyright (C) 2012 jinleileiking +-- Copyright (C) 2017 Jörg Thalheim +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local tonumber = tonumber +local setmetatable = setmetatable +local string = { format = string.format } +local helpers = require("vicious.helpers") +local math = { + min = math.min, + floor = math.floor +} +-- }}} + +local ac_linux = {} + +-- {{{ AC widget type +local function worker(format, warg) + local ac = helpers.pathtotable("/sys/class/power_supply/"..warg) + + local state = ac.online + if state == nil then + return {"N/A"} + elseif state == "1\n" then + return {"On"} + else + return {"Off"} + end +end +-- }}} + + +return setmetatable(ac_linux, { __call = function(_, ...) return worker(...) end }) diff --git a/contrib/ati_linux.lua b/contrib/ati_linux.lua new file mode 100644 index 0000000..0dd4100 --- /dev/null +++ b/contrib/ati_linux.lua @@ -0,0 +1,93 @@ +-- contrib/ati_linux.lua +-- Copyright (C) 2013 NormalRa +-- Copyright (C) 2017 Jörg Thalheim +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local tonumber = tonumber +local io = { open = io.open } +local setmetatable = setmetatable +local helpers = require("vicious.helpers") +local string = { + sub = string.sub, + match = string.match, + gmatch = string.gmatch +} +-- }}} + + +-- ATI: provides various info about ATI GPU status +-- vicious.contrib.ati +local ati_linux = {} + + +-- {{{ Define variables +local _units = { clock = { ["khz"] = 1, ["mhz"] = 1000 }, + voltage = { ["v"] = 1, ["mv"] = 1000 } } +local _reps = { + ["sclk"] = { name = "engine_clock", units = _units.clock, mul = 10 }, + ["mclk"] = { name = "memory_clock", units = _units.clock, mul = 10 }, + ["vddc"] = { name = "voltage", units = _units.voltage }, + ["voltage"] = { name = "voltage", units = _units.voltage }, + ["current engine clock"] = { name = "engine_clock", units = _units.clock }, + ["current memory clock"] = { name = "memory_clock", units = _units.clock } +} +-- }}} + +-- {{{ ATI widget type +local function worker(format, warg) + if not warg then return end + + local pm = helpers.pathtotable("/sys/class/drm/"..warg.."/device") + local _data = {} + + -- Get power info + _data["{method}"] = + pm.power_method and string.sub(pm.power_method, 1, -2) or "N/A" + _data["{dpm_state}"] = + pm.power_dpm_state and string.sub(pm.power_dpm_state, 1, -2) or "N/A" + _data["{dpm_perf_level}"] = + pm.power_dpm_force_performance_level and + string.sub(pm.power_dpm_force_performance_level, 1, -2) or "N/A" + _data["{profile}"] = + pm.power_profile and string.sub(pm.power_profile, 1, -2) or "N/A" + + local f = io.open("/sys/kernel/debug/dri/64/radeon_pm_info", "r") + if f then -- Get ATI info from the debug filesystem + for line in f:lines() do + for k, unit in string.gmatch(line, "(%a+[%a%s]*):[%s]+([%d]+)") do + unit = tonumber(unit) + + _data["{dpm_power_level}"] = -- DPM active? + tonumber(string.match(line, "power level ([%d])")) or "N/A" + + if _reps[k] then + for u, v in pairs(_reps[k].units) do + _data["{".._reps[k].name.." "..u.."}"] = + (unit * (_reps[k].mul or 1)) / v + end + end + end + end + f:close() + end + + return _data +end +-- }}} + +return setmetatable(ati_linux, { __call = function(_, ...) return worker(...) end }) diff --git a/contrib/batpmu_linux.lua b/contrib/batpmu_linux.lua new file mode 100644 index 0000000..85b2eba --- /dev/null +++ b/contrib/batpmu_linux.lua @@ -0,0 +1,93 @@ +-- contrib/batpmu_linux.lua +-- Copyright (C) 2010 Adrian C. +-- Copyright (C) 2017 Jörg Thalheim +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local tonumber = tonumber +local io = { open = io.open } +local setmetatable = setmetatable +local math = { + min = math.min, + floor = math.floor +} +local string = { + find = string.find, + match = string.match, + format = string.format +} +-- }}} + + +-- Batpmu: provides state, charge and remaining time for a requested battery using PMU +-- vicious.contrib.batpmu +local batpmu_linux = {} + + +-- {{{ Battery widget type +local function worker(format, batid) + local battery_state = { + ["full"] = "↯", + ["unknown"] = "⌁", + ["00000013"] = "+", + ["00000011"] = "-" + } + + -- Get /proc/pmu/battery* state + local f = io.open("/proc/pmu/" .. batid) + -- Handler for incompetent users + if not f then return {battery_state["unknown"], 0, "N/A"} end + local statefile = f:read("*all") + f:close() + + -- Get /proc/pmu/info data + local f = io.open("/proc/pmu/info") + local infofile = f:read("*all") + f:close() + + -- Check if the battery is present + if infofile == nil or string.find(infofile, "Battery count[%s]+:[%s]0") then + return {battery_state["unknown"], 0, "N/A"} + end + + + -- Get capacity and charge information + local capacity = string.match(statefile, "max_charge[%s]+:[%s]([%d]+).*") + local remaining = string.match(statefile, "charge[%s]+:[%s]([%d]+).*") + + -- Calculate percentage + local percent = math.min(math.floor(remaining / capacity * 100), 100) + + + -- Get timer information + local timer = string.match(statefile, "time rem%.[%s]+:[%s]([%d]+).*") + if timer == "0" then return {battery_state["full"], percent, "N/A"} end + + -- Get state information + local state = string.match(statefile, "flags[%s]+:[%s]([%d]+).*") + local state = battery_state[state] or battery_state["unknown"] + + -- Calculate remaining (charging or discharging) time + local hoursleft = math.floor(tonumber(timer) / 3600) + local minutesleft = math.floor((tonumber(timer) / 60) % 60) + local time = string.format("%02d:%02d", hoursleft, minutesleft) + + return {state, percent, time} +end +-- }}} + +return setmetatable(batpmu_linux, { __call = function(_, ...) return worker(...) end }) diff --git a/contrib/batproc_linux.lua b/contrib/batproc_linux.lua new file mode 100644 index 0000000..b6b5e35 --- /dev/null +++ b/contrib/batproc_linux.lua @@ -0,0 +1,100 @@ +-- contrib/batproc_linux.lua +-- Copyright (C) 2010 Adrian C. +-- Copyright (C) 2017 Jörg Thalheim +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local tonumber = tonumber +local io = { open = io.open } +local setmetatable = setmetatable +local math = { + min = math.min, + floor = math.floor +} +local string = { + find = string.find, + match = string.match, + format = string.format +} +-- }}} + + +-- Batproc: provides state, charge, and remaining time for a requested battery using procfs +-- vicious.contrib.batproc +local batproc_linux = {} + + +-- {{{ Battery widget type +local function worker(format, batid) + local battery_state = { + ["full"] = "↯", + ["unknown"] = "⌁", + ["charged"] = "↯", + ["charging"] = "+", + ["discharging"] = "-" + } + + -- Get /proc/acpi/battery info + local f = io.open("/proc/acpi/battery/"..batid.."/info") + -- Handler for incompetent users + if not f then return {battery_state["unknown"], 0, "N/A"} end + local infofile = f:read("*all") + f:close() + + -- Check if the battery is present + if infofile == nil or string.find(infofile, "present:[%s]+no") then + return {battery_state["unknown"], 0, "N/A"} + end + + -- Get capacity information + local capacity = string.match(infofile, "last full capacity:[%s]+([%d]+).*") + + + -- Get /proc/acpi/battery state + local f = io.open("/proc/acpi/battery/"..batid.."/state") + local statefile = f:read("*all") + f:close() + + -- Get state information + local state = string.match(statefile, "charging state:[%s]+([%a]+).*") + local state = battery_state[state] or battery_state["unknown"] + + -- Get charge information + local rate = string.match(statefile, "present rate:[%s]+([%d]+).*") + local remaining = string.match(statefile, "remaining capacity:[%s]+([%d]+).*") + + + -- Calculate percentage (but work around broken BAT/ACPI implementations) + local percent = math.min(math.floor(remaining / capacity * 100), 100) + + -- Calculate remaining (charging or discharging) time + if state == "+" then + timeleft = (tonumber(capacity) - tonumber(remaining)) / tonumber(rate) + elseif state == "-" then + timeleft = tonumber(remaining) / tonumber(rate) + else + return {state, percent, "N/A"} + end + local hoursleft = math.floor(timeleft) + local minutesleft = math.floor((timeleft - hoursleft) * 60 ) + local time = string.format("%02d:%02d", hoursleft, minutesleft) + + return {state, percent, time} +end +-- }}} + +return setmetatable(batproc_linux, { __call = function(_, ...) return worker(...) end }) diff --git a/contrib/btc_all.lua b/contrib/btc_all.lua new file mode 100644 index 0000000..804883b --- /dev/null +++ b/contrib/btc_all.lua @@ -0,0 +1,70 @@ +-- contrib/btc_all.lua +-- Copyright (C) 2017 0x5b +-- Copyright (C) 2017 Joerg Thalheim +-- Copyright (C) 2019 Nguyễn Gia Phong +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local pcall = pcall +local helpers = require("vicious.helpers") +local spawn = require("vicious.spawn") + +local success, json = pcall(require, "cjson") +if not success then + json = require("json") +end + +local string = { + sub = string.sub, + upper = string.upper, +} +-- }}} + + +-- Btc: provides current bitcoin price +-- vicious.widgets.btc +local btc_all = {} + + +-- {{ Bitcoin widget type +function btc_all.async(format, warg, callback) + -- Default values + if not warg then warg = "usd" end + + local btc = { ["{price}"] = "N/A" } + local currency_code = string.upper(warg) + local url = "https://api.coindesk.com/v1/bpi/currentprice/" .. currency_code .. ".json" + local cmd = "curl "..helpers.shellquote(url) + + -- {{ Checking response + local function parse(response) + -- If 'response' is not json, 'json.decode' will return Error + local status, data = pcall(function() return json.decode(response) end) + if not status or not data then + return btc + end + + btc["{price}"] = string.sub(data["bpi"][currency_code]["rate"], 0, -3) + return btc + end + -- }} + + spawn.easy_async(cmd, function(stdout) callback(parse(stdout)) end) +end +-- }}} + +return helpers.setasyncall(btc_all) diff --git a/contrib/buildbot_all.lua b/contrib/buildbot_all.lua new file mode 100644 index 0000000..41ca126 --- /dev/null +++ b/contrib/buildbot_all.lua @@ -0,0 +1,197 @@ +-- contrib/buildbot_all.lua +-- Copyright (C) 2012 Andrzje Bieniek +-- Copyright (C) 2017 Jörg Thalheim +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local setmetatable = setmetatable +local pcall = pcall +local json_status, json = pcall(require, "json") +local io = { popen = io.popen } +local pairs = pairs +local assert = assert +-- }}} + +local bb = {} --list of all buildbot builders + +local bs = {OK=1, FAILED=2, RUNNING=3} +local bc = {"green", "red", "yellow"} + +local buildbot_all = {} + +BB = {} +BB.__index = BB + +function BB.create(url, builder) + local b = {} + setmetatable(b,BB) + b.url = url -- buildbot url + b.builder = builder -- builder name + b.lastChecked = 0 -- last checked build number + b.lastSuccessful = 0 -- last successful build number + b.lastResult = nil -- last json parsed result + b.lastError = nil -- last error string or nil if no error + return b +end + +function BB:_queryBuildbot(build_number) + local f = io.popen("curl --connect-timeout 1 "..self.url.."/json/builders/"..self.builder.."/builds/"..build_number) + local jsbuilder = f:read("*all") + f:close() + if #jsbuilder == 0 then + return false, "can't read from url" + end + + local result_status, result = pcall(json.decode, jsbuilder, false) + if not result_status then + return false, "can't parse json data" + end + return true, result +end + +function BB:_getBuildStatus(result) + if #result['text'] > 0 then + local text = result['text'] + if text[1] == "build" and text[2] == "successful" and #text == 2 then + --successful + return bs.OK + else + --failed + return bs.FAILED + end + else + --in progress + return bs.RUNNING + end +end + +-- Function queries buildbot to refresh builds status. +-- * if build is successful or failed it will not be queried again, number is stored in lasteChecked +-- * up to 10 last builds will be checked to find last successful build +function BB:refresh() + local last_pass_fail = 0 + local nr = -1 + local last_result + local iter_counter = 0 + + self.lastError = nil + self.lastResult = nil + --- there is a gap to fill in, iterate all not checked builds starting from latest + while nr > self.lastChecked or nr == -1 do + local r_status, r = self:_queryBuildbot(nr) + local s + + if not r_status then + self.lastError = r + return + end + + s = self:_getBuildStatus(r) + if not last_result then + last_result = r + end + nr = r['number'] + assert(nr > 0) + if last_pass_fail == 0 and (s == bs.OK or s == bs.FAILED) then + last_pass_fail = nr + end + if s == bs.OK then --successful + self.lastSuccessful = nr + break; + end + nr = nr - 1 + iter_counter = iter_counter + 1 + if iter_counter > 10 then --check max last 10 builds when searching for successful build + break; + end + end + if last_pass_fail ~= 0 then + self.lastChecked = last_pass_fail + end + if last_result then + self.lastResult = last_result + end +end + +function BB:getLastSuccessful() + return self.lastSuccessful +end + +function BB:getCurrent() + return self.lastResult['number'] +end + +function BB:getCurrentStatus() + return self:_getBuildStatus(self.lastResult) +end + +function BB:getBuilder() + return self.builder +end + +function BB:getError() + return self.lastError +end + + +local function getBuilderStatus(b) + local s = "[" .. b:getBuilder() + --check if json library was loaded correctly + if not json_status then + return s .. ".can't find libluaX.X-json]" + end + + local err = b:getError() + if err then + return s .. "." .. err .. "]" + end + + if b:getLastSuccessful() ~= 0 then + success_build_nr_str = "".. b:getLastSuccessful() .."" + else + success_build_nr_str = "-" + end + + local current_build_color = bc[b:getCurrentStatus()] + current_build_nr_str = ""..b:getCurrent().."" + + if current_build_color ~= "green" then + s = s .. "." .. current_build_nr_str + end + return s .. "." .. success_build_nr_str .. "]" +end + + +-- {{{ Buildbot widget type +local function worker(format, warg) + if #bb == 0 then --fill up bb with builders when worker function is run for the first time + for i,v in pairs(warg) do + bb[#bb+1] = BB.create(v["url"], v["builder"]) + end + end + + local str = "" + for i,v in pairs(bb) do + v:refresh() + str = str .. " " .. getBuilderStatus(v) + end + return {str .. " "} +end +-- }}} + +setmetatable(buildbot_all, { __call = function(_, ...) return worker(...) end }) + diff --git a/contrib/cmus_all.lua b/contrib/cmus_all.lua new file mode 120000 index 0000000..4ba2bda --- /dev/null +++ b/contrib/cmus_all.lua @@ -0,0 +1 @@ +../widgets/cmus_all.lua \ No newline at end of file diff --git a/contrib/countfiles_all.lua b/contrib/countfiles_all.lua new file mode 100644 index 0000000..6bfdffc --- /dev/null +++ b/contrib/countfiles_all.lua @@ -0,0 +1,64 @@ +-- contrib/countfiles_all.lua +-- Copyright (C) 2017 Jörg Thalheim +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local io = { popen = io.popen } +local setmetatable = setmetatable +local pairs = pairs +-- }}} + +-- countfiles: provides a number of files in several directories +-- @warg.paths a table with the paths which should be checked +-- @warg.pattern a global regex to match files (Default: match all) +-- use posix-egrep style instead of the default (less familiar) emacs regex + +-- Be carefull with directories, who contains a mass of files. +-- "find" is usally fast, but will also produce delays, if the inodes get to big. +-- So if you want to count your music library, you may want to use locate/updatedb instead. + +-- vicious.contrib.countfiles +local countfiles_all = {} + +-- {{{ Sum up widget type +local function worker(format, warg) + if not warg then return end + -- Initialise counter table + local store = {} + + -- Match by default all files + warg.pattern = warg.pattern or ".*" + + for key, value in pairs(warg.paths) do + local f = io.popen("find '"..value.."'".. + " -type f -regextype posix-egrep".. + " -regex '"..warg.pattern.."'") + + local lines = 0 + for line in f:lines() do + lines = lines + 1 + end + + store[key] = (store[key] or 0) + lines + + f:close() + end + return store +end +-- }}} + +setmetatable(countfiles_all, { __call = function(_, ...) return worker(...) end }) diff --git a/contrib/dio_linux.lua b/contrib/dio_linux.lua new file mode 100644 index 0000000..780157d --- /dev/null +++ b/contrib/dio_linux.lua @@ -0,0 +1,87 @@ +-- contrib/dio_linux.lua +-- Copyright (C) 2010, Adrian C. +-- Copyright (C) 2017 Jörg Thalheim +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local ipairs = ipairs +local setmetatable = setmetatable +local table = { insert = table.insert } +local string = { gmatch = string.gmatch } +local helpers = require("vicious.helpers") +-- }}} + + +-- Disk I/O: provides I/O statistics for requested storage devices +-- vicious.contrib.dio +local dio_linux = {} + + +-- Initialize function tables +local disk_usage = {} +local disk_total = {} +-- Variable definitions +local unit = { ["s"] = 1, ["kb"] = 2, ["mb"] = 2048 } + +-- {{{ Disk I/O widget type +local function worker(format, disk) + if not disk then return end + + local disk_lines = { [disk] = {} } + local disk_stats = helpers.pathtotable("/sys/block/" .. disk) + + if disk_stats.stat then + local match = string.gmatch(disk_stats.stat, "[%s]+([%d]+)") + for i = 1, 11 do -- Store disk stats + table.insert(disk_lines[disk], match()) + end + end + + -- Ensure tables are initialized correctly + local diff_total = { [disk] = {} } + if not disk_total[disk] then + disk_usage[disk] = {} + disk_total[disk] = {} + + while #disk_total[disk] < #disk_lines[disk] do + table.insert(disk_total[disk], 0) + end + end + + for i, v in ipairs(disk_lines[disk]) do + -- Diskstats are absolute, substract our last reading + diff_total[disk][i] = v - disk_total[disk][i] + + -- Store totals + disk_total[disk][i] = v + end + + -- Calculate and store I/O + helpers.uformat(disk_usage[disk], "read", diff_total[disk][3], unit) + helpers.uformat(disk_usage[disk], "write", diff_total[disk][7], unit) + helpers.uformat(disk_usage[disk], "total", diff_total[disk][7] + diff_total[disk][3], unit) + + -- Store I/O scheduler + if disk_stats.queue and disk_stats.queue.scheduler then + disk_usage[disk]["{sched}"] = string.gmatch(disk_stats.queue.scheduler, "%[([%a]+)%]") + end + + return disk_usage[disk] +end +-- }}} + +return setmetatable(dio_linux, { __call = function(_, ...) return worker(...) end }) diff --git a/contrib/init.lua b/contrib/init.lua new file mode 100644 index 0000000..a59179d --- /dev/null +++ b/contrib/init.lua @@ -0,0 +1,31 @@ +-- contrib/init.lua +-- Copyright (C) 2010-2012 Adrian C. (anrxc) +-- Copyright (C) 2011 Joerg T. (Mic92) +-- Copyright (C) 2012 Arvydas Sidorenko +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Setup environment +local setmetatable = setmetatable +local wrequire = require("vicious.helpers").wrequire + +-- Vicious: widgets for the awesome window manager +-- vicious.contrib +local contrib = { _NAME = "vicious.contrib" } +-- }}} + +-- Load modules at runtime as needed +return setmetatable(contrib, { __index = wrequire }) diff --git a/contrib/mpc_all.lua b/contrib/mpc_all.lua new file mode 100644 index 0000000..4660ffb --- /dev/null +++ b/contrib/mpc_all.lua @@ -0,0 +1,63 @@ +-- contrib/mpc_all.lua +-- Copyright (C) 2009 Lucas de Vries +-- Copyright (C) 2010 Adrian C. +-- Copyright (C) 2017 Jörg Thalheim +-- Copyright (C) 2018 Nguyễn Gia Phong +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local type = type +local io = { popen = io.popen } +local setmetatable = setmetatable +local string = { find = string.find } +local helpers = require("vicious.helpers") +-- }}} + + +-- Mpc: provides the currently playing song in MPD +-- vicious.contrib.mpc +local mpc_all = {} + + +-- {{{ MPC widget type +local function worker(format, warg) + -- Get data from mpd + local f = io.popen("mpc") + local np = f:read("*line") + f:close() + + -- Not installed, + if np == nil or -- off or stoppped. + (string.find(np, "MPD_HOST") or string.find(np, "volume:")) + then + return {"Stopped"} + end + + -- Check if we should scroll, or maybe truncate + if warg then + if type(warg) == "table" then + np = helpers.scroll(np, warg[1], warg[2]) + else + np = helpers.truncate(np, warg) + end + end + + return {np} +end +-- }}} + +return setmetatable(mpc_all, { __call = function(_, ...) return worker(...) end }) diff --git a/contrib/net_linux.lua b/contrib/net_linux.lua new file mode 100644 index 0000000..85b9250 --- /dev/null +++ b/contrib/net_linux.lua @@ -0,0 +1,154 @@ +-- contrib/net_linux.lua +-- Copyright (C) 2009 Henning Glawe +-- Copyright (C) 2009 Lucas de Vries +-- Copyright (C) 2010 Adrian C. +-- Copyright (C) 2017 Jörg Thalheim +-- Copyright (C) 2017 Roberto +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local pairs = pairs +local tonumber = tonumber +local os = { time = os.time } +local io = { lines = io.lines } +local setmetatable = setmetatable +local string = { match = string.match } +local helpers = require("vicious.helpers") +-- }}} + + +-- Net: provides usage statistics for all network interfaces +-- vicious.contrib.net +local net_linux = {} + + +-- Initialise function tables +local nets = {} +-- Variable definitions +local unit = { ["b"] = 1, ["kb"] = 1024, + ["mb"] = 1024^2, ["gb"] = 1024^3 +} + +-- {{{ Net widget type +local function worker(format, tignorelist) + local args = {} + local tignore = {} + local total_rx = 0 + local total_tx = 0 + local any_up = 0 + + if not tignorelist then + tignorelist = {"lo", "wmaster0"} + end + for k, i in pairs(tignorelist) do + tignore[i] = true + end + + -- Get NET stats + for line in io.lines("/proc/net/dev") do + -- Match wmaster0 as well as rt0 (multiple leading spaces) + local name = string.match(line, "^[%s]?[%s]?[%s]?[%s]?([%w]+):") + if name ~= nil then + -- Received bytes, first value after the name + local recv = tonumber(string.match(line, ":[%s]*([%d]+)")) + -- Transmited bytes, 7 fields from end of the line + local send = tonumber(string.match(line, + "([%d]+)%s+%d+%s+%d+%s+%d+%s+%d+%s+%d+%s+%d+%s+%d$")) + + if not tignore[name] then + total_rx = total_rx + recv + total_tx = total_tx + send + end + + helpers.uformat(args, name .. " rx", recv, unit) + helpers.uformat(args, name .. " tx", send, unit) + + if nets[name] == nil then + -- Default values on the first run + nets[name] = {} + + helpers.uformat(args, name .. " down", 0, unit) + helpers.uformat(args, name .. " up", 0, unit) + args["{"..name.." carrier}"] = 0 + + nets[name].time = os.time() + else -- Net stats are absolute, substract our last reading + local interval = os.time() - nets[name].time > 0 and + os.time() - nets[name].time or 1 + nets[name].time = os.time() + + local down = (recv - nets[name][1]) / interval + local up = (send - nets[name][2]) / interval + + helpers.uformat(args, name .. " down", down, unit) + helpers.uformat(args, name .. " up", up, unit) + + -- Carrier detection + sysnet = helpers.pathtotable("/sys/class/net/" .. name) + + if sysnet.carrier then + ccarrier = tonumber(sysnet.carrier) + + args["{"..name.." carrier}"] = ccarrier + if ccarrier == 1 and not tignore[name] then + any_up = 1 + end + else + args["{"..name.." carrier}"] = 0 + end + end + + -- Store totals + nets[name][1] = recv + nets[name][2] = send + end + end + + helpers.uformat(args, "total rx", total_rx, unit) + helpers.uformat(args, "total tx", total_tx, unit) + + if nets["total"] == nil then + -- Default values on the first run + nets["total"] = {} + + helpers.uformat(args, "total down", 0, unit) + helpers.uformat(args, "total up", 0, unit) + args["{total carrier}"] = 0 + + nets["total"].time = os.time() + else -- Net stats are absolute, substract our last reading + local interval = os.time() - nets["total"].time > 0 and + os.time() - nets["total"].time or 1 + nets["total"].time = os.time() + + local down = (total_rx - nets["total"][1]) / interval + local up = (total_tx - nets["total"][2]) / interval + + helpers.uformat(args, "total down", down, unit) + helpers.uformat(args, "total up", up, unit) + args["{total carrier}"] = any_up + end + + -- Store totals + nets["total"][1] = total_rx + nets["total"][2] = total_tx + + return args +end +-- }}} + +return setmetatable(net_linux, { __call = function(_, ...) return worker(...) end }) diff --git a/contrib/netcfg.lua b/contrib/netcfg.lua new file mode 100644 index 0000000..672a8f3 --- /dev/null +++ b/contrib/netcfg.lua @@ -0,0 +1,50 @@ +-- contrib/netcfg.lua +-- Copyright (C) 2010 Radu A. +-- Copyright (C) 2010 Adrian C. (anrxc) +-- Copyright (C) 2012 Arvydas Sidorenko +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local io = { popen = io.popen } +local setmetatable = setmetatable +local table = { insert = table.insert } +-- }}} + + +-- Netcfg: provides active netcfg network profiles +-- vicious.contrib.netcfg +local netcfg = {} + + +-- {{{ Netcfg widget type +local function worker(format) + -- Initialize counters + local profiles = {} + + local f = io.popen("ls -1 /var/run/network/profiles") + for line in f:lines() do + if line ~= nil then + table.insert(profiles, line) + end + end + f:close() + + return profiles +end +-- }}} + +return setmetatable(netcfg, { __call = function(_, ...) return worker(...) end }) diff --git a/contrib/nvinf_all.lua b/contrib/nvinf_all.lua new file mode 100644 index 0000000..c1515cf --- /dev/null +++ b/contrib/nvinf_all.lua @@ -0,0 +1,49 @@ +-- contrib/nvinf_all.lua +-- Copyright (C) 2015 Ziyuan Guo +-- Copyright (C) 2017 Jörg Thalheim +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local tonumber = tonumber +local io = { popen = io.popen } +local string = { gmatch = string.gmatch } +local setmetatable = setmetatable +local helpers = require("vicious.helpers") +-- }}} + + +-- vicious.widgets.nvinf +local nvinf_all = {} + + +-- {{{ NVIDIA infomation widget type +local function worker(format, warg) + if not warg then warg = "0" end + local nv_inf = {} + local f = io.popen("LC_ALL=C nvidia-settings -q GPUUtilization -q [gpu:"..helpers.shellquote(warg).."]/GPUCoreTemp -q [gpu:"..helpers.shellquote(warg).."]/GPUCurrentClockFreqs -t") + local all_info = f:read("*all") + f:close() + + for num in string.gmatch(all_info, "%d+") do + nv_inf[#nv_inf + 1] = tonumber(num) + end + + return nv_inf +end +-- }}} + +return setmetatable(nvinf_all, { __call = function(_, ...) return worker(...) end }) diff --git a/contrib/nvsmi_all.lua b/contrib/nvsmi_all.lua new file mode 100644 index 0000000..a6c4df8 --- /dev/null +++ b/contrib/nvsmi_all.lua @@ -0,0 +1,56 @@ +-- contrib/nvsmi_all.lua +-- Copyright (C) 2014 Adrian C. +-- Copyright (C) 2017 Jörg Thalheim +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local tonumber = tonumber +local io = { popen = io.popen } +local setmetatable = setmetatable +local string = { match = string.match } +-- }}} + + +-- nvsmi: provides GPU information from nvidia SMI +-- vicious.contrib.nvsmi +local nvsmi_all = {} + + +-- {{{ GPU Information widget type +local function worker(format, warg) + -- Fallback to querying first device + if not warg then warg = "0" end + + -- Get data from smi + -- * Todo: support more; MEMORY,UTILIZATION,ECC,POWER,CLOCK,COMPUTE,PIDS,PERFORMANCE + local f = io.popen("nvidia-smi -q -d TEMPERATURE -i " .. warg) + local smi = f:read("*all") + f:close() + + -- Not installed + if smi == nil then return {0} end + + -- Get temperature information + local _thermal = string.match(smi, "Gpu[%s]+:[%s]([%d]+)[%s]C") + -- Handle devices without data + if _thermal == nil then return {0} end + + return {tonumber(_thermal)} +end +-- }}} + +return setmetatable(nvsmi_all, { __call = function(_, ...) return worker(...) end }) diff --git a/contrib/openweather_all.lua b/contrib/openweather_all.lua new file mode 100644 index 0000000..20c5d87 --- /dev/null +++ b/contrib/openweather_all.lua @@ -0,0 +1,121 @@ +-- contrib/openweather_all.lua +-- Copyright (C) 2013 NormalRa +-- Copyright (C) 2017 Jörg Thalheim +-- Copyright (C) 2020 Marcel Arpogaus +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . +-- {{{ Grab environment +local tonumber = tonumber +local string = {match = string.match} +local math = {ceil = math.ceil, floor = math.floor} +local helpers = require "vicious.helpers" +local spawn = require "vicious.spawn" +-- }}} + +-- Openweather: provides weather information for a requested station +-- vicious.widgets.openweather +local openweather_all = {} + +-- Initialize function tables +local _wdirs = {"N", "NE", "E", "SE", "S", "SW", "W", "NW", "N"} +local _wdata = { + ["{city}"] = "N/A", + ["{wind deg}"] = "N/A", + ["{wind aim}"] = "N/A", + ["{wind mps}"] = "N/A", + ["{wind kmh}"] = "N/A", + ["{sky}"] = "N/A", + ["{weather}"] = "N/A", + ["{temp c}"] = "N/A", + ["{temp min c}"] = "N/A", + ["{temp max c}"] = "N/A", + ["{sunrise}"] = -1, + ["{sunset}"] = -1, + ["{humid}"] = "N/A", + ["{press}"] = "N/A" +} + +-- {{{ Openweather widget type +local function parse(stdout, stderr, exitreason, exitcode) + -- Check if there was a timeout or a problem with the station + if stdout == nil or exitcode ~= 0 then return _wdata end + + _wdata["{city}"] = -- City name + string.match(stdout, '"name":"([%a%s%-]+)"') or _wdata["{city}"] + _wdata["{wind deg}"] = -- Wind degrees + string.match(stdout, '"deg":([%d]+)') or _wdata["{wind deg}"] + _wdata["{wind mps}"] = -- Wind speed in meters per second + string.match(stdout, '"speed":([%d%.]+)') or _wdata["{wind mps}"] + _wdata["{sky}"] = -- Sky conditions + string.match(stdout, '"main":"([%a]+)"') or _wdata["{sky}"] + _wdata["{weather}"] = -- Weather description + string.match(stdout, '"description":"([%a%s]+)"') or _wdata["{weather}"] + _wdata["{temp c}"] = -- Temperature in celsius + string.match(stdout, '"temp":([%-]?[%d%.]+)') or _wdata["{temp c}"] + _wdata["{temp min c}"] = -- Minimal Temperature in celsius + string.match(stdout, '"temp_min":([%-]?[%d%.]+)') or _wdata["{temp min c}"] + _wdata["{temp max c}"] = -- Maximal Temperature in celsius + string.match(stdout, '"temp_max":([%-]?[%d%.]+)') or _wdata["{temp max c}"] + _wdata["{humid}"] = -- Relative humidity in percent + string.match(stdout, '"humidity":([%d]+)') or _wdata["{humid}"] + _wdata["{sunrise}"] = -- Sunrise + tonumber(string.match(stdout, '"sunrise":([%d]+)')) or _wdata["{sunrise}"] + _wdata["{sunset}"] = -- Sunset + tonumber(string.match(stdout, '"sunset":([%d]+)')) or _wdata["{sunset}"] + _wdata["{press}"] = -- Pressure in hPa + string.match(stdout, '"pressure":([%d%.]+)') or _wdata["{press}"] + + -- Wind speed in km/h + if _wdata["{wind mps}"] ~= "N/A" then + _wdata["{wind mps}"] = math.floor(tonumber(_wdata["{wind mps}"]) + .5) + _wdata["{wind kmh}"] = math.ceil(_wdata["{wind mps}"] * 3.6) + end -- Temperature in °C + if _wdata["{temp c}"] ~= "N/A" then + _wdata["{temp c}"] = math.floor(tonumber(_wdata["{temp c}"]) + .5) + end -- Calculate wind direction + if _wdata["{wind deg}"] ~= "N/A" then + _wdata["{wind deg}"] = tonumber(_wdata["{wind deg}"]) + + -- Lua tables start at [1] + if (_wdata["{wind deg}"] / 45) % 1 == 0 then + _wdata["{wind aim}"] = _wdirs[_wdata["{wind deg}"] / 45 + 1] + else + _wdata["{wind aim}"] = _wdirs[math.ceil(_wdata["{wind deg}"] / 45) + + 1] .. + _wdirs[math.floor( + _wdata["{wind deg}"] / 45) + 1] + end + end + + return _wdata +end + +function openweather_all.async(format, warg, callback) + if not warg then return callback {} end + if type(warg) ~= "table" then return callback {} end + + -- Get weather forceast using the city ID code, from: + -- * OpenWeatherMap.org + local openweather = "http://api.openweathermap.org/data/2.5/weather?id=" .. + warg.city_id .. "&appid=" .. warg.app_id .. + "&mode=json&units=metric" + + spawn.easy_async("curl --connect-timeout 1 -fsm 3 '" .. openweather .. "'", + function(...) callback(parse(...)) end) +end +-- }}} + +return helpers.setasyncall(openweather_all) diff --git a/contrib/ossvol_linux.lua b/contrib/ossvol_linux.lua new file mode 100644 index 0000000..6ef6f3b --- /dev/null +++ b/contrib/ossvol_linux.lua @@ -0,0 +1,68 @@ +-- contrib/ossvol_linux.lua +-- Copyright (C) 2010 Adrian C. +-- Copyright (C) 2017 Jörg Thalheim +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local tonumber = tonumber +local io = { popen = io.popen } +local setmetatable = setmetatable +local string = { match = string.match } +-- }}} + + +-- Ossvol: provides volume levels of requested OSS mixers +-- vicious.contrib.ossvol +local ossvol_linux = {} + + +-- {{{ Volume widget type +local function worker(format, warg) + if not warg then return end + + local mixer_state = { + ["on"] = "♫", -- "", + ["off"] = "♩" -- "M" + } + + -- Get mixer control contents + local f = io.popen("ossmix -c") + local mixer = f:read("*all") + f:close() + + -- Capture mixer control state + local volu = tonumber(string.match(mixer, warg .. "[%s]([%d%.]+)"))/0.25 + local mute = string.match(mixer, "vol%.mute[%s]([%a]+)") + -- Handle mixers without data + if volu == nil then + return {0, mixer_state["off"]} + end + + -- Handle mixers without mute + if mute == "OFF" and volu == "0" + -- Handle mixers that are muted + or mute == "ON" then + mute = mixer_state["off"] + else + mute = mixer_state["on"] + end + + return {volu, mute} +end +-- }}} + +return setmetatable(ossvol_linux, { __call = function(_, ...) return worker(...) end }) diff --git a/contrib/pop_all.lua b/contrib/pop_all.lua new file mode 100644 index 0000000..3d24059 --- /dev/null +++ b/contrib/pop_all.lua @@ -0,0 +1,71 @@ +-- contrib/pop_all.lua +-- Copyright (c) 2010 Boris Bolgradov +-- Copyright (C) 2017 Jörg Thalheim +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +--------------------------------------------------- +-- This widget type depends on luasocket. +-- +-- Widget arguments are host, port, username and +-- password, i.e.: +-- {"mail.myhost.com", 110, "John", "132435"} +--------------------------------------------------- + +-- {{{ Grab environment +local tonumber = tonumber +local setmetatable = setmetatable +local sock_avail, socket = pcall(function() + return require("socket") +end) +-- }}} + + +-- POP: provides the count of new messages in a POP3 mailbox +-- vicious.contrib.pop +local pop_all = {} + + +-- {{{ POP3 count widget type +local function worker(format, warg) + if not sock_avail or (not warg or #warg ~= 4) then + return {"N/A"} + end + + local host, port = warg[1], tonumber(warg[2]) + local user, pass = warg[3], warg[4] + + local client = socket.tcp() + client:settimeout(3) + client:connect(host, port) + client:receive("*l") + client:send("USER " .. user .. "\r\n") + client:receive("*l") + client:send("PASS " .. pass .. "\r\n") + client:receive("*l") + client:send("STAT" .. "\r\n") + local response = client:receive("*l") + client:close() + + if response:find("%+OK") then + response = response:match("%+OK (%d+)") + end + + return {response} +end +-- }}} + +return setmetatable(pop_all, { __call = function(_, ...) return worker(...) end }) diff --git a/contrib/pulse_all.lua b/contrib/pulse_all.lua new file mode 100644 index 0000000..1322ae0 --- /dev/null +++ b/contrib/pulse_all.lua @@ -0,0 +1,139 @@ +-- contrib/pulse_all.lua +-- Copyright (C) 2010 MrMagne +-- Copyright (C) 2010,2017 Jörg Thalheim +-- Copyright (C) 2017 Jonathan McCrohan +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local type = type +local tonumber = tonumber +local io = { popen = io.popen } +local setmetatable = setmetatable +local os = { execute = os.execute } +local table = { insert = table.insert } +local string = { + find = string.find, + match = string.match, + format = string.format, + gmatch = string.gmatch +} +local math = { + floor = math.floor, + ceil = math.ceil +} +-- }}} + + +-- Pulse: provides volume levels of requested pulseaudio sinks and methods to change them +-- vicious.contrib.pulse +local pulse_all = {} + +-- {{{ Helper function +local function pacmd(args) + local f = io.popen("pacmd "..args) + if f == nil then + return nil + else + local line = f:read("*all") + f:close() + return line + end +end + +local function escape(text) + local special_chars = { ["."] = "%.", ["-"] = "%-" } + return text:gsub("[%.%-]", special_chars) +end + +local cached_sinks = {} +local function get_sink_name(sink) + if type(sink) == "string" then return sink end + -- avoid nil keys + local key = sink or 1 + -- Cache requests + if not cached_sinks[key] then + local line = pacmd("list-sinks") + if line == nil then return nil end + for s in string.gmatch(line, "name: <(.-)>") do + table.insert(cached_sinks, s) + end + end + + return cached_sinks[key] +end + + +-- }}} + +-- {{{ Pulseaudio widget type +local function worker(format, sink) + sink = get_sink_name(sink) + if sink == nil then return {0, "unknown"} end + + -- Get sink data + local data = pacmd("dump") + if sink == nil then return {0, "unknown"} end + + -- If mute return 0 (not "Mute") so we don't break progressbars + if string.find(data,"set%-sink%-mute "..escape(sink).." yes") then + return {0, "off"} + end + + local vol = tonumber(string.match(data, "set%-sink%-volume "..escape(sink).." (0x[%x]+)")) + if vol == nil then vol = 0 end + volpercent = vol/0x10000*100 + + return { volpercent % 1 >= 0.5 and math.ceil(volpercent) or math.floor(volpercent), "on"} +end +-- }}} + +-- {{{ Volume control helper +function pulse_all.add(percent, sink) + sink = get_sink_name(sink) + if sink == nil then return end + + local data = pacmd("dump") + + local pattern = "set%-sink%-volume "..escape(sink).." (0x[%x]+)" + local initial_vol = tonumber(string.match(data, pattern)) + + local vol = initial_vol + percent/100*0x10000 + if vol > 0x10000 then vol = 0x10000 end + if vol < 0 then vol = 0 end + + vol = math.ceil(vol) + + local cmd = string.format("pacmd set-sink-volume %s 0x%x >/dev/null", sink, vol) + return os.execute(cmd) +end + +function pulse_all.toggle(sink) + sink = get_sink_name(sink) + if sink == nil then return end + + local data = pacmd("dump") + local pattern = "set%-sink%-mute "..escape(sink).." (%a%a%a?)" + local mute = string.match(data, pattern) + + -- 0 to enable a sink or 1 to mute it. + local state = { yes = 0, no = 1} + local cmd = string.format("pacmd set-sink-mute %s %d", sink, state[mute]) + return os.execute(cmd) +end +-- }}} + +return setmetatable(pulse_all, { __call = function(_, ...) return worker(...) end }) diff --git a/contrib/rss_all.lua b/contrib/rss_all.lua new file mode 100644 index 0000000..f8b3dea --- /dev/null +++ b/contrib/rss_all.lua @@ -0,0 +1,84 @@ +-- contrib/rss_all.lua +-- Copyright (C) 2009 olcc +-- Copyright (C) 2017 Jörg Thalheim +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +--------------------------------------------------- +-- This is now a standalone RSS reader for awesome: +-- * http://github.com/olcc/aware +--------------------------------------------------- + +-- {{{ Grab environment +local pairs = pairs +local io = { popen = io.popen } +local setmetatable = setmetatable +-- }}} + + +-- RSS: provides latest world news +-- vicious.contrib.rss +local rss_all = {} + + +-- {{{ RSS widget type +local function worker(format, input) + -- input: * feed - feed url + -- * object - entity to look for (typically: 'item') + -- * fields - fields to read (example: 'link', 'title', 'description') + -- output: * count - number of entities found + -- * one table for each field, containing wanted values + local feed = input.feed + local object = input.object + local fields = input.fields + + -- Initialise tables + local out = {} + + for _, v in pairs(fields) do + out[v] = {} + end + + -- Initialise variables + local ob = nil + local i,j,k = 1, 1, 0 + local curl = "curl -A 'Mozilla/4.0' -fsm 5 --connect-timeout 3 " + + -- Get the feed + local f = io.popen(curl .. '"' .. feed .. '"') + local feed = f:read("*all") + f:close() + + while true do + i, j, ob = feed.find(feed, "<" .. object .. ">(.-)", i) + if not ob then break end + + for _, v in pairs(fields) do + out[v][k] = ob:match("<" .. v .. ">(.*)") + end + + k = k+1 + i = j+1 + end + + -- Update the entity count + out.count = k + + return out +end +-- }}} + +return setmetatable(rss_all, { __call = function(_, ...) return worker(...) end }) diff --git a/contrib/sensors_linux.lua b/contrib/sensors_linux.lua new file mode 100644 index 0000000..6d7e87c --- /dev/null +++ b/contrib/sensors_linux.lua @@ -0,0 +1,83 @@ +-- contrib/sensors_linux.lua +-- Copyright (C) 2010 Greg D. +-- Copyright (C) 2017 Jörg Thalheim +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local tonumber = tonumber +local io = { popen = io.popen } +local setmetatable = setmetatable +local table = { insert = table.insert } +local string = { + gsub = string.gsub, + match = string.match +} +-- }}} + + +-- Sensors: provides access to lm_sensors data +-- vicious.contrib.sensors +local sensors_linux = {} + + +-- {{{ Split helper function +local function datasplit(str) + -- Splitting strings into associative array + -- with some magic to get the values right. + str = string.gsub(str, "\n", ":") + + local tbl = {} + string.gsub(str, "([^:]*)", function (v) + if string.match(v, ".") then + table.insert(tbl, v) + end + end) + + local assoc = {} + for c = 1, #tbl, 2 do + local k = string.gsub(tbl[c], ".*_", "") + local v = tonumber(string.match(tbl[c+1], "[%d]+")) + assoc[k] = v + end + + return assoc +end +-- }}} + +-- {{{ Sensors widget type +local function worker(format, warg) + -- Get data from all sensors + local f = io.popen("LANG=C sensors -uA") + local lm_sensors = f:read("*all") + f:close() + + local sensor_data = string.gsub( + string.match(lm_sensors, warg..":\n(%s%s.-)\n[^ ]"), " ", "") + + -- One of: crit, max + local divisor = "crit" + local s_data = datasplit(sensor_data) + + if s_data[divisor] and s_data[divisor] > 0 then + s_data.percent = s_data.input / s_data[divisor] * 100 + end + + return {s_data.input, tonumber(s_data.percent)} +end +-- }}} + +return setmetatable(sensors_linux, { __call = function(_, ...) return worker(...) end }) diff --git a/contrib/wpa_all.lua b/contrib/wpa_all.lua new file mode 100644 index 0000000..e35125a --- /dev/null +++ b/contrib/wpa_all.lua @@ -0,0 +1,79 @@ +-- contrib/wpa_all.lua +-- Copyright (C) 2012 jinleileiking +-- Copyright (C) 2017 Jörg Thalheim +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local tonumber = tonumber +local math = { ceil = math.ceil } +local setmetatable = setmetatable +local helpers = require("vicious.helpers") +local io = { + open = io.open, + popen = io.popen +} +local string = { + find = string.find, + match = string.match +} +-- }}} + + +-- Wifi: provides wireless information for a requested interface +local wpa_all = {} + +local info = { + ["{ssid}"] = "N/A", + ["{bssid}"] = "N/A", + ["{ip}"] = "N/A", + ["{qual}"] = "N/A", +} + +-- {{{ Wireless widget type +local function worker(format, warg) + if not warg then return info end + + local wpa_cmd = "wpa_cli -i'" .. warg .. "' status 2>&1" + local f = io.popen(wpa_cmd) + local output = f:read("*all") + f:close() + + if not output then return info end + + state = string.match(output, 'wpa_state=([%a]+)') or 'N/A' + info["{bssid}"] = string.match(output, 'bssid=([%d%a:]+)') or 'N/A' + info["{ssid}"] = string.match(output, 'ssid=([%a]+)') or 'N/A' + info["{ip}"] = string.match(output, 'ip_address=([%d.]+)') or 'N/A' + + if not state == 'COMPLETED' then + return info + end + + local wpa_cmd = "wpa_cli -i'" .. warg .. "' bss " .. bssid .. " 2>&1" + local f = io.popen(wpa_cmd) + local output = f:read("*all") + f:close() + + if not output then return info end + + info["{qual}"] = string.match(output, 'qual=([%d]+)') + + return info +end +-- }}} + +return setmetatable(wpa_all, { __call = function(_, ...) return worker(...) end }) diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..d0c3cbf --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = source +BUILDDIR = build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 0000000..2e75f9a --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,3 @@ +Sphinx >= 3 +sphinx_rtd_theme +sphinxcontrib-luadomain diff --git a/docs/source/caching.rst b/docs/source/caching.rst new file mode 100644 index 0000000..5efec11 --- /dev/null +++ b/docs/source/caching.rst @@ -0,0 +1,37 @@ +.. _caching: + +Power and Caching +================= + +When a lot of widgets are in use they, and awesome, can generate a lot +of wake-ups and also be very expensive for system resources. This is +especially important when running on battery power. It was a big problem +with awesome v2 and widgets that used shell scripts to gather data, +and with widget libraries written in languages like Ruby. + +Lua is an extremely fast and efficient programming language, and Vicious +takes advantage of that. But suspending Vicious widgets is one way +to prevent them from draining your battery, despite that. + +Update intervals also play a big role, and you can save a lot of power +with a smart approach. Don't use intervals like: 5, 10, 30, 60, etc. +to avoid harmonics. If you take the 60-second mark as an example, +all of your widgets would be executed at that point. Instead think about +using only prime numbers, in that case you will have only a few widgets +executed at any given time interval. When choosing intervals also consider +what a widget actually does. Some widget types read files that reside +in memory, others call external utilities and some, like the mbox widget, +read big files. + +Vicious can also cache values returned by widget types. Caching enables you +to have multiple widgets using the same widget type. With caching its worker +function gets executed only once---which is also great for saving power. + +* Some widget types keep internal data and if you call one multiple times + without caching, the widget that executes it first would modify stored values. + This can lead to problems and give you inconsistent data. Remember it + for widget types like CPU and Network usage, which compare the old set + of data with the new one to calculate current usage. +* Widget types that require a widget argument to be passed should be + handled carefully. If you are requesting information for different devices + then caching should not be used, because you could get inconsistent data. diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst new file mode 120000 index 0000000..bfa394d --- /dev/null +++ b/docs/source/changelog.rst @@ -0,0 +1 @@ +../../CHANGELOG.rst \ No newline at end of file diff --git a/docs/source/conf.py b/docs/source/conf.py new file mode 100644 index 0000000..33edd5f --- /dev/null +++ b/docs/source/conf.py @@ -0,0 +1,38 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. +# For a full list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# Project information +project = 'Vicious' +copyright = '2020, vicious-widgets' +author = 'vicious-widgets' + +# The full version, including alpha/beta/rc tags +release = '2.4.2' + + +# Add any Sphinx extension module names here, as strings. +# They can be extensions coming with Sphinx (named 'sphinx.ext.*') +# or your custom ones. +extensions = ['sphinx.ext.extlinks', 'sphinxcontrib.luadomain'] +extlinks = {'github': ('https://github.com/%s', '@')} + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = [] + +# Options for HTML output +html_theme = 'sphinx_rtd_theme' +html_show_copyright = False + +# Add any paths that contain custom static files (such as style sheets) +# here, relative to this directory. They are copied after the builtin +# static files, so a file named "default.css" will overwrite the builtin +# "default.css". +html_static_path = [] diff --git a/docs/source/contrib.rst b/docs/source/contrib.rst new file mode 100644 index 0000000..a1cfee0 --- /dev/null +++ b/docs/source/contrib.rst @@ -0,0 +1,248 @@ +Contrib Widget Types +==================== + +Contrib libraries, or widget types, are extra snippets of code you can use. +Some are for less common hardware, and other were contributed by Vicious users. +The contrib directory also holds widget types that were obsoleted or rewritten. +Contrib widgets will not be imported by init unless you explicitly enable it, +or load them in your ``rc.lua``. + +Usage within Awesome +-------------------- + +To use contrib widgets uncomment the line that loads them in ``init.lua``. +Or you can load them in your rc.lua after you require Vicious: + +.. code-block:: lua + + local vicious = require"vicious" + vicious.contrib = require"vicious.contrib" + +Widget Types +------------ + +Most widget types consist of worker functions that take the ``format`` argument +given to :lua:func:`vicious.register` as the first argument, +``warg`` as the second, and return a table of values to insert in +the format string. But we have not insisted on this coding style in contrib. +So widgets like PulseAudio have emerged that are different. These widgets +could also depend on Lua libraries that are not distributed with the +core Lua distribution. Ease of installation and use does not necessarily +have to apply to contributed widgets. + +vicious.contrib.ac +^^^^^^^^^^^^^^^^^^ + +Provide status about the power supply (AC). + +Supported platforms: GNU/Linux, requiring ``sysfs``. + +* Argument: the AC device, i.e ``"AC"`` or ``"ACAD"``. The device is linked + under ``/sys/class/power_supply/`` and should have a file called ``online``. +* Returns ``{"On"}`` if AC is connected, else ``{"Off"}``. + If AC doesn't exist, returns ``{"N/A"}``. + +vicious.contrib.ati +^^^^^^^^^^^^^^^^^^^ + +Provides various info about ATI GPU status. + +Supported platforms: GNU/Linux, requiring ``sysfs``. + +* Argument: card ID, e.g. ``"card0"`` (and where possible, + uses ``debugfs`` to gather data on radeon power management) +* Returns a table with string keys: ``${method}``, ``${dpm_state}``, + ``${dpm_perf_level}``, ``${profile}``, ``${engine_clock mhz}``, + ``${engine_clock khz}``, ``${memory_clock mhz}``, ``${memory_clock khz}``, + ``${voltage v}``, ``${voltage mv}`` + +vicious.contrib.batpmu +^^^^^^^^^^^^^^^^^^^^^^ + +vicious.contrib.batproc +^^^^^^^^^^^^^^^^^^^^^^^ + +vicious.contrib.btc +^^^^^^^^^^^^^^^^^^^ + +Provides current Bitcoin price in any currency by +[code](https://en.wikipedia.org/wiki/ISO_4217). + + +Platform independent, although requiring ``curl`` and either +[lua-cjson](https://github.com/mpx/lua-cjson/) or +[luajson](https://github.com/harningt/luajson/). + +* Argument: currency code, e.g. ``"usd"``, ``"rub"`` and other. + Default to ``"usd"``. +* Returns a table with string key ``${price}``. + +vicious.contrib.buildbot +^^^^^^^^^^^^^^^^^^^^^^^^ + +Provides last build status for configured buildbot builders +(http://trac.buildbot.net/). + +Supported platforms: platform independent, though requiring Lua JSON parser +[luajson](https://github.com/harningt/luajson/). + +Returns build status in the format: +``[..]``. +If ```` is the same as ```` +only one number is displayed. ```` colors: +red---failed, green---successful, yellow---in progress. + +vicious.contrib.countfiles +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +vicious.contrib.cmus +^^^^^^^^^^^^^^^^^^^^ + +.. note:: + + This widget type has been promoted to :ref:`widgets`. + +Provides cmus player information using ``cmus-remote``. + +Supported platforms: platform independent. + +* Argument: a table whose first field is the socket including host (or nil). +* Returns a table with string keys: ``${status}``, ``${artist}``, ``${title}``, + ``${duration}``, ``${file}``, ``${continue}``, ``${shuffle}``, ``${repeat}``. + +vicious.contrib.dio +^^^^^^^^^^^^^^^^^^^ + +Provides I/O statistics for requested storage devices. + +* Argument: the disk as an argument, i.e. ``"sda"``, or a specific + partition, i.e. ``"sda/sda2"`` +* Returns a table with string keys: ``${total_s}``, ``${total_kb}``, + ``${total_mb}``, ``${read_s}``, ``${read_kb}``, ``${read_mb}``, + ``${write_s}``, ``${write_kb}``, ``${write_mb}`` and ``${sched}`` + +vicious.contrib.mpc +^^^^^^^^^^^^^^^^^^^ + +vicious.contrib.netcfg +^^^^^^^^^^^^^^^^^^^^^^ + +vicious.contrib.net +^^^^^^^^^^^^^^^^^^^ + +vicious.contrib.openweather +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Provides weather information for a requested city from OpenWeatherMap (OWM) + +* Argument: a table containing the fields ``city_id`` with the OWM city ID, e.g. + ``"2643743"`` and ``app_id`` with the the OWM app ID, e.g + ``"4c57f0c88d9844630327623633ce269cf826ab99"`` +* Returns a table with string keys: ``${city}``, ``${humid}``, ``${press}``, + ``${sky}``, ``${sunrise}``, ``${sunset}``, ``${temp c}``, ``${temp max c}``, + ``${temp min c}``, ``${weather}``, ``${wind aim}``, ``${wind deg}``, + ``${wind kmh}`` and ``${wind mps}``, + +vicious.contrib.nvinf +^^^^^^^^^^^^^^^^^^^^^ + +Provides GPU utilization, core temperature, clock frequency information about +Nvidia GPU from nvidia-settings + +Supported Platforms: platform independent + +* Argument (optional): card ID as an argument, e.g. ``"1"``, default to ID 0 +* Returns an array containing: + + * ``$1``: Usage of GPU core + * ``$2``: Usage of GPU memory + * ``$3``: Usage of video engine + * ``$4``: Usage of PCIe bandwidth + * ``$5``: Uemperature of requested graphics device + * ``$6``: Urequency of GPU core + * ``$7``: Uemory transfer rate + +vicious.contrib.nvsmi +^^^^^^^^^^^^^^^^^^^^^ + +Provides (very basic) information about Nvidia GPU status from SMI + +Supported platforms: platform independent + +* Argument (optional): card ID as an argument, e.g. ``"1"``, default to ID 0 +* Returns an array containing temperature of requested graphics device + +vicious.contrib.ossvol +^^^^^^^^^^^^^^^^^^^^^^ + +vicious.contrib.pop +^^^^^^^^^^^^^^^^^^^ + +vicious.contrib.pulse +^^^^^^^^^^^^^^^^^^^^^ + +Provides volume levels of requested pulseaudio sinks and functions to +manipulate them + +* Argument (optional): name of a sink as an optional argument. A number will + be interpret as an index, if no argument is given, it will take the + first-best. To get a list of available sinks run + ``pacmd list-sinks | grep 'name:'``. +* Returns an array whose only element is the volume level + +vicious.contrib.pulse.add(percent[, sink]) +"""""""""""""""""""""""""""""""""""""""""" + +* ``percent`` is the percentage to increment or decrement the volume + from its current value +* Returns the exit status of ``pacmd`` + +vicious.contrib.pulse.toggle([sink]) +"""""""""""""""""""""""""""""""""""" + +* Toggles mute state +* Returns the exit status of ``pacmd`` + +vicious.contrib.rss +^^^^^^^^^^^^^^^^^^^ + +vicious.contrib.sensors +^^^^^^^^^^^^^^^^^^^^^^^ + +vicious.contrib.wpa +^^^^^^^^^^^^^^^^^^^ + +Provides information about the wifi status. + +Supported Platforms: platform independent, requiring ``wpa_cli``. + +* Argument: the interface, e.g. ``"wlan0"`` or ``"wlan1"`` +* Returns a table with string keys: + ``${ssid}``, ``${qual}``, ``${ip}``, ``${bssid}`` + +Usage Examples +-------------- + +PulseAudio Widget +^^^^^^^^^^^^^^^^^ + +.. code-block:: lua + + vol = wibox.widget.textbox() + local sink = "alsa_output.pci-0000_00_1b.0.analog-stereo" + vicious.register(vol, vicious.contrib.pulse, " $1%", 2, sink) + vol:buttons(awful.util.table.join( + awful.button({}, 1, function () awful.util.spawn("pavucontrol") end), + awful.button({}, 4, function () vicious.contrib.pulse.add(5, sink) end), + awful.button({}, 5, function () vicious.contrib.pulse.add(-5, sink) end))) + +Buildbot Widget +^^^^^^^^^^^^^^^ + +.. code-block:: lua + + buildbotwidget = wibox.widget.textbox() + vicious.register( + buildbotwidget, vicious.contrib.buildbot, "$1,", 3600, + { { builder="coverage", url="http://buildbot.buildbot.net" }, + { builder="tarball-slave", url="http://buildbot.buildbot.net" } }) diff --git a/docs/source/contributing.rst b/docs/source/contributing.rst new file mode 120000 index 0000000..e9a8ba6 --- /dev/null +++ b/docs/source/contributing.rst @@ -0,0 +1 @@ +../../CONTRIBUTING.rst \ No newline at end of file diff --git a/docs/source/copying.rst b/docs/source/copying.rst new file mode 100644 index 0000000..a0a8e20 --- /dev/null +++ b/docs/source/copying.rst @@ -0,0 +1,83 @@ +License and Credits +=================== + +Wicked was written by: + +* Lucas de Vries + +Vicious was originally written by: + +* Adrian C. (anrxc) . + +Vicious is released under `GNU GPLv2+`_ and is currently maintained by: + +* :github:`Jörg Thalheim ` +* :github:`mutlusun` (especially the FreeBSD port) +* :github:`Daniel Hahler ` +* :github:`Nguyễn Gia Phong ` +* :github:`Enric Morales ` + (especially the OpenBSD port) + +Over the years, Vicious has also received various patches and improvements +from the following contributors, listed in alphabetic order: + +* 0x5b +* Adam Lee +* Alexander Koch +* Amir Mohammad Saied +* Andrea Scarpino +* Andreas Geisenhainer +* Andrew Merenbach +* Andrzej Bieniek +* Arthur Axel 'fREW' Schmidt +* Arvydas Sidorenko +* Benedikt Sauer +* Beniamin Kalinowski +* Benoît Zugmeyer +* blastmaster +* Brandon Hartshorn +* crondog +* David Udelson +* Dodo The Last +* Elric Milon +* getzze +* Greg D. +* Hagen Schink +* Henning Glawe +* Hiltjo Posthuma +* :github:`James Reed ` +* Jay Kamat +* Jeremy +* jinleileiking +* joe di castro +* Joerg Jaspert +* Jonathan McCrohan +* :github:`Juan Carlos Menonita ` +* Juergen Descher +* Julian Volodia +* Keith Hughitt +* Lorenzo Gaggini +* Lyderic Lefever +* Martin Striz +* Martin Ueding +* Mellich +* Michael Kressibucher +* Michael Unterkalmsteiner +* niko +* Noah Tilton +* Normal Ra +* Perry Hargrave +* Rémy CLOUARD +* :github:`Roberto ` +* Sébastien Luttringer +* Shadowmourne G +* starenka +* Suseika +* Uli Schlachter +* Wtfcoder +* Xaver Hellauer +* zhrtz + +and many others. + +.. _GNU GPLv2+: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html diff --git a/docs/source/custom.rst b/docs/source/custom.rst new file mode 100644 index 0000000..6cdcda7 --- /dev/null +++ b/docs/source/custom.rst @@ -0,0 +1,22 @@ +.. _custom-wtype: + +Custom Widget Types +=================== + +Use any of the existing widget types as a starting point for your own. +Write a quick worker function that does the work and plug it in. +How data will be formatted, will it be red or blue, should be +defined in ``rc.lua`` (or somewhere else, outside the actual module). + +Before writing a widget type you should check if there is already one +in the contrib directory of Vicious. The contrib directory contains +extra widgets you can use. Some are for less common hardware, and others +were contributed by Vicious users. Most of the contrib widgets are obsolete. +Contrib widgets will not be imported by init unless you explicitly enable it, +or load them in your ``rc.lua``. + +Some users would like to avoid writing new modules. For them Vicious kept +the old Wicked functionality, possibility to register their own functions +as widget types. By providing them as the second argument to +:lua:func:`vicious.register`. Your function can accept ``format`` and ``warg`` +arguments, just like workers. diff --git a/docs/source/examples.rst b/docs/source/examples.rst new file mode 100644 index 0000000..f57d7ed --- /dev/null +++ b/docs/source/examples.rst @@ -0,0 +1,97 @@ +Usage Examples +============== + +Start with a simple widget, like ``date``, then build your setup from there, +one widget at a time. Also remember that besides creating and registering +widgets you have to add them to a ``wibox`` (statusbar) in order to +actually display them. + +Date Widget +----------- + +Update every 2 seconds (the default interval), +use standard date sequences as the format string: + +.. code-block:: lua + + datewidget = wibox.widget.textbox() + vicious.register(datewidget, vicious.widgets.date, "%b %d, %R") + +Memory Widget +------------- + +Update every 13 seconds, append ``MiB`` to 2nd and 3rd returned values +and enables caching. + +.. code-block:: lua + + memwidget = wibox.widget.textbox() + vicious.cache(vicious.widgets.mem) + vicious.register(memwidget, vicious.widgets.mem, "$1 ($2MiB/$3MiB)", 13) + +HDD Temperature Widget +---------------------- + +Update every 19 seconds, request the temperature level of ``/dev/sda`` and +append *°C* to the returned value. Since the listening port is not provided, +default one is used. + +.. code-block:: lua + + hddtempwidget = wibox.widget.textbox() + vicious.register(hddtempwidget, vicious.widgets.hddtemp, "${/dev/sda} °C", 19) + +Mbox Widget +----------- + +Updated every 5 seconds, provide full path to the mbox as argument: + +.. code-block:: lua + + mboxwidget = wibox.widget.textbox() + vicious.register(mboxwidget, vicious.widgets.mbox, "$1", 5, + "/home/user/mail/Inbox") + +Battery Widget +-------------- + +Update every 61 seconds, request the current battery charge level +and displays a progressbar, provides ``BAT0`` as battery ID: + +.. code-block:: lua + + batwidget = wibox.widget.progressbar() + + -- Create wibox with batwidget + batbox = wibox.layout.margin( + wibox.widget{ { max_value = 1, widget = batwidget, + border_width = 0.5, border_color = "#000000", + color = { type = "linear", + from = { 0, 0 }, + to = { 0, 30 }, + stops = { { 0, "#AECF96" }, + { 1, "#FF5656" } } } }, + forced_height = 10, forced_width = 8, + direction = 'east', color = beautiful.fg_widget, + layout = wibox.container.rotate }, + 1, 1, 3, 3) + + -- Register battery widget + vicious.register(batwidget, vicious.widgets.bat, "$2", 61, "BAT0") + +CPU Usage Widget +---------------- + +Update every 3 seconds, feed the graph with total usage percentage +of all CPUs/cores: + +.. code-block:: lua + + cpuwidget = awful.widget.graph() + cpuwidget:set_width(50) + cpuwidget:set_background_color"#494B4F" + cpuwidget:set_color{ type = "linear", from = { 0, 0 }, to = { 50, 0 }, + stops = { { 0, "#FF5656" }, + { 0.5, "#88A175" }, + { 1, "#AECF96" } } } + vicious.register(cpuwidget, vicious.widgets.cpu, "$1", 3) diff --git a/docs/source/format.rst b/docs/source/format.rst new file mode 100644 index 0000000..49b93c4 --- /dev/null +++ b/docs/source/format.rst @@ -0,0 +1,137 @@ +.. _format-func: + +Format Functions +================ + +You can use a function instead of a string as the format parameter. +Then you are able to check the value returned by the widget type +and change it or perform some action. You can change the color of +the battery widget when it goes below a certain point, hide widgets +when they return a certain value or maybe use ``string.format`` for padding. + +Do not confuse this with just coloring the widget, in those cases +standard Pango markup can be inserted into the format string. + +The format function will get the widget as its first argument, table with +the values otherwise inserted into the format string as its second argument, +and will return the text/data to be used for the widget. + +Examples +-------- + +Hide mpd widget when no song is playing +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: lua + + mpdwidget = wibox.widget.textbox() + vicious.register( + mpdwidget, + vicious.widgets.mpd, + function (widget, args) + if args["{state}"] == "Stop" then + return '' + else + return ('MPD: %s - %s'):format( + args["{Artist}"], args["{Title}"]) + end + end) + +Use string.format for padding +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: lua + + uptimewidget = wibox.widget.textbox() + vicious.register(uptimewidget, vicious.widgets.uptime, + function (widget, args) + return ("Uptime: %02d %02d:%02d "):format( + args[1], args[2], args[3]) + end, 61) + +When it comes to padding it is also useful to mention how a widget +can be configured to have a fixed width. You can set a fixed width on +your textbox widgets by changing their ``width`` field (by default width +is automatically adapted to text width). The following code forces +a fixed width of 50 px to the uptime widget, and aligns its text to the right: + +.. code-block:: lua + + uptimewidget = wibox.widget.textbox() + uptimewidget.width, uptimewidget.align = 50, "right" + vicious.register(uptimewidget, vicious.widgets.uptime, "$1 $2:$3", 61) + +Stacked graph +^^^^^^^^^^^^^ + +Stacked graphs are handled specially by Vicious: ``format`` functions passed +to the corresponding widget types must return an array instead of a string. + +.. code-block:: lua + + cpugraph = wibox.widget.graph() + cpugraph:set_stack(true) + cpugraph:set_stack_colors{ "red", "yellow", "green", "blue" } + vicious.register(cpugraph, vicious.widgets.cpu, + function (widget, args) + return { args[2], args[3], args[4], args[5] } + end, 3) + +The snipet above enables graph stacking/multigraph and plots usage of all four +CPU cores on a single graph. + +Substitute widget types' symbols +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If you are not happy with default symbols used in volume, battery, cpufreq and +other widget types, use your own symbols without any need to modify modules. +The following example uses a custom table map to modify symbols representing +the mixer state: on or off/mute. + +.. code-block:: lua + + volumewidget = wibox.widget.textbox() + vicious.register(volumewidget, vicious.widgets.volume, + function (widget, args) + local label = { ["🔉"] = "O", ["🔈"] = "M" } + return ("Volume: %d%% State: %s"):format( + args[1], label[args[2]]) + end, 2, "PCM") + +.. _call-example: + +Get data from the widget +^^^^^^^^^^^^^^^^^^^^^^^^ + +:lua:func:`vicious.call` could be useful for naughty notification and scripts: + +.. code-block:: lua + + mybattery = wibox.widget.textbox() + vicious.register(mybattery, vicious.widgets.bat, "$2%", 17, "0") + mybattery:buttons(awful.util.table.join(awful.button( + {}, 1, + function () + naughty.notify{ title = "Battery indicator", + text = vicious.call(vicious.widgets.bat, + "Remaining time: $3", "0") } + end))) + +Format functions can be used as well: + +.. code-block:: lua + + mybattery:buttons(awful.util.table.join(awful.button( + {}, 1, + function () + naughty.notify{ + title = "Battery indicator", + text = vicious.call( + vicious.widgets.bat, + function (widget, args) + return ("%s: %10sh\n%s: %14d%%\n%s: %12dW"):format( + "Remaining time", args[3], + "Wear level", args[4], + "Present rate", args[5]) + end, "0") } + end))) diff --git a/docs/source/index.rst b/docs/source/index.rst new file mode 100644 index 0000000..67324e8 --- /dev/null +++ b/docs/source/index.rst @@ -0,0 +1,50 @@ +Welcome to Vicious' documentation! +================================== + +Vicious is a modular widget library for window managers, but mostly catering +to users of the `awesome window manager`_. It was derived from the old +*wicked* widget library, and has some of the old *wicked* widget types, +a few of them rewritten, and a good number of new ones. + +Vicious widget types are a framework for creating your own widgets. +Vicious contains modules that gather data about your system, +and a few *awesome* helper functions that make it easier to register timers, +suspend widgets and so on. Vicious doesn't depend on any third party Lua_ +library, but may depend on additional system utilities. + +Table of Contents +----------------- + +.. toctree:: + :maxdepth: 2 + + usage-lua + usage-awesome + examples + widgets + contrib + custom + format + caching + security + contributing + copying + changelog + +See Also +-------- + +* Manual pages: `awesome(1)`_, `awesomerc(5)`_ +* `Awesome declarative layout system`_ +* `My first awesome`_ +* `Example awesome configuration`_ (outdated) + +.. _awesome window manager: https://awesomewm.org +.. _Lua: https://www.lua.org +.. _awesome(1): https://awesomewm.org/doc/manpages/awesome.1.html +.. _awesomerc(5): https://awesomewm.org/doc/manpages/awesomerc.5.html +.. _Awesome declarative layout system: + https://awesomewm.org/apidoc/documentation/03-declarative-layout.md.html +.. _My first awesome: + https://awesomewm.org/doc/api/documentation/07-my-first-awesome.md.html +.. _Example awesome configuration: http://git.sysphere.org/awesome-configs/ diff --git a/docs/source/security.rst b/docs/source/security.rst new file mode 100644 index 0000000..ceb6935 --- /dev/null +++ b/docs/source/security.rst @@ -0,0 +1,27 @@ +Security Notes +============== + +At the moment only one widget type (Gmail) requires +authentication information in order to get to the data. +In the future there could be more, and you should give some thought +to the issue of protecting your data. The Gmail widget type by default +stores login information in the ``~/.netrc`` file, and you are advised +to make sure that file is only readable by the owner. Other than that +we can not force all users to conform to one standard, +one way of keeping it secure, like in some keyring. + +First let's clear why we simply don't encrypt the login information +and store it in ciphertext. By exposing the algorithm anyone can +reverse the encryption steps. Some claim even that's better than +plaintext but it's just security through obscurity. + +Here are some ideas actually worth your time. Users that have KDE +(or parts of it) installed could store their login information into +the Kwallet service and request it via DBus from the widget type. +It can be done with tools like ``dbus-send`` and ``qdbus``. +The Gnome keyring should support the same, so those with parts of Gnome +installed could use that keyring. + +Users of GnuPG (and its agent) could consider encrypting the netrc file +with their GPG key. Through the GPG Passphrase Agent they could then +decrypt the file transparently while their session is active. diff --git a/docs/source/usage-awesome.rst b/docs/source/usage-awesome.rst new file mode 100644 index 0000000..20b2888 --- /dev/null +++ b/docs/source/usage-awesome.rst @@ -0,0 +1,162 @@ +Usage within Awesome +==================== + +To use Vicious with awesome_, install the package from your operating system +provider, or download the source code and move it to your awesome +configuration directory in ``$XDG_CONFIG_HOME`` (usually ``~/.config``):: + + git clone https://github.com/vicious-widgets/vicious.git + mv vicious $XDG_CONFIG_HOME/awesome/ + +Vicious will only load modules for widget types you intend to use in +your awesome configuration, to avoid having useless modules sitting in +your memory. + +Then add the following to the top of your ``rc.lua``: + +.. code-block:: lua + + local vicious = require("vicious") + +vicious.register +---------------- + +Once you create a widget (a textbox, graph or a progressbar), +call ``vicious.register`` to register it with Vicious: + +.. lua:function:: vicious.register(widget, wtype, format, interval, warg) + + Register a widget. + + :param widget: awesome widget created from + ``awful.widget`` or ``wibox.widget`` + + :param wtype: either of + + * Vicious widget type: any widget type + :ref:`provided by Vicious ` or customly defined. + * ``function``: custom function from your own + awesome configuration can be registered as widget types + (see :ref:`custom-wtype`). + + :param format: either of + + * string: ``$key`` will be replaced by respective value in the table + ``t`` returned by the widget type, i.e. use ``$1``, ``$2``, etc. + to retrieve data from an integer-indexed table (a.k.a. array); + ``${foo bar}`` will be substituted by ``t["{foo bar}"]``. + * ``function (widget, args)`` can be used to manipulate data returned + by the widget type (see :ref:`format-func`). + + :param interval: number of seconds between updates of the widget + (default: 2). See :ref:`caching` for more information. + + :param warg: arguments to be passed to widget types, e.g. the battery ID. + +``vicious.register`` alone is not much different from awful.widget.watch_, +which has been added to Awesome since version 4.0. However, Vicious offers +more advanced control of widgets' behavior by providing the following functions. + +vicious.unregister +------------------ + +.. lua:function:: vicious.unregister(widget, keep) + + Unregister a widget. + + :param widget: awesome widget created from + ``awful.widget`` or ``wibox.widget`` + :param keep: if true suspend ``widget`` and wait for activation + :type keep: bool + +vicious.suspend +--------------- + +.. lua:function:: vicious.suspend() + + Suspend all widgets. + +See `example automation script`_ for the "laptop-mode-tools" start-stop module. + +vicious.activate +---------------- + +.. lua:function:: vicious.activate([widget]) + + Restart suspended widget(s). + + :param widget: if provided only that widget will be activated + +vicious.cache +------------- + +.. lua:function:: vicious.cache(wtype) + + Enable caching of values returned by a widget type. + +vicious.force +-------------- + +.. lua:function:: vicious.force(wtable) + + Force update of given widgets. + + :param wtable: table of one or more widgets to be updated + +vicious.call[_async] +-------------------- + +.. lua:function:: vicious.call(wtype, format, warg) + + Get formatted data from a synchronous widget type + (:ref:`example `). + + :param wtype: either of + + * Vicious widget type: any synchronous widget type + :ref:`provided by Vicious ` or customly defined. + * ``function``: custom function from your own + awesome configuration can be registered as widget types + (see :ref:`custom-wtype`). + + :param format: either of + + * string: ``$key`` will be replaced by respective value in the table + ``t`` returned by the widget type, i.e. use ``$1``, ``$2``, etc. + to retrieve data from an integer-indexed table (a.k.a. array); + ``${foo bar}`` will be substituted by ``t["{foo bar}"]``. + * ``function (widget, args)`` can be used to manipulate data returned + by the widget type (see :ref:`format-func`). + + :param warg: arguments to be passed to the widget type, e.g. the battery ID. + + :return: ``nil`` if the widget type is asynchronous, + otherwise the formatted data from with widget type. + +.. lua:function:: vicious.call_async(wtype, format, warg, callback) + + Get formatted data from an asynchronous widget type. + + :param wtype: any asynchronous widget type + :ref:`provided by Vicious ` or customly defined. + + :param format: either of + + * string: ``$key`` will be replaced by respective value in the table + ``t`` returned by the widget type, i.e. use ``$1``, ``$2``, etc. + to retrieve data from an integer-indexed table (a.k.a. array); + ``${foo bar}`` will be substituted by ``t["{foo bar}"]``. + * ``function (widget, args)`` can be used to manipulate data returned + by the widget type (see :ref:`format-func`). + + :param warg: arguments to be passed to the widget type. + + :param callback: function taking the formatted data from with widget type. + If the given widget type happens to be synchronous, + ``nil`` will be passed to ``callback``. + +.. _awesome: https://awesomewm.org/ +.. _awful.widget.watch: + https://awesomewm.org/doc/api/classes/awful.widget.watch.html +.. _example automation script: + http://sysphere.org/~anrxc/local/sources/lmt-vicious.sh diff --git a/docs/source/usage-lua.rst b/docs/source/usage-lua.rst new file mode 100644 index 0000000..116886e --- /dev/null +++ b/docs/source/usage-lua.rst @@ -0,0 +1,15 @@ +Usage as a Lua Library +====================== + +When provided by an operating system package, or installed from source +into the Lua library path, Vicious can be used as a regular Lua_ library, +to be used stand-alone or to feed widgets of any window manager +(e.g. Ion, WMII). It is compatible with Lua 5.1 and above. + +.. code-block:: lua + + > widgets = require("vicious.widgets.init") + > print(widgets.volume(nil, "Master")[1]) + 100 + +.. _Lua: https://www.lua.org/ diff --git a/docs/source/widgets.rst b/docs/source/widgets.rst new file mode 100644 index 0000000..ad71b55 --- /dev/null +++ b/docs/source/widgets.rst @@ -0,0 +1,509 @@ +.. _widgets: + +Officially Supported Widget Types +================================= + +Widget types consist of worker functions that take two arguments +``format`` and ``warg`` (in that order), which were previously +passed to :lua:func:`vicious.register`, and return a table of values +to be formatted by ``format``. + +vicious.widgets.bat +------------------- + +Provides state, charge, and remaining time for a requested battery. + +Supported platforms: GNU/Linux (require ``sysfs``), +FreeBSD (require ``acpiconf``) and OpenBSD (no extra requirements). + +* ``warg`` (from now on will be called *argument*): + + * On GNU/Linux: battery ID, e.g. ``"BAT0"`` + * On FreeBSD (optional): battery ID, e.g. ``"batt"`` or ``"0"`` + * On OpenBSD (optional): ``bat`` followed by battery index, + e.g. ``"bat0"`` or ``"bat1"`` on systems with more than one battery + +* Returns an array (integer-indexed table) consisting of: + + * ``$1``: State of requested battery + * ``$2``: Charge level in percent + * ``$3``: Remaining (charging or discharging) time + * ``$4``: Wear level in percent + * ``$5``: Current (dis)charge rate in Watt + +vicious.contrib.cmus +-------------------- + +Provides cmus player information using ``cmus-remote``. + +Supported platforms: platform independent. + +* Argument: a table whose first field is the socket including host (or nil). +* Returns a table with string keys: ``${status}``, ``${artist}``, ``${title}``, + ``${duration}``, ``${file}``, ``${continue}``, ``${shuffle}``, ``${repeat}``. + +vicious.widgets.cpu +------------------- + +Provides CPU usage for all available CPUs/cores. Since this widget type give +CPU utilization between two consecutive calls, it is recommended to enable +caching if it is used to register multiple widgets (#71). + +Supported platforms: GNU/Linux, FreeBSD, OpenBSD. + +On FreeBSD and Linux returns an array containing: + +* ``$1``: usage of all CPUs/cores +* ``$2``, ``$3``, etc. are respectively the usage of 1st, 2nd, etc. CPU/core + +On OpenBSD returns an array containing: + +* ``$1``: usage of all CPUs/cores + +vicious.widgets.cpufreq +----------------------- + +Provides freq, voltage and governor info for a requested CPU. + +Supported platforms: GNU/Linux, FreeBSD. + +* Argument: CPU ID, e.g. ``"cpu0"`` on GNU/Linux, ``"0"`` on FreeBSD +* Returns an array containing: + + * ``$1``: Frequency in MHz + * ``$2``: Frequency in GHz + * ``$3``: Voltage in mV + * ``$4``: Voltage in V + * ``$5``: Governor state + * On FreeBSD: only the first two are supported + (other values will always be ``"N/A"``) + +vicious.widgets.cpuinf +---------------------- + +Provides speed and cache information for all available CPUs/cores. + +Supported platforms: GNU/Linux. + +Returns a table whose keys using CPU ID as a base, e.g. ``${cpu0 mhz}``, +``${cpu0 ghz}``, ``${cpu0 kb}``, ``${cpu0 mb}``, ``${cpu1 mhz}``, etc. + +vicious.widgets.date +-------------------- + +Provides access to Lua's ``os.date``, with optional settings for time format +and time offset. + +Supported platforms: platform independent. + +* ``format`` (optional): a `strftime(3)`_ format specification string + (format functions are not supported). If not provided, use the prefered + representation for the current locale. +* Argument (optional): time offset in seconds, e.g. for different a time zone. + If not provided, current time is used. +* Returns the output of ``os.date`` formatted by ``format`` *string*. + +vicious.widgets.dio +------------------- + +Provides I/O statistics for all available storage devices. + +Supported platforms: GNU/Linux. + +Returns a table with string keys: ``${sda total_s}``, ``${sda total_kb}``, +``${sda total_mb}``, ``${sda read_s}``, ``${sda read_kb}``, ``${sda read_mb}``, +``${sda write_s}``, ``${sda write_kb}``, ``${sda write_mb}``, +``${sda iotime_ms}``, ``${sda iotime_s}``, ``${sdb1 total_s}``, etc. + +vicious.widget.fanspeed +----------------------- + +Provides fanspeed information for specified fans. + +Supported platforms: FreeBSD. + +* Argument: full ``sysctl`` string to one or multiple entries, + e.g. ``"dev.acpi_ibm.0.fan_speed"`` +* Returns speed of specified fan in RPM, ``"N/A"`` on error + (probably wrong string) + +vicious.widgets.fs +------------------ + +Provides usage of disk space. + +Supported platforms: platform independent. + +* Argument (optional): if true includes remote filesystems, otherwise fallback + to default, where only local filesystems are included. +* Returns a table with string keys, using mount points as a base, + e.g. ``${/ size_mb}``, ``${/ size_gb}``, ``${/ used_mb}``, ``${/ used_gb}``, + ``${/ used_p}``, ``${/ avail_mb}``, ``${/ avail_gb}``, ``${/ avail_p}``, + ``${/home size_mb}``, etc. + mb and gb refer to mebibyte and gibibyte respectively. + +vicious.widgets.gmail +--------------------- + +Provides count of new and subject of last e-mail on Gmail. + +Supported platform: platform independent, requiring ``curl``. + +This widget expects login information in your ``~/.netrc`` file, e.g. +``machine mail.google.com login user password pass``. Use your `app +password`_ if you can, or disable `two step verification`_ +and `allow access for less secure apps`_. + +.. caution:: + + Making these settings is a security risk! + +* Arguments (optional): either a number or a table + + * If it is a number, subject will be truncated. + * If it is a table whose first field is the maximum length and second field + is the widget name (e.g. ``"gmailwidget"``), scrolling will be used. + +* Returns a table with string keys: ``${count}`` and ``${subject}`` + +vicious.widgets.hddtemp +----------------------- + +Provides hard drive temperatures using the hddtemp daemon. + +Supported platforms: GNU/Linux, requiring ``hddtemp`` and ``curl``. + +* Argument (optional): ``hddtemp`` listening port (default: 7634) +* Returns a table with string keys, using hard drives as a base, e.g. + ``${/dev/sda}`` and ``${/dev/sdc}``. + +vicious.widgets.hwmontemp +------------------------- + +Provides name-based access to hwmon devices via sysfs. + +Supported platforms: GNU/Linux + +* Argument: an array with sensor name and input number + (optional, falling back to ``1``), e.g. ``{"radeon", 2}`` +* Returns a table with just the temperature value: ``$1`` +* Usage example: + + .. code-block:: lua + + gputemp = wibox.widget.textbox() + vicious.register(gputemp, vicious.widgets.hwmontemp, " $1°C", 5, {"radeon"}) + +vicious.widgets.mbox +-------------------- + +Provides the subject of last e-mail in a mbox file. + +Supported platforms: platform independent. + +* Argument: either a string or a table: + + * A string representing the full path to the mbox, or + * Array of the form ``{path, maximum_length[, widget_name]}``. + If the widget name is provided, scrolling will be used. + * Note: the path will be escaped so special variables like ``~`` will not + work, use ``os.getenv`` instead to access environment variables. + +* Returns an array whose first value is the subject of the last e-mail. + +vicious.widgets.mboxc +--------------------- + +Provides the count of total, old and new messages in mbox files. + +Supported platforms: platform independent. + +* Argument: an array full paths to mbox files. +* Returns an array containing: + + * ``$1``: Total number of messages + * ``$2``: Number of old messages + * ``$3``: Number of new messages + +vicious.widgets.mdir +-------------------- + +Provides the number of unread messages in Maildir structures/directories. + +Supported platforms: platform independent. + +* Argument: an array with full paths to Maildir structures. +* Returns an array containing: + + * ``$1``: Number of new messages + * ``$2``: Number of *old* messages lacking the *Seen* flag + +vicious.widgets.mem +------------------- + +Provides RAM and Swap usage statistics. + +Supported platforms: GNU/Linux, FreeBSD. + +Returns (per platform): +* GNU/Linux: an array consisting of: + + * ``$1``: Memory usage in percent + * ``$2``: Memory usage in MiB + * ``$3``: Total system memory in MiB + * ``$4``: Free memory in MiB + * ``$5``: Swap usage in percent + * ``$6``: Swap usage in MiB + * ``$7``: Total system swap in MiB + * ``$8``: Free swap in MiB + * ``$9``: Memory usage with buffers and cache, in MiB + +* FreeBSD: an array including: + + * ``$1``: Memory usage in percent + * ``$2``: Memory usage in MiB + * ``$3``: Total system memory in MiB + * ``$4``: Free memory in MiB + * ``$5``: Swap usage in percent + * ``$6``: Swap usage in MiB + * ``$7``: Total system swap in MiB + * ``$8``: Free swap in MiB + * ``$9``: Wired memory in percent + * ``$10``: Wired memory in MiB + * ``$11``: Unfreeable memory (basically active+inactive+wired) in percent + * ``$12``: Unfreeable memory in MiB + +vicious.widgets.mpd +------------------- + +Provides Music Player Daemon information. + +Supported platforms: platform independent (required tools: ``curl``). + +* Argument: an array including password, hostname and port in that order. + ``nil`` fields will be fallen back to default + (``localhost:6600`` without password). +* Returns a table with string keys: ``${volume}``, ``${bitrate}``, + ``${elapsed}`` (in seconds), ``${duration}`` (in seconds), + ``${Elapsed}`` (formatted as [hh:]mm:ss), + ``${Duration}`` (formatted as [hh:]mm:ss), ``${Progress}`` (in percentage), + ``${random}``, ``${repeat}``, ``${state}``, ``${Artist}``, ``${Title}``, + ``${Album}``, ``${Genre}`` and optionally ``${Name}`` and ``${file}``. + +vicious.widgets.net +------------------- + +Provides state and usage statistics of network interfaces. + +Supported platforms: GNU/Linux, FreeBSD. + +* Argument (FreeBSD only): desired interface, e.g. ``"wlan0"`` +* Returns (per platform): + + * GNU/Linux: a table with string keys, using net interfaces as a base, + e.g. ``${eth0 carrier}``, ``${eth0 rx_b}``, ``${eth0 tx_b}``, + ``${eth0 rx_kb}``, ``${eth0 tx_kb}``, ``${eth0 rx_mb}``, + ``${eth0 tx_mb}``, ``${eth0 rx_gb}``, ``${eth0 tx_gb}``, + ``${eth0 down_b}``, ``${eth0 up_b}``, ``${eth0 down_kb}``, + ``${eth0 up_kb}``, ``${eth0 down_mb}``, ``${eth0 up_mb}``, + ``${eth0 down_gb}``, ``${eth0 up_gb}``, ``${eth1 rx_b}``, etc. + * FreeBSD: a table with string keys: ``${carrier}``, ``${rx_b}``, ``${tx_b}``, + ``${rx_kb}``, ``${tx_kb}``, ``${rx_mb}``, ``${tx_mb}``, ``${rx_gb}``, + ``${tx_gb}``, ``${down_b}``, ``${up_b}``, ``${down_kb}``, ``${up_kb}``, + ``${down_mb}``, ``${up_mb}``, ``${down_gb}``, ``${up_gb}``. + +vicious.widgets.notmuch +----------------------- + +Provides a message count according to an arbitrary Notmuch query. + +Supported platforms: platform independent. + +Argument: the query that is passed to Notmuch. For instance: +``tag:inbox AND tag:unread`` returns the number of unread messages with +tag "inbox". + +Returns a table with string keys containing: + +* ``${count}``: the count of messages that match the query + + +vicious.widgets.org +------------------- + +Provides agenda statistics for Emacs org-mode. + +Supported platforms: platform independent. + +* Argument: an array of full paths to agenda files, + which will be parsed as arguments. +* Returns an array consisting of + + * ``$1``: Number of tasks you forgot to do + * ``$2``: Number of tasks for today + * ``$3``: Number of tasks for the next 3 days + * ``$4``: Number of tasks to do in the week + +vicious.widgets.os +------------------ + +Provides operating system information. + +Supported platforms: platform independent. + +Returns an array containing: +* ``$1``: Operating system in use +* ``$2``: Release version +* ``$3``: Username +* ``$4``: Hostname +* ``$5``: Available system entropy +* ``$6``: Available entropy in percent + +vicious.widgets.pkg +------------------- + +Provides number of pending updates on UNIX systems. Be aware that some package +managers need to update their local databases (as root) before showing the +correct number of updates. + +Supported platforms: platform independent, although it requires Awesome +``awful.spawn`` library for non-blocking spawning. + +* Argument: distribution name, e.g. ``"Arch"``, ``"Arch C"``, ``"Arch S"``, + ``"Debian"``, ``"Ubuntu"``, ``"Fedora"``, ``"FreeBSD"``, ``"Mandriva"``. +* Returns an array including: + + * ``$1``: Number of available updates + * ``$2``: Packages available for update + +vicious.widgets.raid +-------------------- + +Provides state information for a requested RAID array. + +Supported platforms: GNU/Linux. + +* Argument: the RAID array ID. +* Returns an array containing: + + * ``$1``: Number of assigned devices + * ``$2``: Number of active devices + +vicious.widgets.thermal +----------------------- + +Provides temperature levels of several thermal zones. + +Supported platforms: GNU/Linux, FreeBSD. + +* Argument (per platform): + + * GNU/Linux: either a string - the thermal zone, e.g. ``"thermal_zone0"``, + or a table of the form ``{thermal_zone, data_source[, input_file]}``. + Available ``data_source``'s and corresponding default ``input_file`` + are given in the table below. For instance, if ``"thermal_zone0"`` + is passed, temperature would be read from + ``/sys/class/thermal/thermal_zone0/temp``. This widget type is confusing + and ugly but it is kept for backward compatibility. + * FreeBSD: either a full ``sysctl`` path to a thermal zone, e.g. + ``"hw.acpi.thermal.tz0.temperature"``, or a table with multiple paths. + +* Returns (per platform): + + * GNU/Linux: an array whose first value is the requested temperature. + * FreeBSD: a table whose keys are provided paths thermal zones. + +=============== ======================== ====================== +``data_source`` Path Default ``input_file`` +=============== ======================== ====================== + ``"sys"`` /sys/class/thermal/ ``"temp"`` + ``"core"`` /sys/devices/platform/ ``"temp2_input"`` + ``"hwmon"`` /sys/class/hwmon/ ``"temp1_input"`` + ``"proc"`` /proc/acpi/thermal_zone/ ``"temperature"`` +=============== ======================== ====================== + +vicious.widgets.uptime +---------------------- + +Provides system uptime and load information. + +Supported platforms: GNU/Linux, FreeBSD. + +Returns an array containing: + +* ``$1``: Uptime in days +* ``$2``: Uptime in hours +* ``$3``: Uptime in minutes +* ``$4``: Load average in the past minute +* ``$5``: Load average in the past 5 minutes +* ``$6``: Load average in the past 15 minutes + +vicious.widgets.volume +---------------------- + +Provides volume levels and state of requested mixers. + +Supported platforms: GNU/Linux (requiring ``amixer``), FreeBSD. + +* Argument (per platform): + + * GNU/Linux: either a string containing the ALSA mixer control + (e.g. ``"Master"``) or a table including command line arguments + to be passed to `amixer(1)`_, e.g. ``{"PCM", "-c", "0"}`` + or ``{"Master", "-D", "pulse"}`` + * FreeBSD: the mixer control, e.g. ``"vol"`` + +* Returns an array consisting of (per platform): + + * GNU/Linux: ``$1`` as the volume level and ``$2`` as the mute state of + the requested control + * FreeBSD: ``$1`` as the volume level of the *left* channel, ``$2`` as the + volume level of the *right* channel and ``$3`` as the mute state of the + desired control + +vicious.widgets.weather +----------------------- + +Provides weather information for a requested station. + +Supported platforms: any having Awesome and ``curl`` installed. + +* Argument: the ICAO station code, e.g. ``"LDRI"`` +* Returns a table with string keys: ``${city}``, ``${wind}``, ``${windmph}``, + ``${windkmh}``, ``${sky}``, ``${weather}``, ``${tempf}``, ``${tempc}``, + ``${humid}``, ``${dewf}``, ``${dewc}`` and ``${press}``, ``${when}`` + +vicious.widgets.wifi +-------------------- + +Provides wireless information for a requested interface. + +Supported platforms: GNU/Linux. + +* Argument: the network interface, e.g. ``"wlan0"`` +* Returns a table with string keys: ``${ssid}``, ``${mode}``, + ``${chan}``, ``${rate}`` (Mb/s), ``${freq}`` (MHz), + ``${txpw}`` (transmission power, in dBm), ``${sign}`` (signal level), + ``${link}`` and ``${linp}`` (link quality per 70 and per cent) + +vicious.widgets.wifiiw +---------------------- + +Provides wireless information for a requested interface (similar to +vicious.widgets.wifi, but uses ``iw`` instead of ``iwconfig``). + +Supported platforms: GNU/Linux. + +* Argument: the network interface, e.g. ``"wlan0"`` +* Returns a table with string keys: ``${bssid}``, ``${ssid}``, + ``${mode}``, ``${chan}``, ``${rate}`` (Mb/s), ``${freq}`` (MHz), + ``${linp}`` (link quality in percent), + ``${txpw}`` (transmission power, in dBm) + and ``${sign}`` (signal level, in dBm) + +.. _strftime(3): https://linux.die.net/man/3/strftime +.. _app password: https://support.google.com/accounts/answer/185833?hl=en +.. _two step verification: https://support.google.com/accounts/answer/1064203 +.. _allow access for less secure apps: + https://www.google.com/settings/security/lesssecureapps +.. _amixer(1): https://linux.die.net/man/1/amixer diff --git a/helpers.lua b/helpers.lua new file mode 100644 index 0000000..e7807e5 --- /dev/null +++ b/helpers.lua @@ -0,0 +1,309 @@ +-- helper functions +-- Copyright (C) 2009 Benedikt Sauer +-- Copyright (C) 2009 Henning Glawe +-- Copyright (C) 2009 Lucas de Vries +-- Copyright (C) 2009 Rémy C. +-- Copyright (C) 2009-2012 Adrian C. (anrxc) +-- Copyright (C) 2011 Joerg T. (Mic92) +-- Copyright (C) 2012 Arvydas Sidorenko +-- Copyright (C) 2012 Jörg Thalheim +-- Copyright (C) 2014-2015 Jörg Thalheim +-- Copyright (C) 2017 Joerg Thalheim +-- Copyright (C) 2017,2019 mutlusun +-- Copyright (C) 2017-2018 Jörg Thalheim +-- Copyright (C) 2018-2019 Nguyễn Gia Phong +-- Copyright (C) 2019 Alexander Koch +-- Copyright (C) 2019 Enric Morales +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local ipairs = ipairs +local pairs = pairs +local rawget = rawget +local require = require +local tonumber = tonumber +local tostring = tostring +local io = { open = io.open, popen = io.popen } +local setmetatable = setmetatable +local getmetatable = getmetatable +local string = { + upper = string.upper, + lower = string.lower, + format = string.format, + match = string.match, + find = string.find, +} +local table = { concat = table.concat } +local pcall = pcall +local assert = assert +local spawn = require("vicious.spawn") +-- }}} + + +-- Helpers: provides helper functions for vicious widgets +-- vicious.helpers +local helpers = {} + +-- {{{ Constants definitions +local OS_UNSUPPORTED_ERR = "Vicious: platform not supported: %s" +local NOT_FOUND_MSG = "module '%s' not found" +local NOT_FOUND_ERR = [[ +Vicious: %s is not available for the current platform or does not exist]] +-- }}} + +-- {{{ Variable definitions +local scroller = {} +-- }}} + +-- {{{ Helper functions +-- {{{ Determine operating system +local kernel_name +function helpers.getos() + if kernel_name ~= nil then + return kernel_name + end + + local f = io.popen("uname -s") + kernel_name = string.lower(f:read("*line")) + f:close() + + return kernel_name +end +-- }}} + +-- {{{ Loader of vicious modules +function helpers.wrequire(collection, key) + local ret = rawget(collection, key) + + if ret then + return ret + end + + local ostable = { + linux = { "linux", "all" }, + freebsd = { "freebsd", "bsd", "all" }, + openbsd = { "openbsd", "bsd", "all" } + } + + local platform = ostable[helpers.getos()] + assert(platform, OS_UNSUPPORTED_ERR:format(helpers.getos())) + + local basename = collection._NAME .. '.' .. key + for i = 1, #platform do + local name = basename .. '_' .. platform[i] + local status, value = pcall(require, name) + if status then + ret = value + break + end + + -- This is ugly but AFAWK there is no other way to check for + -- the type of error. If other error get caught, raise it. + if value:find(NOT_FOUND_MSG:format(name), 1, true) == nil then + require(name) + end + end + + assert(ret, NOT_FOUND_ERR:format(basename)) + return ret +end +-- }}} + +-- {{{ Set widget type's __call metamethod to given worker function +function helpers.setcall(worker) + return setmetatable( + {}, { __call = function(_, ...) return worker(...) end }) +end +-- }}} + +-- {{{ Set __call metamethod to widget type table having async key +function helpers.setasyncall(wtype) + local function worker(format, warg) + local ret + wtype.async(format, warg, function (data) ret = data end) + while ret == nil do end + return ret + end + local metatable = { __call = function (_, ...) return worker(...) end } + return setmetatable(wtype, metatable) +end +-- }}} + +-- {{{ Expose path as a Lua table +function helpers.pathtotable(dir) + return setmetatable({ _path = dir }, + { __index = function(self, index) + local path = self._path .. '/' .. index + local f = io.open(path) + if f then + local s = f:read("*all") + f:close() + if s then + return s + else + local o = { _path = path } + setmetatable(o, getmetatable(self)) + return o + end + end + end + }) +end +-- }}} + +-- {{{ Format a string with args +function helpers.format(format, args) + for var, val in pairs(args) do + format = format:gsub("$" .. (tonumber(var) and var or + var:gsub("[-+?*]", function(i) return "%"..i end)), + val) + end + + return format +end +-- }}} + +-- {{{ Format units to one decimal point +function helpers.uformat(array, key, value, unit) + for u, v in pairs(unit) do + array["{"..key.."_"..u.."}"] = string.format("%.1f", value/v) + end + + return array +end +-- }}} + +-- {{{ Escape a string +function helpers.escape(text) + local xml_entities = { + ["\""] = """, + ["&"] = "&", + ["'"] = "'", + ["<"] = "<", + [">"] = ">" + } + + return text and text:gsub("[\"&'<>]", xml_entities) +end +-- }}} + +-- {{{ Escape a string for safe usage on the command line +function helpers.shellquote(arg) + local s = tostring(arg) + if s == nil then return "" end + -- use single quotes, and put single quotes into double quotes + -- the string $'b is then quoted as '$'"'"'b'"'"' + return "'" .. s:gsub("'", "'\"'\"'") .. "'" +end +-- }}} + +-- {{{ Capitalize a string +function helpers.capitalize(text) + return text and text:gsub("([%w])([%w]*)", function(c, s) + return string.upper(c) .. s + end) +end +-- }}} + +-- {{{ Truncate a string +function helpers.truncate(text, maxlen) + local txtlen = text:len() + + if txtlen > maxlen then + text = text:sub(1, maxlen - 3) .. "..." + end + + return text +end +-- }}} + +-- {{{ Scroll through a string +function helpers.scroll(text, maxlen, widget) + if not scroller[widget] then + scroller[widget] = { i = 1, d = true } + end + + local txtlen = text:len() + local state = scroller[widget] + + if txtlen > maxlen then + if state.d then + text = text:sub(state.i, state.i + maxlen) .. "..." + state.i = state.i + 3 + + if maxlen + state.i >= txtlen then + state.d = false + end + else + text = "..." .. text:sub(state.i, state.i + maxlen) + state.i = state.i - 3 + + if state.i <= 1 then + state.d = true + end + end + end + + return text +end +-- }}} + +-- {{{ Parse output of sysctl command calling the `parse` function +function helpers.sysctl_async(path_table, parse) + local ret = {} + local path = {} + + for i=1,#path_table do + path[i] = helpers.shellquote(path_table[i]) + end + + path = table.concat(path, " ") + + spawn.with_line_callback("sysctl " .. path, { + stdout = function (line) + local separators = { + freebsd = ": ", + linux = " = ", + openbsd = "=" + } + local pattern = ("(.+)%s(.+)"):format(separators[helpers.getos()]) + local key, value = string.match(line, pattern) + ret[key] = value + end, + stderr = function (line) + local messages = { + openbsd = { "level name .+ in (.+) is invalid" }, + linux = { "cannot stat /proc/sys/(.+):", + "permission denied on key '(.+)'" }, + freebsd = { "unknown oid '(.+)'" } + } + + for _, error_message in ipairs(messages[helpers.getos()]) do + local key = line:match(error_message) + if key then + key = key:gsub("/", ".") + ret[key] = "N/A" + end + end + end, + output_done = function () parse(ret) end + }) +end +-- }}} + +return helpers +-- }}} diff --git a/init.lua b/init.lua new file mode 100644 index 0000000..d8a293a --- /dev/null +++ b/init.lua @@ -0,0 +1,356 @@ +-- Vicious module initialization +-- Copyright (C) 2009 Lucas de Vries +-- Copyright (C) 2009-2013 Adrian C. (anrxc) +-- Copyright (C) 2011-2017 Joerg Thalheim +-- Copyright (C) 2012 Arvydas Sidorenko +-- Copyright (C) 2013 Dodo +-- Copyright (C) 2014 blastmaster +-- Copyright (C) 2015,2019 Daniel Hahler +-- Copyright (C) 2017 James Reed +-- Copyright (C) 2017 getzze +-- Copyright (C) 2017 mutlusun +-- Copyright (C) 2018 Beniamin Kalinowski +-- Copyright (C) 2018,2020 Nguyễn Gia Phong +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Setup environment +local type = type +local pairs = pairs +local tonumber = tonumber +local timer = type(timer) == "table" and timer or require("gears.timer") +local os = { time = os.time } +local table = { + insert = table.insert, + remove = table.remove +} +local helpers = require("vicious.helpers") + +-- Vicious: widgets for the awesome window manager +local vicious = {} +vicious.widgets = require("vicious.widgets") +--vicious.contrib = require("vicious.contrib") + +-- Initialize tables +local timers = {} +local registered = {} +local widget_cache = {} +-- }}} + + +-- {{{ Local functions +-- {{{ Update a widget +local function update(widget, reg, disablecache) + -- Check if there are any equal widgets + if reg == nil then + for w, i in pairs(registered) do + if w == widget then + for _, r in pairs(i) do + update(w, r, disablecache) + end + end + end + + return + end + + local update_time = os.time() + + local function format_data(data) + local ret + if type(data) == "table" then + local escaped_data = {} + for k, v in pairs(data) do + if type(v) == "string" then + escaped_data[k] = helpers.escape(v) + else + escaped_data[k] = v + end + end + + if type(reg.format) == "string" then + ret = helpers.format(reg.format, escaped_data) + elseif type(reg.format) == "function" then + ret = reg.format(widget, escaped_data) + end + end + return ret or data + end + + local function topercent(e) return tonumber(e) and tonumber(e) / 100 end + + local function update_value(data) + local fmtd_data = format_data(data) + if widget.add_value ~= nil then + if widget.get_stack ~= nil and widget:get_stack() then + for idx, _ in ipairs(widget:get_stack_colors()) do + if fmtd_data[idx] then + widget:add_value(topercent(fmtd_data[idx]), idx) + end + end + else + widget:add_value(topercent(fmtd_data)) + end + elseif widget.set_value ~= nil then + widget:set_value(topercent(fmtd_data)) + elseif widget.set_markup ~= nil then + widget:set_markup(fmtd_data) + else + widget.text = fmtd_data + end + end + + local function update_cache(data, t, cache) + -- Update cache + if t and cache then + cache.time, cache.data = t, data + end + end + + -- Check for cached output newer than the last update + local c = widget_cache[reg.wtype] + if c and update_time < c.time + reg.timeout and not disablecache then + update_value(c.data) + elseif reg.wtype then + if type(reg.wtype) == "table" and reg.wtype.async then + if not reg.lock then + reg.lock = true + return reg.wtype.async(reg.format, + reg.warg, + function(data) + update_cache(data, update_time, c) + update_value(data) + reg.lock=false + end) + end + else + local data = reg.wtype(reg.format, reg.warg) + update_cache(data, update_time, c) + update_value(data) + end + end +end +-- }}} + +-- {{{ Register from reg object +local function regregister(reg) + if not reg.running then + if registered[reg.widget] == nil then + registered[reg.widget] = {} + table.insert(registered[reg.widget], reg) + else + local already = false + + for w, i in pairs(registered) do + if w == reg.widget then + for _, v in pairs(i) do + if v == reg then + already = true + break + end + end + + if already then + break + end + end + end + + if not already then + table.insert(registered[reg.widget], reg) + end + end + + -- Start the timer + if reg.timeout > 0 then + local tm = timers[reg.timeout] and timers[reg.timeout].timer + tm = tm or timer({ timeout = reg.timeout }) + if tm.connect_signal then + tm:connect_signal("timeout", reg.update) + else + tm:add_signal("timeout", reg.update) + end + if not timers[reg.timeout] then + timers[reg.timeout] = { timer = tm, refs = 1 } + else + timers[reg.timeout].refs = timers[reg.timeout].refs + 1 + end + if not tm.started then + tm:start() + end + -- Initial update + reg.update() + end + reg.running = true + end +end +-- }}} +-- }}} + + +-- {{{ Global functions +-- {{{ Register a widget +function vicious.register(widget, wtype, format, timeout, warg) + local reg = { + -- Set properties + wtype = wtype, + lock = false, + format = format, + timeout = timeout or 2, + warg = warg, + widget = widget, + } + reg.timer = timeout -- For backward compatibility. + + -- Set functions + function reg.update() + update(widget, reg) + end + + -- Register a reg object + regregister(reg) + + -- Return a reg object for reuse + return reg +end +-- }}} + +-- {{{ Unregister a widget +function vicious.unregister(widget, keep, reg) + if reg == nil then + for w, i in pairs(registered) do + if w == widget then + for _, v in pairs(i) do + reg = vicious.unregister(w, keep, v) + end + end + end + + return reg + end + + if not keep then + for w, i in pairs(registered) do + if w == widget then + for k, v in pairs(i) do + if v == reg then + table.remove(registered[w], k) + end + end + end + end + end + + if not reg.running then + return reg + end + + -- Disconnect from timer + local tm = timers[reg.timeout] + if tm.timer.disconnect_signal then + tm.timer:disconnect_signal("timeout", reg.update) + else + tm.timer:remove_signal("timeout", reg.update) + end + reg.running = false + -- Stop the timer + tm.refs = tm.refs - 1 + if tm.refs == 0 and tm.timer.started then + tm.timer:stop() + end + + return reg +end +-- }}} + +-- {{{ Enable caching of a widget type +function vicious.cache(wtype) + if wtype ~= nil then + if widget_cache[wtype] == nil then + widget_cache[wtype] = { data = nil, time = 0 } + end + end +end +-- }}} + +-- {{{ Force update of widgets +function vicious.force(wtable) + if type(wtable) == "table" then + for _, w in pairs(wtable) do + update(w, nil, true) + end + end +end +-- }}} + +-- {{{ Suspend all widgets +function vicious.suspend() + for w, i in pairs(registered) do + for _, v in pairs(i) do + vicious.unregister(w, true, v) + end + end +end +-- }}} + +-- {{{ Activate a widget +function vicious.activate(widget) + for w, i in pairs(registered) do + if widget == nil or w == widget then + for _, v in pairs(i) do + regregister(v) + end + end + end +end +-- }}} + +-- {{{ Get formatted data from a synchronous widget type +function vicious.call(wtype, format, warg) + if wtype.async ~= nil then return nil end + + local data = wtype(format, warg) + if type(format) == "string" then + return helpers.format(format, data) + elseif type(format) == "function" then + return format(wtype, data) + end +end +-- }}} + +-- {{{ Get formatted data from an asynchronous widget type +function vicious.call_async(wtype, format, warg, callback) + if wtype.async == nil then + callback() + return + end + + wtype.async( + format, warg, + function (data) + if type(format) == "string" then + callback(helpers.format(format, data)) + elseif type(format) == "function" then + callback(format(wtype, data)) + else + callback() + end + end) +end +-- }}} + +return vicious +-- }}} diff --git a/rc.lua b/rc.lua new file mode 100644 index 0000000..32add2c --- /dev/null +++ b/rc.lua @@ -0,0 +1,650 @@ +-- If LuaRocks is installed, make sure that packages installed through it are +-- found (e.g. lgi). If LuaRocks is not installed, do nothing. +pcall(require, "luarocks.loader") + +-- Standard awesome library +local gears = require("gears") +local awful = require("awful") +require("awful.autofocus") +-- Widget and layout library +local wibox = require("wibox") +-- Theme handling library +local beautiful = require("beautiful") +-- Notification library +local naughty = require("naughty") +local menubar = require("menubar") +local hotkeys_popup = require("awful.hotkeys_popup") +--local hotkeys = require("awful.hotkeys_popup") +--local hotkeys_popup = hotkeys_popup.widget.new({ width = 100, height = 100 }) +--hotkeys_popup.show_help = hotkeys_popup.widget.show_help +-- Enable hotkeys help widget for VIM and other apps +-- when client with a matching name is opened: +require("awful.hotkeys_popup.keys.vim") +--local cairo = require("lgi").cairo + +-- My custom libs +local vicious = require("vicious") + +-- {{{ Error handling +-- Check if awesome encountered an error during startup and fell back to +-- another config (This code will only ever execute for the fallback config) +if awesome.startup_errors then + naughty.notify({ preset = naughty.config.presets.critical, + title = "Oops, there were errors during startup!", + text = awesome.startup_errors }) +end + +-- Handle runtime errors after startup +do + local in_error = false + awesome.connect_signal("debug::error", function (err) + -- Make sure we don't go into an endless error loop + if in_error then return end + in_error = true + + naughty.notify({ preset = naughty.config.presets.critical, + title = "Oops, an error happened!", + text = tostring(err) }) + in_error = false + end) +end +-- }}} + +-- {{{ Variable definitions +-- Themes define colours, icons, font and wallpapers. +--beautiful.init(gears.filesystem.get_themes_dir() .. "default/theme.lua") +local theme_path = string.format("%s/.config/awesome/themes/%s/theme.lua", os.getenv("HOME"), "zenburn") +beautiful.init(theme_path) +beautiful.font = "Indie Flower Bold 12" +--local cr = cairo.Context() +--beautiful.hotkeys_shape = gears.shape.rect(cr, 10, 10, 10) +--beautiful.useless_gap = 3 + + +-- This is used later as the default terminal and editor to run. +terminal = "kitty" +editor = os.getenv("EDITOR") or "vim" +editor_cmd = terminal .. " -e " .. editor + +-- Default modkey. +-- Usually, Mod4 is the key with a logo between Control and Alt. +-- If you do not like this or do not have such a key, +-- I suggest you to remap Mod4 to another key using xmodmap or other tools. +-- However, you can use another modifier like Mod1, but it may interact with others. +modkey = "Mod4" + +-- Table of layouts to cover with awful.layout.inc, order matters. +awful.layout.layouts = { + awful.layout.suit.floating, + awful.layout.suit.tile, + awful.layout.suit.tile.left, + awful.layout.suit.tile.bottom, + awful.layout.suit.tile.top, + awful.layout.suit.fair, + awful.layout.suit.fair.horizontal, + awful.layout.suit.spiral, + awful.layout.suit.spiral.dwindle, + awful.layout.suit.max, + awful.layout.suit.max.fullscreen, + awful.layout.suit.magnifier, + awful.layout.suit.corner.nw, + -- awful.layout.suit.corner.ne, + -- awful.layout.suit.corner.sw, + -- awful.layout.suit.corner.se, +} +-- }}} + +-- {{{ Menu +-- Create a launcher widget and a main menu +myawesomemenu = { + { "hotkeys", function() hotkeys_popup.show_help(nil, awful.screen.focused()) end }, + { "manual", terminal .. " -e man awesome" }, + { "edit config", editor_cmd .. " " .. awesome.conffile }, + { "restart", awesome.restart }, + { "quit", function() awesome.quit() end }, +} + +mymainmenu = awful.menu({ items = { { "awesome", myawesomemenu, beautiful.awesome_icon }, + { "terminal", terminal } + } + }) + +mylauncher = awful.widget.launcher({ image = beautiful.awesome_icon, + menu = mymainmenu }) + +-- Menubar configuration +menubar.utils.terminal = terminal -- Set the terminal for applications that require it +-- }}} + + +-- Keyboard map indicator and switcher +mykeyboardlayout = awful.widget.keyboardlayout() + +-- {{{ Wibar + +-- Create a wibox for each screen and add it +local taglist_buttons = gears.table.join( + awful.button({ }, 1, function(t) t:view_only() end), + awful.button({ modkey }, 1, function(t) + if client.focus then + client.focus:move_to_tag(t) + end + end), + awful.button({ }, 3, awful.tag.viewtoggle), + awful.button({ modkey }, 3, function(t) + if client.focus then + client.focus:toggle_tag(t) + end + end), + awful.button({ }, 4, function(t) awful.tag.viewnext(t.screen) end), + awful.button({ }, 5, function(t) awful.tag.viewprev(t.screen) end) + ) + +local tasklist_buttons = gears.table.join( + awful.button({ }, 1, function (c) + if c == client.focus then + c.minimized = true + else + c:emit_signal( + "request::activate", + "tasklist", + {raise = true} + ) + end + end), + awful.button({ }, 3, function() + awful.menu.client_list({ theme = { width = 250 } }) + end), + awful.button({ }, 4, function () + awful.client.focus.byidx(1) + end), + awful.button({ }, 5, function () + awful.client.focus.byidx(-1) + end)) + +local function set_wallpaper(s) + -- Wallpaper + if beautiful.wallpaper then + local wallpaper = beautiful.wallpaper + -- If wallpaper is a function, call it with the screen + if type(wallpaper) == "function" then + wallpaper = wallpaper(s) + end + gears.wallpaper.maximized(wallpaper, s, true) + end +end + +---- + +datewidget = wibox.widget.textbox() +vicious.register(datewidget, vicious.widgets.date, "%b %d, %R ") + +--memwidget = wibox.widget.textbox() +--vicious.cache(vicious.widgets.mem) +--vicious.register(memwidget, vicious.widgets.mem, " $2/$3 $1% $6/$7 $5% ", 1) + +-- Create wibox with batwidget +batwidget = wibox.widget.progressbar() +batbox = wibox.layout.margin( + wibox.widget{ { max_value = 1, widget = batwidget, + border_width = 0.5, border_color = "#000000", + color = { type = "linear", + from = { 0, 0 }, + to = { 0, 30 }, + stops = { { 0, "#AECF96" }, + { 1, "#FF5656" } } } }, + forced_height = 10, forced_width = 8, + direction = 'east', color = beautiful.fg_widget, + layout = wibox.container.rotate }, + 1, 4, 1, 1) + +-- Register battery widget +vicious.register(batwidget, vicious.widgets.bat, "$2", 1, "BAT1") + + +cpuwidget = awful.widget.graph() +cpuwidget:set_width(50) +cpuwidget:set_background_color"#4A3F9800" +cpuwidget:set_color{ type = "linear", from = { 0, 0 }, to = { 50, 0 }, + stops = { { 0, "#4A3F9800" }, + { 0.1, "#FF5656" }, + { 0.95, "#4A3F9800" } } } +vicious.register(cpuwidget, vicious.widgets.cpu, "$1", 1) + +memwidget = awful.widget.graph() +memwidget:set_width(50) +memwidget:set_background_color"#4A3F9800" +memwidget:set_color{ type = "linear", from = { 0, 0 }, to = { 50, 0 }, + stops = { { 0, "#4A3F9800" }, + { 0.1, "#FF5656" }, + { 0.95, "#4A3F9800" } } } +vicious.register(memwidget, vicious.widgets.mem, "$1", 1) + +swapwidget = awful.widget.graph() +swapwidget:set_width(50) +swapwidget:set_background_color"#4A3F9800" +swapwidget:set_color{ type = "linear", from = { 0, 0 }, to = { 50, 0 }, + stops = { { 0, "#4A3F9800" }, + { 0.1, "#FF5656" }, + { 0.95, "#4A3F9800" } } } +vicious.register(swapwidget, vicious.widgets.mem, "$5", 1) + +---- + +-- Re-set wallpaper when a screen's geometry changes (e.g. different resolution) +screen.connect_signal("property::geometry", set_wallpaper) + +awful.screen.connect_for_each_screen(function(s) + -- Wallpaper + set_wallpaper(s) + + -- Each screen has its own tag table. + awful.tag({ "1", "2", "3", "4", "5", "6", "7", "8", "9" }, s, awful.layout.layouts[1]) + + -- Create a promptbox for each screen + s.mypromptbox = awful.widget.prompt() + -- Create an imagebox widget which will contain an icon indicating which layout we're using. + -- We need one layoutbox per screen. + s.mylayoutbox = awful.widget.layoutbox(s) + s.mylayoutbox:buttons(gears.table.join( + awful.button({ }, 1, function () awful.layout.inc( 1) end), + awful.button({ }, 3, function () awful.layout.inc(-1) end), + awful.button({ }, 4, function () awful.layout.inc( 1) end), + awful.button({ }, 5, function () awful.layout.inc(-1) end))) + -- Create a taglist widget + s.mytaglist = awful.widget.taglist { + screen = s, + filter = awful.widget.taglist.filter.all, + buttons = taglist_buttons + } + + -- Create a tasklist widget + s.mytasklist = awful.widget.tasklist { + screen = s, + filter = awful.widget.tasklist.filter.currenttags, + buttons = tasklist_buttons + } + + -- Create the wibox + s.mywibox = awful.wibar({ position = "bottom", screen = s }) + + -- Add widgets to the wibox + s.mywibox:setup { + layout = wibox.layout.align.horizontal, + { -- Left widgets + layout = wibox.layout.fixed.horizontal, + mylauncher, + s.mytaglist, + s.mypromptbox, + }, + s.mytasklist, -- Middle widget + { -- Right widgets + layout = wibox.layout.fixed.horizontal, + wibox.widget.systray(), + memwidget, + swapwidget, + cpuwidget, + mykeyboardlayout, + datewidget, + batbox, + s.mylayoutbox, + }, + } +end) +-- }}} + +-- {{{ Mouse bindings +root.buttons(gears.table.join( + awful.button({ }, 3, function () mymainmenu:toggle() end), + awful.button({ }, 4, awful.tag.viewnext), + awful.button({ }, 5, awful.tag.viewprev) +)) +-- }}} + +-- {{{ Key bindings +globalkeys = gears.table.join( + awful.key({ modkey, }, "s", hotkeys_popup.show_help, + {description="show help", group="awesome"}), + awful.key({ modkey, }, "Left", awful.tag.viewprev, + {description = "view previous", group = "tag"}), + awful.key({ modkey, }, "Right", awful.tag.viewnext, + {description = "view next", group = "tag"}), + awful.key({ modkey, }, "Escape", awful.tag.history.restore, + {description = "go back", group = "tag"}), + + awful.key({ modkey, }, "j", + function () + awful.client.focus.byidx( 1) + end, + {description = "focus next by index", group = "client"} + ), + awful.key({ modkey, }, "k", + function () + awful.client.focus.byidx(-1) + end, + {description = "focus previous by index", group = "client"} + ), + awful.key({ modkey, }, "w", function () mymainmenu:show() end, + {description = "show main menu", group = "awesome"}), + + -- Layout manipulation + awful.key({ modkey, "Shift" }, "j", function () awful.client.swap.byidx( 1) end, + {description = "swap with next client by index", group = "client"}), + awful.key({ modkey, "Shift" }, "k", function () awful.client.swap.byidx( -1) end, + {description = "swap with previous client by index", group = "client"}), + awful.key({ modkey, "Control" }, "j", function () awful.screen.focus_relative( 1) end, + {description = "focus the next screen", group = "screen"}), + awful.key({ modkey, "Control" }, "k", function () awful.screen.focus_relative(-1) end, + {description = "focus the previous screen", group = "screen"}), + awful.key({ modkey, }, "u", awful.client.urgent.jumpto, + {description = "jump to urgent client", group = "client"}), + awful.key({ modkey, }, "Tab", + function () + awful.client.focus.history.previous() + if client.focus then + client.focus:raise() + end + end, + {description = "go back", group = "client"}), + + -- Standard program + awful.key({ modkey, }, "Return", function () awful.spawn(terminal) end, + {description = "open a terminal", group = "launcher"}), + awful.key({ modkey, "Control" }, "r", awesome.restart, + {description = "reload awesome", group = "awesome"}), + awful.key({ modkey, "Shift" }, "q", awesome.quit, + {description = "quit awesome", group = "awesome"}), + + awful.key({ modkey, }, "l", function () awful.tag.incmwfact( 0.05) end, + {description = "increase master width factor", group = "layout"}), + awful.key({ modkey, }, "h", function () awful.tag.incmwfact(-0.05) end, + {description = "decrease master width factor", group = "layout"}), + awful.key({ modkey, "Shift" }, "h", function () awful.tag.incnmaster( 1, nil, true) end, + {description = "increase the number of master clients", group = "layout"}), + awful.key({ modkey, "Shift" }, "l", function () awful.tag.incnmaster(-1, nil, true) end, + {description = "decrease the number of master clients", group = "layout"}), + awful.key({ modkey, "Control" }, "h", function () awful.tag.incncol( 1, nil, true) end, + {description = "increase the number of columns", group = "layout"}), + awful.key({ modkey, "Control" }, "l", function () awful.tag.incncol(-1, nil, true) end, + {description = "decrease the number of columns", group = "layout"}), + awful.key({ modkey, }, "space", function () awful.layout.inc( 1) end, + {description = "select next", group = "layout"}), + awful.key({ modkey, "Shift" }, "space", function () awful.layout.inc(-1) end, + {description = "select previous", group = "layout"}), + + awful.key({ modkey, "Control" }, "n", + function () + local c = awful.client.restore() + -- Focus restored client + if c then + c:emit_signal( + "request::activate", "key.unminimize", {raise = true} + ) + end + end, + {description = "restore minimized", group = "client"}), + + -- Prompt + awful.key({ modkey }, "r", function () awful.screen.focused().mypromptbox:run() end, + {description = "run prompt", group = "launcher"}), + + awful.key({ modkey }, "x", + function () + awful.prompt.run { + prompt = "Run Lua code: ", + textbox = awful.screen.focused().mypromptbox.widget, + exe_callback = awful.util.eval, + history_path = awful.util.get_cache_dir() .. "/history_eval" + } + end, + {description = "lua execute prompt", group = "awesome"}), + -- Menubar + awful.key({ modkey }, "p", function() menubar.show() end, + {description = "show the menubar", group = "launcher"}) +) + +clientkeys = gears.table.join( + awful.key({ modkey, }, "f", + function (c) + c.fullscreen = not c.fullscreen + c:raise() + end, + {description = "toggle fullscreen", group = "client"}), + awful.key({ modkey, "Control" }, "s", + function (c) + awful.titlebar.toggle(c) + end, + {description = "toggle titlebar", group = "client"}), + awful.key({ modkey, "Shift" }, "c", function (c) c:kill() end, + {description = "close", group = "client"}), + awful.key({ modkey, "Control" }, "space", awful.client.floating.toggle , + {description = "toggle floating", group = "client"}), + awful.key({ modkey, "Control" }, "Return", function (c) c:swap(awful.client.getmaster()) end, + {description = "move to master", group = "client"}), + awful.key({ modkey, }, "o", function (c) c:move_to_screen() end, + {description = "move to screen", group = "client"}), + awful.key({ modkey, }, "t", function (c) c.ontop = not c.ontop end, + {description = "toggle keep on top", group = "client"}), + awful.key({ modkey, }, "n", + function (c) + -- The client currently has the input focus, so it cannot be + -- minimized, since minimized clients can't have the focus. + c.minimized = true + end , + {description = "minimize", group = "client"}), + awful.key({ modkey, }, "m", + function (c) + c.maximized = not c.maximized + c:raise() + end , + {description = "(un)maximize", group = "client"}), + awful.key({ modkey, "Control" }, "m", + function (c) + c.maximized_vertical = not c.maximized_vertical + c:raise() + end , + {description = "(un)maximize vertically", group = "client"}), + awful.key({ modkey, "Shift" }, "m", + function (c) + c.maximized_horizontal = not c.maximized_horizontal + c:raise() + end , + {description = "(un)maximize horizontally", group = "client"}) +) + +-- Bind all key numbers to tags. +-- Be careful: we use keycodes to make it work on any keyboard layout. +-- This should map on the top row of your keyboard, usually 1 to 9. +for i = 1, 9 do + globalkeys = gears.table.join(globalkeys, + -- View tag only. + awful.key({ modkey }, "#" .. i + 9, + function () + local screen = awful.screen.focused() + local tag = screen.tags[i] + if tag then + tag:view_only() + end + end, + {description = "view tag #"..i, group = "tag"}), + -- Toggle tag display. + awful.key({ modkey, "Control" }, "#" .. i + 9, + function () + local screen = awful.screen.focused() + local tag = screen.tags[i] + if tag then + awful.tag.viewtoggle(tag) + end + end, + {description = "toggle tag #" .. i, group = "tag"}), + -- Move client to tag. + awful.key({ modkey, "Shift" }, "#" .. i + 9, + function () + if client.focus then + local tag = client.focus.screen.tags[i] + if tag then + client.focus:move_to_tag(tag) + end + end + end, + {description = "move focused client to tag #"..i, group = "tag"}), + -- Toggle tag on focused client. + awful.key({ modkey, "Control", "Shift" }, "#" .. i + 9, + function () + if client.focus then + local tag = client.focus.screen.tags[i] + if tag then + client.focus:toggle_tag(tag) + end + end + end, + {description = "toggle focused client on tag #" .. i, group = "tag"}) + ) +end + +clientbuttons = gears.table.join( + awful.button({ }, 1, function (c) + c:emit_signal("request::activate", "mouse_click", {raise = true}) + end), + awful.button({ modkey }, 1, function (c) + c:emit_signal("request::activate", "mouse_click", {raise = true}) + awful.mouse.client.move(c) + end), + awful.button({ modkey }, 3, function (c) + c:emit_signal("request::activate", "mouse_click", {raise = true}) + awful.mouse.client.resize(c) + end) +) + +-- Set keys +root.keys(globalkeys) +-- }}} + +-- {{{ Rules +-- Rules to apply to new clients (through the "manage" signal). +awful.rules.rules = { + -- All clients will match this rule. + { rule = { }, + properties = { border_width = beautiful.border_width, + border_color = beautiful.border_normal, + focus = awful.client.focus.filter, + raise = true, + keys = clientkeys, + buttons = clientbuttons, + screen = awful.screen.preferred, + placement = awful.placement.no_overlap+awful.placement.no_offscreen + } + }, + + -- Floating clients. + { rule_any = { + instance = { + "DTA", -- Firefox addon DownThemAll. + "copyq", -- Includes session name in class. + "pinentry", + }, + class = { + "Arandr", + "Blueman-manager", + "Gpick", + "Kruler", + "MessageWin", -- kalarm. + "Sxiv", + "Tor Browser", -- Needs a fixed window size to avoid fingerprinting by screen size. + "Wpa_gui", + "veromix", + "xtightvncviewer"}, + + -- Note that the name property shown in xprop might be set slightly after creation of the client + -- and the name shown there might not match defined rules here. + name = { + "Event Tester", -- xev. + }, + role = { + "AlarmWindow", -- Thunderbird's calendar. + "ConfigManager", -- Thunderbird's about:config. + "pop-up", -- e.g. Google Chrome's (detached) Developer Tools. + } + }, properties = { floating = false }}, + + -- Add titlebars to normal clients and dialogs + { rule_any = {type = { "normal", "dialog" } + }, properties = { titlebars_enabled = false } + }, + + -- Set Firefox to always map on the tag named "2" on screen 1. + -- { rule = { class = "Firefox" }, + -- properties = { screen = 1, tag = "2" } }, +} +-- }}} + +-- {{{ Signals +-- Signal function to execute when a new client appears. +client.connect_signal("manage", function (c) + -- Set the windows at the slave, + -- i.e. put it at the end of others instead of setting it master. + -- if not awesome.startup then awful.client.setslave(c) end + + if awesome.startup + and not c.size_hints.user_position + and not c.size_hints.program_position then + -- Prevent clients from being unreachable after screen count changes. + awful.placement.no_offscreen(c) + end +end) + +-- Add a titlebar if titlebars_enabled is set to true in the rules. +client.connect_signal("request::titlebars", function(c) + -- buttons for the titlebar + local buttons = gears.table.join( + awful.button({ }, 1, function() + c:emit_signal("request::activate", "titlebar", {raise = true}) + awful.mouse.client.move(c) + end), + awful.button({ }, 3, function() + c:emit_signal("request::activate", "titlebar", {raise = true}) + awful.mouse.client.resize(c) + end) + ) + awful.titlebar(c) : setup { + { -- Left + awful.titlebar.widget.iconwidget(c), + buttons = buttons, + layout = wibox.layout.fixed.horizontal + }, + { -- Middle + { -- Title + align = "center", + widget = awful.titlebar.widget.titlewidget(c) + }, + buttons = buttons, + layout = wibox.layout.flex.horizontal + }, + { -- Right + awful.titlebar.widget.floatingbutton (c), + awful.titlebar.widget.maximizedbutton(c), + awful.titlebar.widget.stickybutton (c), + awful.titlebar.widget.ontopbutton (c), + awful.titlebar.widget.closebutton (c), + layout = wibox.layout.fixed.horizontal() + }, + layout = wibox.layout.align.horizontal + } +end) + +--client.connect_signal("property::floating", function(c) +-- if c.floating then +-- awful.titlebar.show(c) +-- else +-- awful.titlebar.hide(c) +-- end +-- end) + +-- Enable sloppy focus, so that focus follows mouse. +client.connect_signal("mouse::enter", function(c) + c:emit_signal("request::activate", "mouse_enter", {raise = false}) +end) + +client.connect_signal("focus", function(c) c.border_color = beautiful.border_focus end) +client.connect_signal("unfocus", function(c) c.border_color = beautiful.border_normal end) +-- }}} diff --git a/rc.lua.save b/rc.lua.save new file mode 100644 index 0000000..cfa6821 --- /dev/null +++ b/rc.lua.save @@ -0,0 +1,628 @@ +-- If LuaRocks is installed, make sure that packages installed through it are +-- found (e.g. lgi). If LuaRocks is not installed, do nothing. +pcall(require, "luarocks.loader") + +-- Standard awesome library +local gears = require("gears") +local awful = require("awful") +require("awful.autofocus") +-- Widget and layout library +local wibox = require("wibox") +-- Theme handling library +local beautiful = require("beautiful") +-- Notification library +local naughty = require("naughty") +local menubar = require("menubar") +local hotkeys_popup = require("awful.hotkeys_popup") +-- Enable hotkeys help widget for VIM and other apps +-- when client with a matching name is opened: +require("awful.hotkeys_popup.keys") + +-- My custom libs +local vicious = require("vicious") + +-- {{{ Error handling +-- Check if awesome encountered an error during startup and fell back to +-- another config (This code will only ever execute for the fallback config) +if awesome.startup_errors then + naughty.notify({ preset = naughty.config.presets.critical, + title = "Oops, there were errors during startup!", + text = awesome.startup_errors }) +end + +-- Handle runtime errors after startup +do + local in_error = false + awesome.connect_signal("debug::error", function (err) + -- Make sure we don't go into an endless error loop + if in_error then return end + in_error = true + + naughty.notify({ preset = naughty.config.presets.critical, + title = "Oops, an error happened!", + text = tostring(err) }) + in_error = false + end) +end +-- }}} + +-- {{{ Variable definitions +-- Themes define colours, icons, font and wallpapers. +beautiful.init(gears.filesystem.get_themes_dir() .. "default/theme.lua") +beautiful.font = "Indie Flower Bold 12" + +-- This is used later as the default terminal and editor to run. +terminal = "konsole" +editor = os.getenv("EDITOR") or "nano" +editor_cmd = terminal .. " -e " .. editor + +-- Default modkey. +-- Usually, Mod4 is the key with a logo between Control and Alt. +-- If you do not like this or do not have such a key, +-- I suggest you to remap Mod4 to another key using xmodmap or other tools. +-- However, you can use another modifier like Mod1, but it may interact with others. +modkey = "Mod4" + +-- Table of layouts to cover with awful.layout.inc, order matters. +awful.layout.layouts = { + awful.layout.suit.floating, + awful.layout.suit.tile, + awful.layout.suit.tile.left, + awful.layout.suit.tile.bottom, + awful.layout.suit.tile.top, + awful.layout.suit.fair, + awful.layout.suit.fair.horizontal, + awful.layout.suit.spiral, + awful.layout.suit.spiral.dwindle, + awful.layout.suit.max, + awful.layout.suit.max.fullscreen, + awful.layout.suit.magnifier, + awful.layout.suit.corner.nw, + -- awful.layout.suit.corner.ne, + -- awful.layout.suit.corner.sw, + -- awful.layout.suit.corner.se, +} +-- }}} + +-- {{{ Menu +-- Create a launcher widget and a main menu +myawesomemenu = { + { "hotkeys", function() hotkeys_popup.show_help(nil, awful.screen.focused()) end }, + { "manual", terminal .. " -e man awesome" }, + { "edit config", editor_cmd .. " " .. awesome.conffile }, + { "restart", awesome.restart }, + { "quit", function() awesome.quit() end }, +} + +mymainmenu = awful.menu({ items = { { "awesome", myawesomemenu, beautiful.awesome_icon }, + { "open terminal", terminal } + } + }) + +mylauncher = awful.widget.launcher({ image = beautiful.awesome_icon, + menu = mymainmenu }) + +-- Menubar configuration +menubar.utils.terminal = terminal -- Set the terminal for applications that require it +-- }}} + +-- Keyboard map indicator and switcher +mykeyboardlayout = awful.widget.keyboardlayout() + +-- {{{ Wibar + +-- Create a wibox for each screen and add it +local taglist_buttons = gears.table.join( + awful.button({ }, 1, function(t) t:view_only() end), + awful.button({ modkey }, 1, function(t) + if client.focus then + client.focus:move_to_tag(t) + end + end), + awful.button({ }, 3, awful.tag.viewtoggle), + awful.button({ modkey }, 3, function(t) + if client.focus then + client.focus:toggle_tag(t) + end + end), + awful.button({ }, 4, function(t) awful.tag.viewnext(t.screen) end), + awful.button({ }, 5, function(t) awful.tag.viewprev(t.screen) end) + ) + +local tasklist_buttons = gears.table.join( + awful.button({ }, 1, function (c) + if c == client.focus then + c.minimized = true + else + c:emit_signal( + "request::activate", + "tasklist", + {raise = true} + ) + end + end), + awful.button({ }, 3, function() + awful.menu.client_list({ theme = { width = 250 } }) + end), + awful.button({ }, 4, function () + awful.client.focus.byidx(1) + end), + awful.button({ }, 5, function () + awful.client.focus.byidx(-1) + end)) + +local function set_wallpaper(s) + -- Wallpaper + if beautiful.wallpaper then + local wallpaper = beautiful.wallpaper + -- If wallpaper is a function, call it with the screen + if type(wallpaper) == "function" then + wallpaper = wallpaper(s) + end + gears.wallpaper.maximized(wallpaper, s, true) + end +end + +---- + +datewidget = wibox.widget.textbox() +vicious.register(datewidget, vicious.widgets.date, "%b %d, %R ") + +--memwidget = wibox.widget.textbox() +--vicious.cache(vicious.widgets.mem) +--vicious.register(memwidget, vicious.widgets.mem, " $2/$3 $1% $6/$7 $5% ", 1) + +batwidget = wibox.widget.progressbar() + +-- Create wibox with batwidget +batbox = wibox.layout.margin( + wibox.widget{ { max_value = 1, widget = batwidget, + border_width = 0.5, border_color = "#000000", + color = { type = "linear", + from = { 0, 0 }, + to = { 0, 30 }, + stops = { { 0, "#AECF96" }, + { 1, "#FF5656" } } } }, + forced_height = 10, forced_width = 8, + direction = 'east', color = beautiful.fg_widget, + layout = wibox.container.rotate }, + 1, 4, 1, 1) + +-- Register battery widget +vicious.register(batwidget, vicious.widgets.bat, "$2", 1, "BAT0") + + +cpuwidget = awful.widget.graph() +cpuwidget:set_width(50) +cpuwidget:set_background_color"#494B4F" +cpuwidget:set_color{ type = "linear", from = { 0, 0 }, to = { 50, 0 }, + stops = { { 0, "#FF5656" }, + { 0.5, "#88A175" }, + { 1, "#AECF96" } } } +vicious.register(cpuwidget, vicious.widgets.cpu, "$1", 1) + +memwidget = awful.widget.graph() +memwidget:set_width(50) +memwidget:set_background_color"#494B4F" +memwidget:set_color{ type = "linear", from = { 0, 0 }, to = { 50, 0 }, + stops = { { 0, "#FF5656" }, + { 0.5, "#88A175" }, + { 1, "#AECF96" } } } +vicious.register(memwidget, vicious.widgets.mem, "$1", 1) + +swapwidget = awful.widget.graph() +swapwidget:set_width(50) +swapwidget:set_background_color"#494B4F" +swapwidget:set_color{ type = "linear", from = { 0, 0 }, to = { 50, 0 }, + stops = { { 0, "#FF5656" }, + { 0.5, "#88A175" }, + { 1, "#AECF96" } } } +vicious.register(swapwidget, vicious.widgets.mem, "$5", 1) + +---- + +-- Re-set wallpaper when a screen's geometry changes (e.g. different resolution) +screen.connect_signal("property::geometry", set_wallpaper) + +awful.screen.connect_for_each_screen(function(s) + -- Wallpaper + set_wallpaper(s) + + -- Each screen has its own tag table. + awful.tag({ "1", "2", "3", "4", "5", "6", "7", "8", "9" }, s, awful.layout.layouts[1]) + + -- Create a promptbox for each screen + s.mypromptbox = awful.widget.prompt() + -- Create an imagebox widget which will contain an icon indicating which layout we're using. + -- We need one layoutbox per screen. + s.mylayoutbox = awful.widget.layoutbox(s) + s.mylayoutbox:buttons(gears.table.join( + awful.button({ }, 1, function () awful.layout.inc( 1) end), + awful.button({ }, 3, function () awful.layout.inc(-1) end), + awful.button({ }, 4, function () awful.layout.inc( 1) end), + awful.button({ }, 5, function () awful.layout.inc(-1) end))) + -- Create a taglist widget + s.mytaglist = awful.widget.taglist { + screen = s, + filter = awful.widget.taglist.filter.all, + buttons = taglist_buttons + } + + -- Create a tasklist widget + s.mytasklist = awful.widget.tasklist { + screen = s, + filter = awful.widget.tasklist.filter.currenttags, + buttons = tasklist_buttons + } + + -- Create the wibox + s.mywibox = awful.wibar({ position = "top", screen = s }) + + -- Add widgets to the wibox + s.mywibox:setup { + layout = wibox.layout.align.horizontal, + { -- Left widgets + layout = wibox.layout.fixed.horizontal, + mylauncher, + s.mytaglist, + s.mypromptbox, + }, + s.mytasklist, -- Middle widget + { -- Right widgets + layout = wibox.layout.fixed.horizontal, + wibox.widget.systray(), + memwidget, + swapwidget, + cpuwidget, + mykeyboardlayout, + datewidget, + batbox, + s.mylayoutbox, + }, + } +end) +-- }}} + +-- {{{ Mouse bindings +root.buttons(gears.table.join( + awful.button({ }, 3, function () mymainmenu:toggle() end), + awful.button({ }, 4, awful.tag.viewnext), + awful.button({ }, 5, awful.tag.viewprev) +)) +-- }}} + +-- {{{ Key bindings +globalkeys = gears.table.join( + awful.key({ modkey, }, "s", hotkeys_popup.show_help, + {description="show help", group="awesome"}), + awful.key({ modkey, }, "Left", awful.tag.viewprev, + {description = "view previous", group = "tag"}), + awful.key({ modkey, }, "Right", awful.tag.viewnext, + {description = "view next", group = "tag"}), + awful.key({ modkey, }, "Escape", awful.tag.history.restore, + {description = "go back", group = "tag"}), + + awful.key({ modkey, }, "j", + function () + awful.client.focus.byidx( 1) + end, + {description = "focus next by index", group = "client"} + ), + awful.key({ modkey, }, "k", + function () + awful.client.focus.byidx(-1) + end, + {description = "focus previous by index", group = "client"} + ), + awful.key({ modkey, }, "w", function () mymainmenu:show() end, + {description = "show main menu", group = "awesome"}), + + -- Layout manipulation + awful.key({ modkey, "Shift" }, "j", function () awful.client.swap.byidx( 1) end, + {description = "swap with next client by index", group = "client"}), + awful.key({ modkey, "Shift" }, "k", function () awful.client.swap.byidx( -1) end, + {description = "swap with previous client by index", group = "client"}), + awful.key({ modkey, "Control" }, "j", function () awful.screen.focus_relative( 1) end, + {description = "focus the next screen", group = "screen"}), + awful.key({ modkey, "Control" }, "k", function () awful.screen.focus_relative(-1) end, + {description = "focus the previous screen", group = "screen"}), + awful.key({ modkey, }, "u", awful.client.urgent.jumpto, + {description = "jump to urgent client", group = "client"}), + awful.key({ modkey, }, "Tab", + function () + awful.client.focus.history.previous() + if client.focus then + client.focus:raise() + end + end, + {description = "go back", group = "client"}), + + -- Standard program + awful.key({ modkey, }, "Return", function () awful.spawn(terminal) end, + {description = "open a terminal", group = "launcher"}), + awful.key({ modkey, "Control" }, "r", awesome.restart, + {description = "reload awesome", group = "awesome"}), + awful.key({ modkey, "Shift" }, "q", awesome.quit, + {description = "quit awesome", group = "awesome"}), + + awful.key({ modkey, }, "l", function () awful.tag.incmwfact( 0.05) end, + {description = "increase master width factor", group = "layout"}), + awful.key({ modkey, }, "h", function () awful.tag.incmwfact(-0.05) end, + {description = "decrease master width factor", group = "layout"}), + awful.key({ modkey, "Shift" }, "h", function () awful.tag.incnmaster( 1, nil, true) end, + {description = "increase the number of master clients", group = "layout"}), + awful.key({ modkey, "Shift" }, "l", function () awful.tag.incnmaster(-1, nil, true) end, + {description = "decrease the number of master clients", group = "layout"}), + awful.key({ modkey, "Control" }, "h", function () awful.tag.incncol( 1, nil, true) end, + {description = "increase the number of columns", group = "layout"}), + awful.key({ modkey, "Control" }, "l", function () awful.tag.incncol(-1, nil, true) end, + {description = "decrease the number of columns", group = "layout"}), + awful.key({ modkey, }, "space", function () awful.layout.inc( 1) end, + {description = "select next", group = "layout"}), + awful.key({ modkey, "Shift" }, "space", function () awful.layout.inc(-1) end, + {description = "select previous", group = "layout"}), + + awful.key({ modkey, "Control" }, "n", + function () + local c = awful.client.restore() + -- Focus restored client + if c then + c:emit_signal( + "request::activate", "key.unminimize", {raise = true} + ) + end + end, + {description = "restore minimized", group = "client"}), + + -- Prompt + awful.key({ modkey }, "r", function () awful.screen.focused().mypromptbox:run() end, + {description = "run prompt", group = "launcher"}), + + awful.key({ modkey }, "x", + function () + awful.prompt.run { + prompt = "Run Lua code: ", + textbox = awful.screen.focused().mypromptbox.widget, + exe_callback = awful.util.eval, + history_path = awful.util.get_cache_dir() .. "/history_eval" + } + end, + {description = "lua execute prompt", group = "awesome"}), + -- Menubar + awful.key({ modkey }, "p", function() menubar.show() end, + {description = "show the menubar", group = "launcher"}) +) + +clientkeys = gears.table.join( + awful.key({ modkey, }, "f", + function (c) + c.fullscreen = not c.fullscreen + c:raise() + end, + {description = "toggle fullscreen", group = "client"}), + awful.key({ modkey, "Shift" }, "c", function (c) c:kill() end, + {description = "close", group = "client"}), + awful.key({ modkey, "Control" }, "space", awful.client.floating.toggle , + {description = "toggle floating", group = "client"}), + awful.key({ modkey, "Control" }, "Return", function (c) c:swap(awful.client.getmaster()) end, + {description = "move to master", group = "client"}), + awful.key({ modkey, }, "o", function (c) c:move_to_screen() end, + {description = "move to screen", group = "client"}), + awful.key({ modkey, }, "t", function (c) c.ontop = not c.ontop end, + {description = "toggle keep on top", group = "client"}), + awful.key({ modkey, }, "n", + function (c) + -- The client currently has the input focus, so it cannot be + -- minimized, since minimized clients can't have the focus. + c.minimized = true + end , + {description = "minimize", group = "client"}), + awful.key({ modkey, }, "m", + function (c) + c.maximized = not c.maximized + c:raise() + end , + {description = "(un)maximize", group = "client"}), + awful.key({ modkey, "Control" }, "m", + function (c) + c.maximized_vertical = not c.maximized_vertical + c:raise() + end , + {description = "(un)maximize vertically", group = "client"}), + awful.key({ modkey, "Shift" }, "m", + function (c) + c.maximized_horizontal = not c.maximized_horizontal + c:raise() + end , + {description = "(un)maximize horizontally", group = "client"}) +) + +-- Bind all key numbers to tags. +-- Be careful: we use keycodes to make it work on any keyboard layout. +-- This should map on the top row of your keyboard, usually 1 to 9. +for i = 1, 9 do + globalkeys = gears.table.join(globalkeys, + -- View tag only. + awful.key({ modkey }, "#" .. i + 9, + function () + local screen = awful.screen.focused() + local tag = screen.tags[i] + if tag then + tag:view_only() + end + end, + {description = "view tag #"..i, group = "tag"}), + -- Toggle tag display. + awful.key({ modkey, "Control" }, "#" .. i + 9, + function () + local screen = awful.screen.focused() + local tag = screen.tags[i] + if tag then + awful.tag.viewtoggle(tag) + end + end, + {description = "toggle tag #" .. i, group = "tag"}), + -- Move client to tag. + awful.key({ modkey, "Shift" }, "#" .. i + 9, + function () + if client.focus then + local tag = client.focus.screen.tags[i] + if tag then + client.focus:move_to_tag(tag) + end + end + end, + {description = "move focused client to tag #"..i, group = "tag"}), + -- Toggle tag on focused client. + awful.key({ modkey, "Control", "Shift" }, "#" .. i + 9, + function () + if client.focus then + local tag = client.focus.screen.tags[i] + if tag then + client.focus:toggle_tag(tag) + end + end + end, + {description = "toggle focused client on tag #" .. i, group = "tag"}) + ) +end + +clientbuttons = gears.table.join( + awful.button({ }, 1, function (c) + c:emit_signal("request::activate", "mouse_click", {raise = true}) + end), + awful.button({ modkey }, 1, function (c) + c:emit_signal("request::activate", "mouse_click", {raise = true}) + awful.mouse.client.move(c) + end), + awful.button({ modkey }, 3, function (c) + c:emit_signal("request::activate", "mouse_click", {raise = true}) + awful.mouse.client.resize(c) + end) +) + +-- Set keys +root.keys(globalkeys) +-- }}} + +-- {{{ Rules +-- Rules to apply to new clients (through the "manage" signal). +awful.rules.rules = { + -- All clients will match this rule. + { rule = { }, + properties = { border_width = beautiful.border_width, + border_color = beautiful.border_normal, + focus = awful.client.focus.filter, + raise = true, + keys = clientkeys, + buttons = clientbuttons, + screen = awful.screen.preferred, + placement = awful.placement.no_overlap+awful.placement.no_offscreen + } + }, + + -- Floating clients. + { rule_any = { + instance = { + "DTA", -- Firefox addon DownThemAll. + "copyq", -- Includes session name in class. + "pinentry", + }, + class = { + "Arandr", + "Blueman-manager", + "Gpick", + "Kruler", + "MessageWin", -- kalarm. + "Sxiv", + "Tor Browser", -- Needs a fixed window size to avoid fingerprinting by screen size. + "Wpa_gui", + "veromix", + "xtightvncviewer"}, + + -- Note that the name property shown in xprop might be set slightly after creation of the client + -- and the name shown there might not match defined rules here. + name = { + "Event Tester", -- xev. + }, + role = { + "AlarmWindow", -- Thunderbird's calendar. + "ConfigManager", -- Thunderbird's about:config. + "pop-up", -- e.g. Google Chrome's (detached) Developer Tools. + } + }, properties = { floating = true }}, + + -- Add titlebars to normal clients and dialogs + { rule_any = {type = { "normal", "dialog" } + }, properties = { titlebars_enabled = true } + }, + + -- Set Firefox to always map on the tag named "2" on screen 1. + -- { rule = { class = "Firefox" }, + -- properties = { screen = 1, tag = "2" } }, +} +-- }}} + +-- {{{ Signals +-- Signal function to execute when a new client appears. +client.connect_signal("manage", function (c) + -- Set the windows at the slave, + -- i.e. put it at the end of others instead of setting it master. + -- if not awesome.startup then awful.client.setslave(c) end + + if awesome.startup + and not c.size_hints.user_position + and not c.size_hints.program_position then + -- Prevent clients from being unreachable after screen count changes. + awful.placement.no_offscreen(c) + end +end) + +-- Add a titlebar if titlebars_enabled is set to true in the rules. +client.connect_signal("request::titlebars", function(c) + -- buttons for the titlebar + local buttons = gears.table.join( + awful.button({ }, 1, function() + c:emit_signal("request::activate", "titlebar", {raise = true}) + awful.mouse.client.move(c) + end), + awful.button({ }, 3, function() + c:emit_signal("request::activate", "titlebar", {raise = true}) + awful.mouse.client.resize(c) + end) + ) + + awful.titlebar(c) : setup { + { -- Left + awful.titlebar.widget.iconwidget(c), + buttons = buttons, + layout = wibox.layout.fixed.horizontal + }, + { -- Middle + { -- Title + align = "center", + widget = awful.titlebar.widget.titlewidget(c) + }, + buttons = buttons, + layout = wibox.layout.flex.horizontal + }, + { -- Right + awful.titlebar.widget.floatingbutton (c), + awful.titlebar.widget.maximizedbutton(c), + awful.titlebar.widget.stickybutton (c), + awful.titlebar.widget.ontopbutton (c), + awful.titlebar.widget.closebutton (c), + layout = wibox.layout.fixed.horizontal() + }, + layout = wibox.layout.align.horizontal + } +end) + +-- Enable sloppy focus, so that focus follows mouse. +client.connect_signal("mouse::enter", function(c) + c:emit_signal("request::activate", "mouse_enter", {raise = false}) +end) + +client.connect_signal("focus", function(c) c.border_color = beautiful.border_focus end) +client.connect_signal("unfocus", function(c) c.border_color = beautiful.border_normal end) +-- }}} diff --git a/spawn.lua b/spawn.lua new file mode 100644 index 0000000..afe7802 --- /dev/null +++ b/spawn.lua @@ -0,0 +1,88 @@ +-- wrapper around Awesome awful.spawn with fallback +-- Copyright (C) 2019 Nguyễn Gia Phong +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +local status, awful = pcall(require, "awful") +if status then + local spawn = awful.spawn + function spawn.with_line_callback_with_shell(cmd, callbacks) + spawn.with_line_callback({ awful.util.shell, "-c", cmd }, callbacks) + end + return spawn +end + +local io = { popen = io.popen } + +-- In case the Awesome library is not available, +-- fall back to this synchronous, handicapped API. +local spawn = {} + +--- Spawn a program and capture its output line by line. +-- @tparam string cmd The command. +-- @tab callbacks Table containing callbacks that should be invoked on +-- various conditions. +-- @tparam[opt] function callbacks.stdout Function that is called with each +-- line of output on stdout, e.g. `stdout(line)`. +-- @tparam[opt] function callbacks.stderr Function that is supposed to be called +-- with each line of output on stderr, but due to limitation of io.popen, +-- stderr will be discarded (sorry!). +-- @tparam[opt] function callbacks.output_done Function to call when no more +-- output is produced. +-- @tparam[opt] function callbacks.exit Function to call when the spawned +-- process exits. This function gets the exit reason and code as its +-- arguments. +-- The reason can be "exit" or "signal". +-- For "exit", the second argument is the exit code. +-- For "signal", the second argument is the signal causing process +-- termination. +-- @treturn boolean|nil true if cmd terminated successfully, or nil otherwise +function spawn.with_line_callback_with_shell(cmd, callbacks) + local stdout_callback, stdout = callbacks.stdout, io.popen(cmd) + if stdout_callback then + for line in stdout:lines() do stdout_callback(line) end + end + if callbacks.output_done then callbacks.output_done() end + + local success, reason, code = stdout:close() -- this requires Lua 5.2 + if callbacks.exit then callbacks.exit(reason, code) end + return success +end + +--- Spawn a program and capture its output. +-- @tparam string cmd The command. +-- @tab callback Function with the following arguments +-- @tparam string callback.stdout Output on stdout. +-- @tparam string callback.stderr Output on stderr, +-- which will be an empty due to limitation of io.popen. +-- @tparam string callback.exitreason Exit reason ("exit" or "signal"). +-- @tparam integer callback.exitcode Exit code (exit code or signal number, +-- depending on "exitreason"). +-- @treturn boolean|nil true if cmd terminated successfully, or nil otherwise +function spawn.easy_async_with_shell(cmd, callback) + local out_stream = io.popen(cmd) + local stdout = out_stream:read("*all") + local success, reason, code = out_stream:close() -- requiring Lua 5.2 + callback(stdout, "", reason, code) + return success +end + +-- Since io.popen always use a shell +spawn.easy_async = spawn.easy_async_with_shell +spawn.with_line_callback = spawn.with_line_callback_with_shell + +return spawn +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/templates/README.md b/templates/README.md new file mode 100644 index 0000000..80a9ea9 --- /dev/null +++ b/templates/README.md @@ -0,0 +1,48 @@ +# Widget type templates + +Before writing a new widget type, make sure to ask yourself if anyone is going +to need the feature. Only widget types that you (or an incapacitated friend +you can personally talk to) require would be merged. Do not file PRs because +they seem like a good idea. If they're really useful, they'll be requested +in the issue tracker. + +Additionally, too simple widget types (e.g. an one-liner) and those that +are not modular enough are very unlikely to be merged. By *modular*, we mean + +> constructed with standardized units or dimensions +> allowing flexibility and variety in use + +If all the above conditions are met, you can start from one of the templates +in this directory: + +* `sync.lua`: synchronous widget type that does not fork +* `async.lua`: asynchronous widget type for fetching informations using + a command-line utility. As a rule of thumb, if your widget type uses + `io.popen`, you would need to refactor it to use async facilities. + +Your widget types should be placed in `widgets`: the `contrib` directory +exists only for historical reasons and is barely maintained anymore. +The filenames should be of the form `_.lua`, whereas + +* `` is a single (alphanumeric) word, preferably in lowercase +* `` is the OS that the widget type works on. + At the time of writing these are supported: + - `freebsd`: FreeBSD + - `openbsd`: OpenBSD + - `bsd`: all \*BSDs listed above + - `linux`: GNU/Linux + - `all`: all of the above + +Please be aware of `luacheck`, which may help you during the progress. +From `widgets`, run + + luacheck --config .luacheckrc .. + +After finishing the widget type, you should document its usage in the project's +`README.md`. Try to provide at least + +* A brief description +* The list of supported platforms +* Type and structures of the arguments that the widget type passes + (`format` and `warg`), with unused parameters omitted +* Type and structure of the return value diff --git a/templates/async.lua b/templates/async.lua new file mode 100644 index 0000000..d704071 --- /dev/null +++ b/templates/async.lua @@ -0,0 +1,28 @@ +-- template for asynchronous widet types +-- Copyright (C) 2019 Nguyễn Gia Phong +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +local helpers = require"vicious.helpers" + +return helpers.setasyncall{ + async = function (format, warg, callback) + -- In here there should be some asynchronous function + -- from vicious.spawn or helpers.sysctl_async + -- that call callback on the result. + end } + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/templates/sync.lua b/templates/sync.lua new file mode 100644 index 0000000..4b49059 --- /dev/null +++ b/templates/sync.lua @@ -0,0 +1,25 @@ +-- template for synchronous widet types +-- Copyright (C) 2019 Nguyễn Gia Phong +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +local helpers = require"vicious.helpers" + +return helpers.setcall(function (format, warg) + -- Do the processing and return a table here. +end) + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/themes/zenburn/README.md b/themes/zenburn/README.md new file mode 100644 index 0000000..e129d92 --- /dev/null +++ b/themes/zenburn/README.md @@ -0,0 +1,3 @@ +Based on Zenburn + +modified by Surferlul diff --git a/themes/zenburn/artix_wallpaper.png b/themes/zenburn/artix_wallpaper.png new file mode 100644 index 0000000..5cc4fec Binary files /dev/null and b/themes/zenburn/artix_wallpaper.png differ diff --git a/themes/zenburn/layouts/cornerne.png b/themes/zenburn/layouts/cornerne.png new file mode 100644 index 0000000..d541a43 Binary files /dev/null and b/themes/zenburn/layouts/cornerne.png differ diff --git a/themes/zenburn/layouts/cornernw.png b/themes/zenburn/layouts/cornernw.png new file mode 100644 index 0000000..78fa394 Binary files /dev/null and b/themes/zenburn/layouts/cornernw.png differ diff --git a/themes/zenburn/layouts/cornerse.png b/themes/zenburn/layouts/cornerse.png new file mode 100644 index 0000000..19b85c3 Binary files /dev/null and b/themes/zenburn/layouts/cornerse.png differ diff --git a/themes/zenburn/layouts/cornersw.png b/themes/zenburn/layouts/cornersw.png new file mode 100644 index 0000000..a35c476 Binary files /dev/null and b/themes/zenburn/layouts/cornersw.png differ diff --git a/themes/zenburn/layouts/dwindle.png b/themes/zenburn/layouts/dwindle.png new file mode 100644 index 0000000..1aa4bf2 Binary files /dev/null and b/themes/zenburn/layouts/dwindle.png differ diff --git a/themes/zenburn/layouts/fairh.png b/themes/zenburn/layouts/fairh.png new file mode 100644 index 0000000..e176bb3 Binary files /dev/null and b/themes/zenburn/layouts/fairh.png differ diff --git a/themes/zenburn/layouts/fairv.png b/themes/zenburn/layouts/fairv.png new file mode 100644 index 0000000..7c0a92c Binary files /dev/null and b/themes/zenburn/layouts/fairv.png differ diff --git a/themes/zenburn/layouts/floating.png b/themes/zenburn/layouts/floating.png new file mode 100644 index 0000000..a399092 Binary files /dev/null and b/themes/zenburn/layouts/floating.png differ diff --git a/themes/zenburn/layouts/fullscreen.png b/themes/zenburn/layouts/fullscreen.png new file mode 100644 index 0000000..a0c795c Binary files /dev/null and b/themes/zenburn/layouts/fullscreen.png differ diff --git a/themes/zenburn/layouts/magnifier.png b/themes/zenburn/layouts/magnifier.png new file mode 100644 index 0000000..bca6db9 Binary files /dev/null and b/themes/zenburn/layouts/magnifier.png differ diff --git a/themes/zenburn/layouts/max.png b/themes/zenburn/layouts/max.png new file mode 100644 index 0000000..a24900c Binary files /dev/null and b/themes/zenburn/layouts/max.png differ diff --git a/themes/zenburn/layouts/spiral.png b/themes/zenburn/layouts/spiral.png new file mode 100644 index 0000000..8f5aeed Binary files /dev/null and b/themes/zenburn/layouts/spiral.png differ diff --git a/themes/zenburn/layouts/tile.png b/themes/zenburn/layouts/tile.png new file mode 100644 index 0000000..3fcc904 Binary files /dev/null and b/themes/zenburn/layouts/tile.png differ diff --git a/themes/zenburn/layouts/tilebottom.png b/themes/zenburn/layouts/tilebottom.png new file mode 100644 index 0000000..dfe7832 Binary files /dev/null and b/themes/zenburn/layouts/tilebottom.png differ diff --git a/themes/zenburn/layouts/tileleft.png b/themes/zenburn/layouts/tileleft.png new file mode 100644 index 0000000..c5decfd Binary files /dev/null and b/themes/zenburn/layouts/tileleft.png differ diff --git a/themes/zenburn/layouts/tiletop.png b/themes/zenburn/layouts/tiletop.png new file mode 100644 index 0000000..b251661 Binary files /dev/null and b/themes/zenburn/layouts/tiletop.png differ diff --git a/themes/zenburn/taglist/squarefz.png b/themes/zenburn/taglist/squarefz.png new file mode 100644 index 0000000..0927720 Binary files /dev/null and b/themes/zenburn/taglist/squarefz.png differ diff --git a/themes/zenburn/taglist/squarez.png b/themes/zenburn/taglist/squarez.png new file mode 100644 index 0000000..9b41c26 Binary files /dev/null and b/themes/zenburn/taglist/squarez.png differ diff --git a/themes/zenburn/theme.lua b/themes/zenburn/theme.lua new file mode 100644 index 0000000..0c46824 --- /dev/null +++ b/themes/zenburn/theme.lua @@ -0,0 +1,158 @@ +---------------------------------------- +-- "idk what yet" awesome theme -- +-- By Lukas B. (Surferlul) -- +-- based on "Zenburn" awesome theme -- +-- By Adrian C. (anrxc) -- +---------------------------------------- + +local themes_path = require("gears.filesystem").get_themes_dir() +local dpi = require("beautiful.xresources").apply_dpi +local beautiful = require("beautiful") + +-- {{{ Main +local theme = {} +theme.wallpaper = "/home/surferlul/.config/awesome/themes/zenburn/artix_wallpaper.png" +-- }}} + + +-- {{{ Styles +theme.font = "sans 8" + +-- {{{ Colors +theme.fg_normal = "#EDEDDD" +theme.fg_focus = "#F0DFAF" +theme.fg_urgent = "#CC9393" +theme.bg_normal = "#4A3F9833" +theme.bg_focus = "#080811CC" +theme.bg_urgent = "#916A9D33" +theme.bg_systray = theme.bg_normal +-- }}} + +theme.hotkeys_bg = "#111122" +theme.hotkeys_fg = "#EEEEFF" +theme.hotkeys_label_fg = "#111122" +theme.hotkeys_modifiers_fg = "#DD7777" +theme.hotkeys_description_font = "Indie Flower Bold 10" +theme.hotkeys_border_width = 1 +theme.hotkeys_border_color = "#6f6f6f55" + +theme.menubar_bg_normal = "#11112299" +theme.menubar_fg_normal = "#FFBBBB" +theme.menubar_bg_focus = "#333344FF" +theme.menubar_fg_focus = "#BBBBFF" + +theme.menu_bg_normal = "#11112299" +theme.menu_fg_normal = "#FFBBBB" +theme.menu_bg_focus = "#333344FF" +theme.menu_fg_focus = "#BBBBFF" + +-- {{{ Borders +theme.useless_gap = dpi(2) +theme.border_width = dpi(1) +theme.border_normal = "#6F6F6F55" +theme.border_focus = "#6F6F6F55" +theme.border_marked = "#CC939355" +-- }}} + +-- {{{ Titlebars +theme.titlebar_bg_focus = "#3F3F3F" +theme.titlebar_bg_normal = "#3F3F3F" +-- }}} + +-- There are other variable sets +-- overriding the default one when +-- defined, the sets are: +-- [taglist|tasklist]_[bg|fg]_[focus|urgent|occupied|empty|volatile] +-- titlebar_[normal|focus] +-- tooltip_[font|opacity|fg_color|bg_color|border_width|border_color] +-- Example: +--theme.taglist_bg_focus = "#CC9393" +-- }}} + +-- {{{ Widgets +-- You can add as many variables as +-- you wish and access them by using +-- beautiful.variable in your rc.lua +--theme.fg_widget = "#AECF96" +--theme.fg_center_widget = "#88A175" +--theme.fg_end_widget = "#FF5656" +--theme.bg_widget = "#494B4F" +--theme.border_widget = "#3F3F3F" +-- }}} + +-- {{{ Mouse finder +theme.mouse_finder_color = "#CC9393" +-- mouse_finder_[timeout|animate_timeout|radius|factor] +-- }}} + +-- {{{ Menu +-- Variables set for theming the menu: +-- menu_[bg|fg]_[normal|focus] +-- menu_[border_color|border_width] +theme.menu_height = dpi(15) +theme.menu_width = dpi(100) +-- }}} + +-- {{{ Icons +-- {{{ Taglist +theme.taglist_squares_sel = themes_path .. "zenburn/taglist/squarefz.png" +theme.taglist_squares_unsel = themes_path .. "zenburn/taglist/squarez.png" +--theme.taglist_squares_resize = "false" +-- }}} + +-- {{{ Misc +theme.awesome_icon = themes_path .. "zenburn/awesome-icon.png" +theme.menu_submenu_icon = themes_path .. "default/submenu.png" +-- }}} + +-- {{{ Layout +theme.layout_tile = themes_path .. "zenburn/layouts/tile.png" +theme.layout_tileleft = themes_path .. "zenburn/layouts/tileleft.png" +theme.layout_tilebottom = themes_path .. "zenburn/layouts/tilebottom.png" +theme.layout_tiletop = themes_path .. "zenburn/layouts/tiletop.png" +theme.layout_fairv = themes_path .. "zenburn/layouts/fairv.png" +theme.layout_fairh = themes_path .. "zenburn/layouts/fairh.png" +theme.layout_spiral = themes_path .. "zenburn/layouts/spiral.png" +theme.layout_dwindle = themes_path .. "zenburn/layouts/dwindle.png" +theme.layout_max = themes_path .. "zenburn/layouts/max.png" +theme.layout_fullscreen = themes_path .. "zenburn/layouts/fullscreen.png" +theme.layout_magnifier = themes_path .. "zenburn/layouts/magnifier.png" +theme.layout_floating = themes_path .. "zenburn/layouts/floating.png" +theme.layout_cornernw = themes_path .. "zenburn/layouts/cornernw.png" +theme.layout_cornerne = themes_path .. "zenburn/layouts/cornerne.png" +theme.layout_cornersw = themes_path .. "zenburn/layouts/cornersw.png" +theme.layout_cornerse = themes_path .. "zenburn/layouts/cornerse.png" +-- }}} + +-- {{{ Titlebar +theme.titlebar_close_button_focus = themes_path .. "zenburn/titlebar/close_focus.png" +theme.titlebar_close_button_normal = themes_path .. "zenburn/titlebar/close_normal.png" + +theme.titlebar_minimize_button_normal = themes_path .. "default/titlebar/minimize_normal.png" +theme.titlebar_minimize_button_focus = themes_path .. "default/titlebar/minimize_focus.png" + +theme.titlebar_ontop_button_focus_active = themes_path .. "zenburn/titlebar/ontop_focus_active.png" +theme.titlebar_ontop_button_normal_active = themes_path .. "zenburn/titlebar/ontop_normal_active.png" +theme.titlebar_ontop_button_focus_inactive = themes_path .. "zenburn/titlebar/ontop_focus_inactive.png" +theme.titlebar_ontop_button_normal_inactive = themes_path .. "zenburn/titlebar/ontop_normal_inactive.png" + +theme.titlebar_sticky_button_focus_active = themes_path .. "zenburn/titlebar/sticky_focus_active.png" +theme.titlebar_sticky_button_normal_active = themes_path .. "zenburn/titlebar/sticky_normal_active.png" +theme.titlebar_sticky_button_focus_inactive = themes_path .. "zenburn/titlebar/sticky_focus_inactive.png" +theme.titlebar_sticky_button_normal_inactive = themes_path .. "zenburn/titlebar/sticky_normal_inactive.png" + +theme.titlebar_floating_button_focus_active = themes_path .. "zenburn/titlebar/floating_focus_active.png" +theme.titlebar_floating_button_normal_active = themes_path .. "zenburn/titlebar/floating_normal_active.png" +theme.titlebar_floating_button_focus_inactive = themes_path .. "zenburn/titlebar/floating_focus_inactive.png" +theme.titlebar_floating_button_normal_inactive = themes_path .. "zenburn/titlebar/floating_normal_inactive.png" + +theme.titlebar_maximized_button_focus_active = themes_path .. "zenburn/titlebar/maximized_focus_active.png" +theme.titlebar_maximized_button_normal_active = themes_path .. "zenburn/titlebar/maximized_normal_active.png" +theme.titlebar_maximized_button_focus_inactive = themes_path .. "zenburn/titlebar/maximized_focus_inactive.png" +theme.titlebar_maximized_button_normal_inactive = themes_path .. "zenburn/titlebar/maximized_normal_inactive.png" +-- }}} +-- }}} + +return theme + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/themes/zenburn/titlebar/close_focus.png b/themes/zenburn/titlebar/close_focus.png new file mode 100644 index 0000000..cccbadc Binary files /dev/null and b/themes/zenburn/titlebar/close_focus.png differ diff --git a/themes/zenburn/titlebar/close_normal.png b/themes/zenburn/titlebar/close_normal.png new file mode 100644 index 0000000..6e6b645 Binary files /dev/null and b/themes/zenburn/titlebar/close_normal.png differ diff --git a/themes/zenburn/titlebar/floating_focus_active.png b/themes/zenburn/titlebar/floating_focus_active.png new file mode 100644 index 0000000..77ea127 Binary files /dev/null and b/themes/zenburn/titlebar/floating_focus_active.png differ diff --git a/themes/zenburn/titlebar/floating_focus_inactive.png b/themes/zenburn/titlebar/floating_focus_inactive.png new file mode 100644 index 0000000..94b0360 Binary files /dev/null and b/themes/zenburn/titlebar/floating_focus_inactive.png differ diff --git a/themes/zenburn/titlebar/floating_normal_active.png b/themes/zenburn/titlebar/floating_normal_active.png new file mode 100644 index 0000000..878a9fb Binary files /dev/null and b/themes/zenburn/titlebar/floating_normal_active.png differ diff --git a/themes/zenburn/titlebar/floating_normal_inactive.png b/themes/zenburn/titlebar/floating_normal_inactive.png new file mode 100644 index 0000000..4147c7d Binary files /dev/null and b/themes/zenburn/titlebar/floating_normal_inactive.png differ diff --git a/themes/zenburn/titlebar/maximized_focus_active.png b/themes/zenburn/titlebar/maximized_focus_active.png new file mode 100644 index 0000000..9fc0483 Binary files /dev/null and b/themes/zenburn/titlebar/maximized_focus_active.png differ diff --git a/themes/zenburn/titlebar/maximized_focus_inactive.png b/themes/zenburn/titlebar/maximized_focus_inactive.png new file mode 100644 index 0000000..298751c Binary files /dev/null and b/themes/zenburn/titlebar/maximized_focus_inactive.png differ diff --git a/themes/zenburn/titlebar/maximized_normal_active.png b/themes/zenburn/titlebar/maximized_normal_active.png new file mode 100644 index 0000000..1dca071 Binary files /dev/null and b/themes/zenburn/titlebar/maximized_normal_active.png differ diff --git a/themes/zenburn/titlebar/maximized_normal_inactive.png b/themes/zenburn/titlebar/maximized_normal_inactive.png new file mode 100644 index 0000000..f943ac7 Binary files /dev/null and b/themes/zenburn/titlebar/maximized_normal_inactive.png differ diff --git a/themes/zenburn/titlebar/ontop_focus_active.png b/themes/zenburn/titlebar/ontop_focus_active.png new file mode 100644 index 0000000..86e61b7 Binary files /dev/null and b/themes/zenburn/titlebar/ontop_focus_active.png differ diff --git a/themes/zenburn/titlebar/ontop_focus_inactive.png b/themes/zenburn/titlebar/ontop_focus_inactive.png new file mode 100644 index 0000000..04bc63a Binary files /dev/null and b/themes/zenburn/titlebar/ontop_focus_inactive.png differ diff --git a/themes/zenburn/titlebar/ontop_normal_active.png b/themes/zenburn/titlebar/ontop_normal_active.png new file mode 100644 index 0000000..8d9cb5b Binary files /dev/null and b/themes/zenburn/titlebar/ontop_normal_active.png differ diff --git a/themes/zenburn/titlebar/ontop_normal_inactive.png b/themes/zenburn/titlebar/ontop_normal_inactive.png new file mode 100644 index 0000000..d383c77 Binary files /dev/null and b/themes/zenburn/titlebar/ontop_normal_inactive.png differ diff --git a/themes/zenburn/titlebar/sticky_focus_active.png b/themes/zenburn/titlebar/sticky_focus_active.png new file mode 100644 index 0000000..5af45c1 Binary files /dev/null and b/themes/zenburn/titlebar/sticky_focus_active.png differ diff --git a/themes/zenburn/titlebar/sticky_focus_inactive.png b/themes/zenburn/titlebar/sticky_focus_inactive.png new file mode 100644 index 0000000..1a0c481 Binary files /dev/null and b/themes/zenburn/titlebar/sticky_focus_inactive.png differ diff --git a/themes/zenburn/titlebar/sticky_normal_active.png b/themes/zenburn/titlebar/sticky_normal_active.png new file mode 100644 index 0000000..50a6d2b Binary files /dev/null and b/themes/zenburn/titlebar/sticky_normal_active.png differ diff --git a/themes/zenburn/titlebar/sticky_normal_inactive.png b/themes/zenburn/titlebar/sticky_normal_inactive.png new file mode 100644 index 0000000..ea09406 Binary files /dev/null and b/themes/zenburn/titlebar/sticky_normal_inactive.png differ diff --git a/tools/headergen b/tools/headergen new file mode 100755 index 0000000..fe3dd51 --- /dev/null +++ b/tools/headergen @@ -0,0 +1,100 @@ +#!/usr/bin/env lua +-- copyright header generator +-- Copyright (C) 2019 Nguyễn Gia Phong +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +local ipairs = ipairs +local pairs = pairs +local tonumber = tonumber +local io = { open = io.open, popen = io.popen } +local os = { exit = os.exit } +local table = { + concat = table.concat, + insert = table.insert, + sort = table.sort +} + +local HEADER = [[-- %s +%s +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +%s]] +local COMMAND = "git log --date=format:%Y --format='%ad %an <%ae>' " + +if #arg == 0 then + print'usage: headergen lua-source-files' + os.exit(1) +end + +for _, filename in ipairs(arg) do + local fi = io.open(filename) + local content = fi:read'*a' + fi:close() + + local holders = {} + local output = io.popen(COMMAND .. filename) + for line in output:lines() do + local year, author = line:match'(%d+)%s+(.+)' + if holders[author] == nil then holders[author] = {} end + holders[author][year] = true + end + output:close() + + local copyrights = {} + for author, years in pairs(holders) do + local time = {} + for year, _ in pairs(years) do table.insert(time, tonumber(year)) end + table.sort(time) + + local copyright = { 'Copyright (C) ' } + for _, current in ipairs(time) do + local prev = tonumber(copyright[#copyright]) + if prev == nil then + table.insert(copyright, current) + elseif current - 1 ~= prev then + table.insert(copyright, ',') + table.insert(copyright, current) + elseif copyright[#copyright - 1] == '-' then + copyright[#copyright] = current + else + table.insert(copyright, '-') + table.insert(copyright, current) + end + end + table.insert(copyrights, + ('-- %s %s'):format(table.concat(copyright), author)) + end + + local fo = io.open(filename, 'w') + table.sort(copyrights) + fo:write(HEADER:format(filename, table.concat(copyrights, '\n'), content)) + fo:close() +end diff --git a/tools/luacheckrc b/tools/luacheckrc new file mode 100644 index 0000000..d98f02c --- /dev/null +++ b/tools/luacheckrc @@ -0,0 +1,23 @@ +-- Only allow symbols available in all Lua versions +std = "min" + +-- Global objects defined by the C code +read_globals = { + "timer", -- deprecated, but used in older versions. +} + +include_files = { + "*.lua", -- libraries + "widgets/*.lua", -- officially supported widget types + "templates/*.lua", -- officially supported widget types +} + +-- Warnings to be ignored +ignore = { + "212", -- Unused argument. +} + +-- Not enforced, but preferable +max_code_line_length = 80 + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/vicious b/vicious new file mode 160000 index 0000000..150864a --- /dev/null +++ b/vicious @@ -0,0 +1 @@ +Subproject commit 150864a76d4b18c97da1459f355c6a86540a0863 diff --git a/widgets/bat_freebsd.lua b/widgets/bat_freebsd.lua new file mode 100644 index 0000000..384f643 --- /dev/null +++ b/widgets/bat_freebsd.lua @@ -0,0 +1,94 @@ +-- battery widget type for FreeBSD +-- Copyright (C) 2017-2019 mutlusun +-- Copyright (C) 2019 Nguyễn Gia Phong +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local tonumber = tonumber +local math = { floor = math.floor } +local string = { + gmatch = string.gmatch, + format = string.format +} + +local helpers = require("vicious.helpers") +local spawn = require("vicious.spawn") +-- }}} + +-- Battery: provides battery level of requested battery +-- vicious.widgets.battery_freebsd +local bat_freebsd = {} + +-- {{{ Battery widget type +local function parse(stdout, stderr, exitreason, exitcode) + local bat_info = {} + for line in string.gmatch(stdout, "[^\n]+") do + for key,value in string.gmatch(line, "(.+):%s+(.+)") do + bat_info[key] = value + end + end + + -- current state + -- see: https://github.com/freebsd/freebsd/blob/master/usr.sbin/acpi/acpiconf/acpiconf.c + local battery_state = { + ["high"] = "↯", + ["charging"] = "+", + ["critical charging"] = "+", + ["discharging"] = "-", + ["critical discharging"] = "!", + ["critical"] = "!", + } + local state = battery_state[bat_info["State"]] or "N/A" + + -- battery capacity in percent + local percent = tonumber(bat_info["Remaining capacity"]:match"[%d]+") + + -- use remaining (charging or discharging) time calculated by acpiconf + local time = bat_info["Remaining time"] + if time == "unknown" then + time = "∞" + end + + -- calculate wear level from (last full / design) capacity + local wear = "N/A" + if bat_info["Last full capacity"] and bat_info["Design capacity"] then + local l_full = tonumber(bat_info["Last full capacity"]:match"[%d]+") + local design = tonumber(bat_info["Design capacity"]:match"[%d]+") + wear = math.floor(l_full / design * 100) + end + + -- dis-/charging rate as presented by battery + local rate = bat_info["Present rate"]:match"([%d]+)%smW" + rate = string.format("%2.1f", tonumber(rate / 1000)) + + -- returns + -- * state (high "↯", discharging "-", charging "+", N/A "⌁" } + -- * remaining_capacity (percent) + -- * remaining_time, by battery + -- * wear level (percent) + -- * present_rate (mW) + return {state, percent, time, wear, rate} +end + +function bat_freebsd.async(format, warg, callback) + local battery = warg or "batt" + spawn.easy_async("acpiconf -i " .. helpers.shellquote(battery), + function (...) callback(parse(...)) end) +end +-- }}} + +return helpers.setasyncall(bat_freebsd) diff --git a/widgets/bat_linux.lua b/widgets/bat_linux.lua new file mode 100644 index 0000000..186c6e8 --- /dev/null +++ b/widgets/bat_linux.lua @@ -0,0 +1,109 @@ +-- battery widget type for GNU/Linux +-- Copyright (C) 2010 Adrian C. +-- Copyright (C) 2013 NormalRa +-- Copyright (C) 2017 David Udelson +-- Copyright (C) 2017 Roberto +-- Copyright (C) 2017 mutlusun +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local tonumber = tonumber +local string = { format = string.format } +local math = { + min = math.min, + floor = math.floor +} + +local helpers = require"vicious.helpers" +-- }}} + +-- {{{ Battery widget type +return helpers.setcall(function (format, warg) + if not warg then return end + + local battery = helpers.pathtotable("/sys/class/power_supply/"..warg) + local battery_state = { + ["Full\n"] = "↯", + ["Unknown\n"] = "⌁", + ["Charged\n"] = "↯", + ["Charging\n"] = "+", + ["Discharging\n"] = "-" + } + + -- Get current power usage in watt + local curpower = "N/A" + if battery.power_now then + curpower = string.format("%.2f", tonumber(battery.power_now) /1000000) + end + + -- Check if the battery is present + if battery.present ~= "1\n" then + return {battery_state["Unknown\n"], 0, "N/A", 0, curpower} + end + + -- Get state information + local state = battery_state[battery.status] or battery_state["Unknown\n"] + + -- Get capacity information + local remaining, capacity, capacity_design + if battery.charge_now then + remaining, capacity = battery.charge_now, battery.charge_full + capacity_design = battery.charge_full_design or capacity + elseif battery.energy_now then + remaining, capacity = battery.energy_now, battery.energy_full + capacity_design = battery.energy_full_design or capacity + else + return {battery_state["Unknown\n"], 0, "N/A", 0, curpower} + end + + -- Calculate capacity and wear percentage (but work around broken BAT/ACPI implementations) + local percent = math.min(math.floor(remaining / capacity * 100), 100) + local wear = math.floor(100 - capacity / capacity_design * 100) + + -- Get charge information + local rate + if battery.current_now then + rate = tonumber(battery.current_now) + elseif battery.power_now then + rate = tonumber(battery.power_now) + else + return {state, percent, "N/A", wear, curpower} + end + + -- Calculate remaining (charging or discharging) time + local time = "N/A" + + if rate ~= nil and rate ~= 0 then + local timeleft + if state == "+" then + timeleft = (tonumber(capacity)-tonumber(remaining)) / tonumber(rate) + elseif state == "-" then + timeleft = tonumber(remaining) / tonumber(rate) + else + return {state, percent, time, wear, curpower} + end + + -- Calculate time + local hoursleft = math.floor(timeleft) + local minutesleft = math.floor((timeleft - hoursleft) * 60 ) + + time = string.format("%02d:%02d", hoursleft, minutesleft) + end + + return {state, percent, time, wear, curpower} +end) +-- }}} diff --git a/widgets/bat_openbsd.lua b/widgets/bat_openbsd.lua new file mode 100644 index 0000000..fe7665d --- /dev/null +++ b/widgets/bat_openbsd.lua @@ -0,0 +1,94 @@ +-- battery widget type for OpenBSD +-- Copyright (C) 2019 Enric Morales +-- Copyright (C) 2019 Nguyễn Gia Phong +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local pairs = pairs +local tonumber = tonumber +local table = { + insert = table.insert +} + +local math = { + floor = math.floor, + modf = math.modf +} + +local helpers = require("vicious.helpers") +-- }}} + +local bat_openbsd = {} +function bat_openbsd.async(format, warg, callback) + local battery_id = warg or "bat0" + + local fields = { + charging_rate = ("hw.sensors.acpi%s.power0"):format(battery_id), + last_full_capacity = ("hw.sensors.acpi%s.watthour0"):format(battery_id), + remaining_capacity = ("hw.sensors.acpi%s.watthour3"):format(battery_id), + design_capacity = ("hw.sensors.acpi%s.watthour4"):format(battery_id), + state = ("hw.sensors.acpi%s.raw0"):format(battery_id) + } + + local sysctl_args = {} + for _, v in pairs(fields) do table.insert(sysctl_args, v) end + + local battery = {} + helpers.sysctl_async(sysctl_args, function (ret) + for k, v in pairs(fields) do + -- discard the description that comes after the values + battery[k] = tonumber(ret[v]:match("(.-) ")) + end + + local states = { + [0] = "↯", -- not charging + [1] = "-", -- discharging + [2] = "!", -- critical + [3] = "+", -- charging + [4] = "N/A", -- unknown status + [255] = "N/A" -- unimplemented by the driver + } + local state = states[battery.state] + + local charge = tonumber(battery.remaining_capacity + / battery.last_full_capacity * 100) + + local remaining_time + if battery.charging_rate < 1 then + remaining_time = "∞" + else + local raw_time = battery.remaining_capacity / battery.rate + local hours, hour_fraction = math.modf(raw_time) + local minutes = math.floor(60 * hour_fraction) + remaining_time = ("%d:%0.2d"):format(hours, minutes) + end + + local wear = math.floor(battery.last_full_capacity, + battery.design_capacity) + + -- Pass the following arguments to callback function: + -- * battery state symbol (↯, -, !, + or N/A) + -- * remaining capacity (in percent) + -- * remaining time, as reported by the battery + -- * wear level (in percent) + -- * present_rate (in Watts/hour) + return callback({ state, charge, remaining_time, + wear, battery.charging_rate }) + end) +end + +return helpers.setasyncall(bat_openbsd) diff --git a/widgets/cmus_all.lua b/widgets/cmus_all.lua new file mode 100644 index 0000000..e0b046e --- /dev/null +++ b/widgets/cmus_all.lua @@ -0,0 +1,66 @@ +-- cmus music player widget type +-- Copyright (C) 2017 JuanKman94 +-- Copyright (C) 2017 Joerg Thalheim +-- Copyright (C) 2018-2019 Nguyễn Gia Phong +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local type = type +local tonumber = tonumber +local os = { getenv = os.getenv } + +local helpers = require"vicious.helpers" +local spawn = require"vicious.spawn" +-- }}} + +local CMUS_SOCKET = helpers.shellquote(os.getenv"CMUS_SOCKET") + +-- Cmus: provides CMUS information +-- vicious.widgets.cmus +return helpers.setasyncall{ + async = function (format, warg, callback) + local server = "" + if type(warg) == "table" then + server = " --server " .. helpers.shellquote(warg.host or warg[1]) + elseif CMUS_SOCKET ~= nil then + server = " --server " .. CMUS_SOCKET + end + + local cmus_state = { ["{duration}"] = 0, ["{file}"] = "N/A", + ["{status}"] = "N/A", ["{title}"] = "N/A", + ["{artist}"] = "N/A", ["{continue}"] = "off", + ["{shuffle}"] = "off", ["{repeat}"] = "off" } + + spawn.with_line_callback("cmus-remote --query" .. server, { + stdout = function (line) + for module, value in line:gmatch"([%w]+) (.*)$" do + if module == "file" or module == "status" then + cmus_state["{"..module.."}"] = value + elseif module == "duration" then + cmus_state["{"..module.."}"] = tonumber(value) + else + local k, v = value:gmatch("([%w]+) (.*)$")() + if module == "tag" then + cmus_state["{"..k.."}"] = v + elseif module == "set" and v == "true" then + cmus_state["{"..k.."}"] = "on" + end + end + end + end, + output_done = function () callback(cmus_state) end }) + end } diff --git a/widgets/cpu_freebsd.lua b/widgets/cpu_freebsd.lua new file mode 100644 index 0000000..63809e9 --- /dev/null +++ b/widgets/cpu_freebsd.lua @@ -0,0 +1,89 @@ +-- CPU usage widget type for FreeBSD +-- Copyright (C) 2017,2019 mutlusun +-- Copyright (C) 2019 Nguyễn Gia Phong +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local math = { floor = math.floor } +local string = { gmatch = string.gmatch } + +local helpers = require("vicious.helpers") +-- }}} + +-- Cpu: provides CPU usage for all available CPUs/cores +-- vicious.widgets.cpu_freebsd +local cpu_freebsd = {} + +-- Initialize function tables +local cpu_total = {} +local cpu_idle = {} + +-- {{{ CPU widget type +function cpu_freebsd.async(format, warg, callback) + local matches = {} + local tmp_total = {} + local tmp_idle = {} + local tmp_usage = {} + + helpers.sysctl_async({ "kern.cp_times" }, function(ret) + -- Read input data + for v in string.gmatch(ret["kern.cp_times"], "([%d]+)") do + table.insert(matches, v) + end + + -- Set first value of function tables + if #cpu_total == 0 then -- check for empty table + for i = 1, #matches / 5 + 1 do + cpu_total[i] = 0 + cpu_idle[i] = 0 + end + end + for i = 1, #matches / 5 + 1 do + tmp_total[i] = 0 + tmp_idle[i] = 0 + tmp_usage[i] = 0 + end + + -- CPU usage + for i, v in ipairs(matches) do + local index = math.floor((i-1) / 5) + 2 -- current cpu + + tmp_total[1] = tmp_total[1] + v + tmp_total[index] = tmp_total[index] + v + + if (i-1) % 5 == 4 then + tmp_idle[1] = tmp_idle[1] + v + tmp_idle[index] = tmp_idle[index] + v + end + end + + for i = 1, #tmp_usage do + tmp_usage[i] = tmp_total[i] - cpu_total[i] + tmp_usage[i] = math.floor( + (tmp_usage[i] - (tmp_idle[i] - cpu_idle[i])) + / tmp_usage[i] * 100) + end + + cpu_total = tmp_total + cpu_idle = tmp_idle + + return callback(tmp_usage) + end) +end +-- }}} + +return helpers.setasyncall(cpu_freebsd) diff --git a/widgets/cpu_linux.lua b/widgets/cpu_linux.lua new file mode 100644 index 0000000..c023400 --- /dev/null +++ b/widgets/cpu_linux.lua @@ -0,0 +1,86 @@ +-- CPU usage widget type for GNU/Linux +-- Copyright (C) 2009 Lucas de Vries +-- Copyright (C) 2011 Adrian C. +-- Copyright (C) 2011 Jörg Thalheim +-- Copyright (C) 2017 mutlusun +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local ipairs = ipairs +local io = { open = io.open } +local math = { floor = math.floor } +local table = { insert = table.insert } +local string = { + sub = string.sub, + gmatch = string.gmatch +} + +local helpers = require"vicious.helpers" +-- }}} + +-- Initialize function tables +local cpu_usage = {} +local cpu_total = {} +local cpu_active = {} + +-- {{{ CPU widget type +return helpers.setcall(function () + local cpu_lines = {} + + -- Get CPU stats + local f = io.open("/proc/stat") + for line in f:lines() do + if string.sub(line, 1, 3) ~= "cpu" then break end + + cpu_lines[#cpu_lines+1] = {} + + for i in string.gmatch(line, "[%s]+([^%s]+)") do + table.insert(cpu_lines[#cpu_lines], i) + end + end + f:close() + + -- Ensure tables are initialized correctly + for i = #cpu_total + 1, #cpu_lines do + cpu_total[i] = 0 + cpu_usage[i] = 0 + cpu_active[i] = 0 + end + + for i, v in ipairs(cpu_lines) do + -- Calculate totals + local total_new = 0 + for j = 1, #v do + total_new = total_new + v[j] + end + local active_new = total_new - (v[4] + v[5]) + + -- Calculate percentage + local diff_total = total_new - cpu_total[i] + local diff_active = active_new - cpu_active[i] + + if diff_total == 0 then diff_total = 1E-6 end + cpu_usage[i] = math.floor((diff_active / diff_total) * 100) + + -- Store totals + cpu_total[i] = total_new + cpu_active[i] = active_new + end + + return cpu_usage +end) +-- }}} diff --git a/widgets/cpu_openbsd.lua b/widgets/cpu_openbsd.lua new file mode 100644 index 0000000..3a5ccfa --- /dev/null +++ b/widgets/cpu_openbsd.lua @@ -0,0 +1,60 @@ +-- CPU usage widget type for OpenBSD +-- Copyright (C) 2019 Enric Morales +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +local math = { ceil = math.ceil } +local string = { gmatch = string.gmatch } +local table = { insert = table.insert } +local tonumber = tonumber + +local helpers = require("vicious.helpers") + + +-- cpu_openbsd: provides both a helper function that allows reading +-- the CPU usage on OpenBSD systems. +local cpu_openbsd = {} + +-- Initialize the table that will contain the ticks spent in each subsystem +-- values: user, nice, system, spin, interrupts, idle +local ticks = { 0, 0, 0, 0, 0, 0 } + +function cpu_openbsd.async(format, warg, callback) + helpers.sysctl_async({ "kern.cp_time" }, + function (ret) + local current_ticks = {} + for match in string.gmatch(ret["kern.cp_time"], "(%d+)") do + table.insert(current_ticks, tonumber(match)) + end + + local period_ticks = {} + for i = 1, 6 do + table.insert(period_ticks, + current_ticks[i] - ticks[i]) + end + + local cpu_total, cpu_busy = 0, 0 + for i = 1, 6 do cpu_total = cpu_total + period_ticks[i] end + for i = 1, 5 do cpu_busy = cpu_busy + period_ticks[i] end + + local cpu_usage = math.ceil((cpu_busy / cpu_total) * 100) + + ticks = current_ticks + return callback({ cpu_usage }) + end) +end + +return helpers.setasyncall(cpu_openbsd) diff --git a/widgets/cpufreq_freebsd.lua b/widgets/cpufreq_freebsd.lua new file mode 100644 index 0000000..ea20663 --- /dev/null +++ b/widgets/cpufreq_freebsd.lua @@ -0,0 +1,57 @@ +-- CPU frequency widget type for FreeBSD +-- Copyright (C) 2017,2019 mutlusun +-- Copyright (C) 2019 Nguyễn Gia Phong +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local tonumber = tonumber +local helpers = require("vicious.helpers") +-- }}} + +-- Cpufreq: provides freq, voltage and governor info for a requested CPU +-- vicious.widgets.cpufreq +local cpufreq_freebsd = {} + +-- {{{ CPU frequency widget type +function cpufreq_freebsd.async(format, warg, callback) + if not warg then return callback({}) end + + -- Default frequency and voltage values + local freqv = { + ["mhz"] = "N/A", ["ghz"] = "N/A", + ["v"] = "N/A", ["mv"] = "N/A", + ["governor"] = "N/A", + } + + helpers.sysctl_async( + { "dev.cpu." .. warg .. ".freq" }, + function (ret) + freqv.mhz = tonumber(ret["dev.cpu." .. warg .. ".freq"]) + freqv.ghz = freqv.mhz / 1000 + + return callback({ + freqv.mhz, + freqv.ghz, + freqv.mv, + freqv.v, + freqv.governor + }) + end) +end +-- }}} + +return helpers.setasyncall(cpufreq_freebsd) diff --git a/widgets/cpufreq_linux.lua b/widgets/cpufreq_linux.lua new file mode 100644 index 0000000..f3877e4 --- /dev/null +++ b/widgets/cpufreq_linux.lua @@ -0,0 +1,68 @@ +-- CPU frequency widget type for GNU/Linux +-- Copyright (C) 2010 Adrian C. +-- Copyright (C) 2017 mutlusun +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local tonumber = tonumber +local helpers = require("vicious.helpers") +-- }}} + +local GOVERNOR_STATE = { + ["ondemand\n"] = "↯", + ["powersave\n"] = "⌁", + ["userspace\n"] = "¤", + ["performance\n"] = "⚡", + ["conservative\n"] = "⊚" +} + +-- {{{ CPU frequency widget type +return helpers.setcall(function (format, warg) + if not warg then return end + + local _cpufreq = helpers.pathtotable( + ("/sys/devices/system/cpu/%s/cpufreq"):format(warg)) + -- Default frequency and voltage values + local freqv = { + ["mhz"] = "N/A", ["ghz"] = "N/A", + ["v"] = "N/A", ["mv"] = "N/A", + } + + -- Get the current frequency + local freq = tonumber(_cpufreq.scaling_cur_freq) + -- Calculate MHz and GHz + if freq then + freqv.mhz = freq / 1000 + freqv.ghz = freqv.mhz / 1000 + + -- Get the current voltage + if _cpufreq.scaling_voltages then + freqv.mv = tonumber( + _cpufreq.scaling_voltages:match(freq .. "[%s]([%d]+)")) + -- Calculate voltage from mV + freqv.v = freqv.mv / 1000 + end + end + + -- Get the current governor + local governor = _cpufreq.scaling_governor + -- Represent the governor as a symbol + governor = GOVERNOR_STATE[governor] or governor or "N/A" + + return {freqv.mhz, freqv.ghz, freqv.mv, freqv.v, governor} +end) +-- }}} diff --git a/widgets/cpuinf_linux.lua b/widgets/cpuinf_linux.lua new file mode 100644 index 0000000..0df845f --- /dev/null +++ b/widgets/cpuinf_linux.lua @@ -0,0 +1,50 @@ +-- CPU information widget type for GNU/Linux +-- Copyright (C) 2010 Adrian C. +-- Copyright (C) 2017 mutlusun +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local tonumber = tonumber +local io = { lines = io.lines } +local string = { gmatch = string.gmatch } +local helpers = require"vicious.helpers" +-- }}} + +-- {{{ CPU Information widget type +return helpers.setcall(function () + local id = nil + + local cpu_info = {} -- Get CPU info + for line in io.lines("/proc/cpuinfo") do + for k, v in string.gmatch(line, "([%a%s]+)[%s]+:[%s]([%d]+).-$") do + if k == "processor" then + id = v + elseif k == "cpu MHz\t" or k == "cpu MHz" then + local speed = tonumber(v) + cpu_info["{cpu"..id.." mhz}"] = speed + cpu_info["{cpu"..id.." ghz}"] = speed / 1000 + elseif k == "cache size" then + local cache = tonumber(v) + cpu_info["{cpu"..id.." kb}"] = cache + cpu_info["{cpu"..id.." mb}"] = cache / 1024 + end + end + end + + return cpu_info +end) +-- }}} diff --git a/widgets/date_all.lua b/widgets/date_all.lua new file mode 100644 index 0000000..459d505 --- /dev/null +++ b/widgets/date_all.lua @@ -0,0 +1,29 @@ +-- date and time widget type using os.date with optional time formatting +-- Copyright (C) 2009 Lucas de Vries +-- Copyright (C) 2010 Adrian C. +-- Copyright (C) 2017 mutlusun +-- Copyright (C) 2019 Nguyễn Gia Phong +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local os = { date = os.date, time = os.time } +local helpers = require"vicious.helpers" +-- }}} + +return helpers.setcall(function (format, warg) + return os.date(format or nil, warg and os.time()+warg or nil) +end) diff --git a/widgets/dio_linux.lua b/widgets/dio_linux.lua new file mode 100644 index 0000000..60c2343 --- /dev/null +++ b/widgets/dio_linux.lua @@ -0,0 +1,79 @@ +-- disk I/O widget type for GNU/Linux +-- Copyright (C) 2011 Jörg T. +-- Copyright (C) 2017 Elric Milon +-- Copyright (C) 2017 mutlusun +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local pairs = pairs +local io = { lines = io.lines } +local os = { time = os.time, difftime = os.difftime } + +local helpers = require"vicious.helpers" +-- }}} + +-- Initialize function tables +local disk_usage = {} +local disk_stats = {} +local disk_time = 0 +-- Constant definitions +local unit = { ["s"] = 1, ["kb"] = 2, ["mb"] = 2048 } +local time_unit = { ["ms"] = 1, ["s"] = 1000 } + +-- {{{ Disk I/O widget type +return helpers.setcall(function () + local disk_lines = {} + + for line in io.lines("/proc/diskstats") do + local device, read, write, iotime = + -- Linux kernel documentation: Documentation/iostats.txt + line:match"([^%s]+) %d+ %d+ (%d+) %d+ %d+ %d+ (%d+) %d+ %d+ (%d+)" + disk_lines[device] = { read, write, iotime } + end + + local time = os.time() + local interval = os.difftime(time, disk_time) + if interval == 0 then interval = 1 end + + for device, stats in pairs(disk_lines) do + -- Avoid insane values on startup + local last_stats = disk_stats[device] or stats + + -- Check for overflows and counter resets (> 2^32) + if stats[1] < last_stats[1] or stats[2] < last_stats[2] then + for i = 1,3 do last_stats[i] = stats[i] end + end + + -- Diskstats are absolute, substract our last reading + -- * divide by timediff because we don't know the timer value + local read = (stats[1] - last_stats[1]) / interval + local write = (stats[2] - last_stats[2]) / interval + local iotime = (stats[3] - last_stats[3]) / interval + + -- Calculate and store I/O + helpers.uformat(disk_usage, device.." read", read, unit) + helpers.uformat(disk_usage, device.." write", write, unit) + helpers.uformat(disk_usage, device.." total", read + write, unit) + helpers.uformat(disk_usage, device.." iotime", iotime, time_unit) + end + + disk_time = time + disk_stats = disk_lines + + return disk_usage +end) +-- }}} diff --git a/widgets/fanspeed_freebsd.lua b/widgets/fanspeed_freebsd.lua new file mode 100644 index 0000000..818a4ba --- /dev/null +++ b/widgets/fanspeed_freebsd.lua @@ -0,0 +1,55 @@ +-- fan speed widget type for FreeBSD +-- Copyright (C) 2017,2019 mutlusun +-- Copyright (C) 2019 Nguyễn Gia Phong +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local tonumber = tonumber +local type = type + +local helpers = require("vicious.helpers") +-- }}} + +-- fanspeed: provides speed level of fans +-- vicious.widgets.fanspeed +-- +-- expects one or multiple full sysctl strings to entry +-- e.g.: "dev.acpi_ibm.0.fan_speed" +local fanspeed_freebsd = {} + +-- {{{ fanspeed widget type +function fanspeed_freebsd.async(format, warg, callback) + if not warg then return callback({}) end + if type(warg) ~= "table" then warg = { warg } end + + helpers.sysctl_async(warg, function(ret) + local fanspeed = {} + + for i=1,#warg do + if ret[warg[i]] ~= nil then + fanspeed[i] = tonumber(ret[warg[i]]) + else + fanspeed[i] = "N/A" + end + end + + callback(fanspeed) + end) +end +-- }}} + +return helpers.setasyncall(fanspeed_freebsd) diff --git a/widgets/fs_all.lua b/widgets/fs_all.lua new file mode 100644 index 0000000..e1c672d --- /dev/null +++ b/widgets/fs_all.lua @@ -0,0 +1,55 @@ +-- filesystem widget type +-- Copyright (C) 2009 Lucas de Vries +-- Copyright (C) 2010 Adrian C. +-- Copyright (C) 2017 Joerg Thalheim +-- Copyright (C) 2017 mutlusun +-- Copyright (C) 2019 Nguyễn Gia Phong +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local tonumber = tonumber + +local helpers = require"vicious.helpers" +local spawn = require"vicious.spawn" +-- }}} + +-- Mebibyte and gibibyte respectively, because backward compatibility +local UNIT = { mb = 1024, gb = 1024^2 } + +-- FS: provides file system disk space usage +-- vicious.widgets.fs +return helpers.setasyncall{ + async = function(format, warg, callback) + local fs_info = {} -- Get data from df + spawn.with_line_callback_with_shell( + warg and "LC_ALL=C df -kP" or "LC_ALL=C df -klP", + { stdout = function (line) + -- (1024-blocks) (Used) (Available) (Capacity)% (Mounted on) + local s, u, a, p, m = line:match( + "^.-%s+(%d+)%s+(%d+)%s+(%d+)%s+(%d+)%%%s+([%p%w]+)") + + if u and m then -- Handle 1st line and broken regexp + helpers.uformat(fs_info, m .. " size", s, UNIT) + helpers.uformat(fs_info, m .. " used", u, UNIT) + helpers.uformat(fs_info, m .. " avail", a, UNIT) + + fs_info["{" .. m .. " used_p}"] = tonumber(p) + fs_info["{" .. m .. " avail_p}"] = 100 - tonumber(p) + end + end, + output_done = function () callback(fs_info) end }) + end } diff --git a/widgets/gmail_all.lua b/widgets/gmail_all.lua new file mode 100644 index 0000000..f885104 --- /dev/null +++ b/widgets/gmail_all.lua @@ -0,0 +1,64 @@ +-- new e-mails count and last e-mail subject on Gmail +-- Copyright (C) 2010 Adrian C. +-- Copyright (C) 2017 mutlusun +-- Copyright (C) 2017 starenka +-- Copyright (C) 2018-2019 Nguyễn Gia Phong +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local type = type +local tonumber = tonumber +local string = { match = string.match } + +local helpers = require("vicious.helpers") +local spawn = require("vicious.spawn") +-- }}} + +-- Gmail: provides count of new and subject of last e-mail on Gmail +-- vicious.widgets.gmail +local gmail_all = {} + +-- {{{ Gmail widget type +local function parse(warg, stdout, stderr, exitreason, exitcode) + local count = -- Count comes before messages and matches at least 0 + tonumber(string.match(stdout, "([%d]+)")) or 0 + + -- Find subject tag + local title = string.match(stdout, ".-(.-)") or "N/A" + + -- Check if we should scroll, or maybe truncate + if type(warg) == "number" then + title = helpers.truncate(title, warg) + elseif type(warg) == "table" then + title = helpers.scroll(title, warg[1], warg[2]) + end + + return { ["{count}"] = count, ["{subject}"] = title } +end + +function gmail_all.async(format, warg, callback) + -- Get info from the Gmail atom feed using curl --netrc. + -- With username 'user' and password 'pass' + -- $HOME/.netrc should look similar to: + -- machine mail.google.com login user password pass + -- BE AWARE THAT MAKING THESE SETTINGS IS A SECURITY RISK! + spawn.easy_async("curl -fsn https://mail.google.com/mail/feed/atom", + function (...) callback(parse(warg, ...)) end) +end +-- }}} + +return helpers.setasyncall(gmail_all) diff --git a/widgets/hddtemp_linux.lua b/widgets/hddtemp_linux.lua new file mode 100644 index 0000000..f3a11df --- /dev/null +++ b/widgets/hddtemp_linux.lua @@ -0,0 +1,42 @@ +-- hard drive temperatures widget type using hddtemp daemon +-- Copyright (C) 2010 Adrian C. +-- Copyright (C) 2017 mutlusun +-- Copyright (C) 2019 Nguyễn Gia Phong +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local tonumber = tonumber + +local helpers = require"vicious.helpers" +local spawn = require"vicious.spawn" +-- }}} + +-- Hddtemp: provides hard drive temperatures using the hddtemp daemon +-- vicious.widgets.hddtemp +return helpers.setasyncall{ + async = function(format, warg, callback) + if warg == nil then warg = 7634 end -- fallback to default hddtemp port + local hdd_temp = {} -- get info from the hddtemp daemon + spawn.with_line_callback_with_shell( + "echo | curl -fs telnet://127.0.0.1:" .. warg, + { stdout = function (line) + for d, t in line:gmatch"|([%/%w]+)|.-|(%d+)|[CF]|" do + hdd_temp["{"..d.."}"] = tonumber(t) + end + end, + output_done = function () callback(hdd_temp) end }) + end } diff --git a/widgets/hwmontemp_linux.lua b/widgets/hwmontemp_linux.lua new file mode 100644 index 0000000..fe3e50d --- /dev/null +++ b/widgets/hwmontemp_linux.lua @@ -0,0 +1,67 @@ +-- widget type providing name-indexed temperatures from /sys/class/hwmon +-- Copyright (C) 2019, 2020 Alexander Koch +-- Copyright (C) 2019 Nguyễn Gia Phong +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- environment +local type = type +local tonumber = tonumber +local io = { open = io.open } + +local helpers = require"vicious.helpers" +local spawn = require"vicious.spawn" + +local pathcache = {} + +local function read_sensor(path, callback) + local f = io.open(path, "r") + callback{ tonumber(f:read"*line") / 1000 } + f:close() +end + +-- hwmontemp: provides name-indexed temps from /sys/class/hwmon +-- vicious.widgets.hwmontemp +return helpers.setasyncall{ + async = function (format, warg, callback) + if type(warg) ~= "table" or type(warg[1]) ~= "string" then + return callback{} + end + + local input = warg[2] + if type(input) == "number" then + input = ("temp%d_input"):format(input) + else + input = "temp1_input" + end + + local sensor = warg[1] + if pathcache[sensor] then + read_sensor(pathcache[sensor] .. input, callback) + else + spawn.easy_async_with_shell( + "grep " .. sensor .. " -wl /sys/class/hwmon/*/name", + function (stdout, stderr, exitreason, exitcode) + if exitreason == "exit" and exitcode == 0 then + pathcache[sensor] = stdout:gsub("name%s+", "") + read_sensor(pathcache[sensor] .. input, callback) + else + callback{} + end + end) + end + end } +-- vim: ts=4:sw=4:expandtab diff --git a/widgets/init.lua b/widgets/init.lua new file mode 100644 index 0000000..61b8590 --- /dev/null +++ b/widgets/init.lua @@ -0,0 +1,24 @@ +-- widget types initialization +-- Copyright (C) 2010 Adrian C. (anrxc) +-- Copyright (C) 2011,2012 Jörg Thalheim +-- Copyright (C) 2012 Arvydas Sidorenko +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +local setmetatable = setmetatable +local wrequire = require("vicious.helpers").wrequire + +return setmetatable({ _NAME = "vicious.widgets" }, { __index = wrequire }) diff --git a/widgets/mbox_all.lua b/widgets/mbox_all.lua new file mode 100644 index 0000000..d65fac8 --- /dev/null +++ b/widgets/mbox_all.lua @@ -0,0 +1,55 @@ +-- widget type providing the subject of last e-mail in a mbox file +-- Copyright (C) 2010 Adrian C. +-- Copyright (C) 2017 mutlusun +-- Copyright (C) 2018 Nguyễn Gia Phong +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local type = type +local io = { open = io.open } +local helpers = require("vicious.helpers") +-- }}} + +-- Initialize variables +local subject = "N/A" + +-- {{{ Mailbox widget type +return helpers.setcall(function (format, warg) + if not warg then return end + + local f = io.open(type(warg) == "table" and warg[1] or warg) + f:seek("end", -30720) + -- mbox could be huge, get a 30kb chunk from EOF + -- * attachment could be much bigger than 30kb + local txt = f:read("*all") + f:close() + + -- Find all Subject lines + for i in txt:gmatch"Subject: ([^\n]*)" do subject = i end + + -- Check if we should scroll, or maybe truncate + if type(warg) == "table" then + if warg[3] ~= nil then + subject = helpers.scroll(subject, warg[2], warg[3]) + else + subject = helpers.truncate(subject, warg[2]) + end + end + + return { subject } +end) +-- }}} diff --git a/widgets/mboxc_all.lua b/widgets/mboxc_all.lua new file mode 100644 index 0000000..ab33a0d --- /dev/null +++ b/widgets/mboxc_all.lua @@ -0,0 +1,63 @@ +-- widget type providing the count of total, old and new messages in mbox files +-- Copyright (C) 2010 Adrian C. +-- Copyright (C) 2017 mutlusun +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local io = { open = io.open } +local helpers = require"vicious.helpers" +-- }}} + +-- {{{ Mbox count widget type +return helpers.setcall(function (format, warg) + if not warg then return end + + -- Initialize counters + local count = { old = 0, total = 0, new = 0 } + + -- Get data from mbox files + for i=1, #warg do + local f = io.open(warg[i]) + + while true do + -- Read the mbox line by line, if we are going to read + -- some *HUGE* folders then switch to reading chunks + local lines = f:read("*line") + if not lines then break end + + -- Find all messages + -- * http://www.jwz.org/doc/content-length.html + local _, from = lines:find"^From[%s]" + if from ~= nil then count.total = count.total + 1 end + + -- Read messages have the Status header + local _, status = lines:find"^Status:[%s]RO$" + if status ~= nil then count.old = count.old + 1 end + + -- Skip the folder internal data + local _, int = lines:find"^Subject:[%s].*FOLDER[%s]INTERNAL[%s]DATA" + if int ~= nil then count.total = count.total - 1 end + end + f:close() + end + + -- Substract total from old to get the new count + count.new = count.total - count.old + + return {count.total, count.old, count.new} +end) +-- }}} diff --git a/widgets/mdir_all.lua b/widgets/mdir_all.lua new file mode 100644 index 0000000..a57cf16 --- /dev/null +++ b/widgets/mdir_all.lua @@ -0,0 +1,54 @@ +-- widget type providing number of new and unread Maildir messages +-- Copyright (C) 2010 Adrian C. +-- Copyright (C) 2010 Fredrik Ax +-- Copyright (C) 2017 mutlusun +-- Copyright (C) 2019 Nguyễn Gia Phong +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local type = type + +local helpers = require"vicious.helpers" +local spawn = require"vicious.spawn" +-- }}} + +-- vicious.widgets.mdir +local mdir_all = {} + +-- {{{ Maildir widget type +function mdir_all.async(format, warg, callback) + if type(warg) ~= "table" then return callback{} end + local starting_points = "" + for _,dir in ipairs(warg) do + starting_points = starting_points .. " " .. helpers.shellquote(dir) + end + if starting_points == "" then return callback{ 0, 0 } end + + local new, cur = 0, 0 + spawn.with_line_callback( + "find" .. starting_points .. " -type f -regex '.*/cur/.*2,[^S]*$'", + { stdout = function (filename) cur = cur + 1 end, + output_done = function () + spawn.with_line_callback( + "find" .. starting_points .. " -type f -path '*/new/*'", + { stdout = function (filename) new = new + 1 end, + output_done = function () callback{ new, cur } end }) + end }) +end +-- }}} + +return helpers.setasyncall(mdir_all) diff --git a/widgets/mem_freebsd.lua b/widgets/mem_freebsd.lua new file mode 100644 index 0000000..805e9a0 --- /dev/null +++ b/widgets/mem_freebsd.lua @@ -0,0 +1,139 @@ +-- RAM and swap usage widget type for FreeBSD +-- Copyright (C) 2017-2019 mutlusun +-- Copyright (C) 2018 Andreas Geisenhainer +-- Copyright (C) 2019 Nguyễn Gia Phong +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local tonumber = tonumber +local math = { floor = math.floor } +local string = { + match = string.match, + gmatch = string.gmatch, + find = string.find +} + +local helpers = require("vicious.helpers") +local spawn = require("vicious.spawn") +-- }}} + +-- Mem: provides RAM and Swap usage statistics +-- vicious.widgets.mem_freebsd +local mem_freebsd = {} + + +-- {{{ Memory widget type +function mem_freebsd.async(format, warg, callback) + helpers.sysctl_async({ "hw.pagesize", + "vm.stats.vm", + "vm.swap_total", + "vm.swap_enabled" }, + function(ret) + + local pgsz = tonumber(ret["hw.pagesize"]) + local _mem = { buf = {}, total = nil } + + -- Get memory space in bytes + _mem.total = tonumber(ret["vm.stats.vm.v_page_count"]) * pgsz + _mem.buf.free = tonumber(ret["vm.stats.vm.v_free_count"]) * pgsz + _mem.buf.laundry = tonumber(ret["vm.stats.vm.v_laundry_count"]) * pgsz + _mem.buf.cache = tonumber(ret["vm.stats.vm.v_cache_count"]) * pgsz + _mem.buf.wired = tonumber(ret["vm.stats.vm.v_wire_count"]) * pgsz + + -- Rework into megabytes + _mem.total = math.floor(_mem.total/1048576) + _mem.buf.free = math.floor(_mem.buf.free/1048576) + _mem.buf.laundry = math.floor(_mem.buf.laundry/1048576) + _mem.buf.cache = math.floor(_mem.buf.cache/1048576) + _mem.buf.wired = math.floor(_mem.buf.wired/1048576) + + -- Calculate memory percentage + _mem.free = _mem.buf.free + _mem.buf.cache + -- used memory basically consists of active+inactive+wired + _mem.inuse = _mem.total - _mem.free + _mem.notfreeable = _mem.inuse - _mem.buf.laundry + _mem.wire = _mem.buf.wired + + _mem.usep = math.floor(_mem.inuse / _mem.total * 100) + _mem.wirep = math.floor(_mem.wire / _mem.total * 100) + _mem.notfreeablep = math.floor(_mem.notfreeable / _mem.total * 100) + + -- Get swap states + local vm_swap_total = tonumber(ret["vm.swap_total"]) + local vm_swap_enabled = tonumber(ret["vm.swap_enabled"]) + local _swp = { buf = {}, total = nil } + + if vm_swap_enabled == 1 and vm_swap_total > 0 then + -- Initialise variables + _swp.usep = 0 + _swp.inuse = 0 + _swp.total = 0 + _swp.buf.free = 0 + + -- Read output of swapinfo in Mbytes (from async function call) + -- Read content and sum up + spawn.with_line_callback("swapinfo -m", { + stdout = function(line) + if not string.find(line, "Device") then + local ltotal, lused, lfree = string.match( + line, "%s+([%d]+)%s+([%d]+)%s+([%d]+)") + -- Add swap space in Mbytes + _swp.total = _swp.total + tonumber(ltotal) + _swp.inuse = _swp.inuse + tonumber(lused) + _swp.buf.free = _swp.buf.free + tonumber(lfree) + end + end, + output_done = function() + print(_swp.inuse, _swp.total) + _swp.usep = math.floor(_swp.inuse / _swp.total * 100) + callback({ _mem.usep, + _mem.inuse, + _mem.total, + _mem.free, + _swp.usep, + _swp.inuse, + _swp.total, + _swp.buf.free, + _mem.wirep, + _mem.wire, + _mem.notfreeablep, + _mem.notfreeable }) + end + }) + else + _swp.usep = -1 + _swp.inuse = -1 + _swp.total = -1 + _swp.buf.free = -1 + callback({ _mem.usep, + _mem.inuse, + _mem.total, + _mem.free, + _swp.usep, + _swp.inuse, + _swp.total, + _swp.buf.free, + _mem.wirep, + _mem.wire, + _mem.notfreeablep, + _mem.notfreeable }) + end + end) +end +-- }}} + +return helpers.setasyncall(mem_freebsd) diff --git a/widgets/mem_linux.lua b/widgets/mem_linux.lua new file mode 100644 index 0000000..318b51f --- /dev/null +++ b/widgets/mem_linux.lua @@ -0,0 +1,60 @@ +-- RAM and swap usage widget type for GNU/Linux +-- Copyright (C) 2010 Adrian C. +-- Copyright (C) 2009 Lucas de Vries +-- Copyright (C) 2017 mutlusun +-- Copyright (C) 2018 Jay Kamat +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local io = { lines = io.lines } +local math = { floor = math.floor } +local string = { gmatch = string.gmatch } +local helpers = require"vicious.helpers" +-- }}} + +-- {{{ Memory widget type +return helpers.setcall(function () + local _mem = { buf = {}, swp = {} } + + -- Get MEM info + for line in io.lines("/proc/meminfo") do + for k, v in string.gmatch(line, "([%a]+):[%s]+([%d]+).+") do + if k == "MemTotal" then _mem.total = math.floor(v/1024) + elseif k == "MemFree" then _mem.buf.f = math.floor(v/1024) + elseif k == "MemAvailable" then _mem.buf.a = math.floor(v/1024) + elseif k == "Buffers" then _mem.buf.b = math.floor(v/1024) + elseif k == "Cached" then _mem.buf.c = math.floor(v/1024) + elseif k == "SwapTotal" then _mem.swp.t = math.floor(v/1024) + elseif k == "SwapFree" then _mem.swp.f = math.floor(v/1024) + end + end + end + + -- Calculate memory percentage + _mem.free = _mem.buf.a + _mem.inuse = _mem.total - _mem.free + _mem.bcuse = _mem.total - _mem.buf.f + _mem.usep = math.floor(_mem.inuse / _mem.total * 100) + -- Calculate swap percentage + _mem.swp.inuse = _mem.swp.t - _mem.swp.f + _mem.swp.usep = math.floor(_mem.swp.inuse / _mem.swp.t * 100) + + return {_mem.usep, _mem.inuse, _mem.total, _mem.free, + _mem.swp.usep, _mem.swp.inuse, _mem.swp.t, _mem.swp.f, + _mem.bcuse } +end) +-- }}} diff --git a/widgets/mpd_all.lua b/widgets/mpd_all.lua new file mode 100644 index 0000000..6ca43d1 --- /dev/null +++ b/widgets/mpd_all.lua @@ -0,0 +1,135 @@ +-- Music Player Daemon widget type +-- Copyright (C) 2010 Adrian C. +-- Copyright (C) 2017,2019 mutlusun +-- Copyright (C) 2018 Jörg Thalheim +-- Copyright (C) 2018-2019 Nguyễn Gia Phong +-- Copyright (C) 2019 Juan Carlos Menonita +-- Copyright (C) 2019 Lorenzo Gaggini +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local tonumber = tonumber +local math = { floor = math.floor } +local type = type + +local helpers = require"vicious.helpers" +local spawn = require"vicious.spawn" +-- }}} + +-- Mpd: provides Music Player Daemon information +-- vicious.widgets.mpd +local mpd_all = {} + + +-- {{{ Return true if number is nonzero +local function cbool(number) + return type(number) == "number" and number ~= 0 or number +end +-- }}} + +-- {{{ Format playing progress +local function format_progress(elapsed, duration) + local em, es = math.floor(elapsed / 60), math.floor(elapsed % 60) + local dm, ds = math.floor(duration / 60), math.floor(duration % 60) + + if dm < 10 then + return ("%d:%02d"):format(em, es), ("%d:%02d"):format(dm, ds) + elseif dm < 60 then + return ("%02d:%02d"):format(em, es), ("%02d:%02d"):format(dm, ds) + end + + local eh, dh = math.floor(em / 60), math.floor(dm / 60) + em, dm = math.floor(em % 60), math.floor(dm % 60) + if dm < 600 then + return ("%d:%02d:%02d"):format(eh, em, es), + ("%d:%02d:%02d"):format(dh, dm, ds) + else + return ("%02d:%02d:%02d"):format(eh, em, es), + ("%02d:%02d:%02d"):format(dh, dm, ds) + end +end +-- }}} + +-- {{{ Format playing progress (percentage) +local function format_progress_percentage(elapsed, duration) + if duration > 0 then + local percentage = math.floor((elapsed / duration) * 100 + 0.5) + return ("%d%%"):format(percentage) + else + return("0%") + end +end +-- }}} + +-- {{{ MPD widget type +function mpd_all.async(format, warg, callback) + -- Fallback values + local mpd_state = { + ["{volume}"] = 0, + ["{bitrate}"] = 0, + ["{elapsed}"] = 0, + ["{duration}"] = 0, + ["{repeat}"] = false, + ["{random}"] = false, + ["{state}"] = "N/A", + ["{Artist}"] = "N/A", + ["{Title}"] = "N/A", + ["{Album}"] = "N/A", + ["{Genre}"] = "N/A", + --["{Name}"] = "N/A", + --["{file}"] = "N/A", + } + + -- Construct MPD client options, fallback to defaults when necessary + local query = ("printf 'password %s\nstatus\ncurrentsong\nclose\n'"):format( + warg and (warg.password or warg[1]) or '""') + local connect = ("curl --connect-timeout 1 -fsm 3 telnet://%s:%s"):format( + warg and (warg.host or warg[2]) or "127.0.0.1", + warg and (warg.port or warg[3]) or "6600") + + -- Get data from MPD server + spawn.with_line_callback_with_shell(query .. "|" .. connect, { + stdout = function (line) + for k, v in line:gmatch"([%w]+):[%s](.*)$" do + local key = "{" .. k .. "}" + if k == "volume" or k == "bitrate" or + k == "elapsed" or k == "duration" then + mpd_state[key] = v and tonumber(v) + elseif k == "repeat" or k == "random" then + mpd_state[key] = cbool(v) + elseif k == "state" then + mpd_state[key] = helpers.capitalize(v) + elseif k == "Artist" or k == "Title" or + --k == "Name" or k == "file" or + k == "Album" or k == "Genre" then + mpd_state[key] = v + end + end + end, + output_done = function () + -- Formatted elapsed and duration + mpd_state["{Elapsed}"], mpd_state["{Duration}"] = format_progress( + mpd_state["{elapsed}"], mpd_state["{duration}"]) + -- Formatted playing progress percentage + mpd_state["{Progress}"] = format_progress_percentage( + mpd_state["{elapsed}"], mpd_state["{duration}"]) + callback(mpd_state) + end }) +end +-- }}} + +return helpers.setasyncall(mpd_all) diff --git a/widgets/net_freebsd.lua b/widgets/net_freebsd.lua new file mode 100644 index 0000000..42dbf57 --- /dev/null +++ b/widgets/net_freebsd.lua @@ -0,0 +1,108 @@ +-- network status and usage widget type for FreeBSD +-- Copyright (C) 2017,2019 mutlusun +-- Copyright (C) 2019 Nguyễn Gia Phong +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local tonumber = tonumber +local os = { time = os.time } +local string = { + match = string.match, + gmatch = string.gmatch +} + +local helpers = require("vicious.helpers") +local spawn = require("vicious.spawn") +-- }}} + + +-- Net: provides state and usage statistics of all network interfaces +-- vicious.widgets.net +local net_freebsd = {} + + +-- Initialize function tables +local nets = {} +-- Variable definitions +local unit = { ["b"] = 1, ["kb"] = 1024, + ["mb"] = 1024^2, ["gb"] = 1024^3 +} + +-- {{{ Net widget type +local function parse(stdout, stderr, exitreason, exitcode) + + local args = {} + local buffer = nil + local now = os.time() + + for line in string.gmatch(stdout, "[^\n]+") do + if not (line:find(" +-- Copyright (C) 2010 Adrian C. +-- Copyright (C) 2017 mutlusun +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local tonumber = tonumber +local os = { time = os.time } +local io = { lines = io.lines } +local string = { match = string.match } +local helpers = require("vicious.helpers") +-- }}} + +-- Initialize function tables +local nets = {} +-- Variable definitions +local unit = { ["b"] = 1, ["kb"] = 1024, + ["mb"] = 1024^2, ["gb"] = 1024^3 +} + +-- {{{ Net widget type +return helpers.setcall(function () + local args = {} + + -- Get NET stats + for line in io.lines("/proc/net/dev") do + -- Match wmaster0 as well as rt0 (multiple leading spaces) + local name = string.match(line, "^[%s]?[%s]?[%s]?[%s]?([%w]+):") + if name ~= nil then + -- Received bytes, first value after the name + local recv = tonumber(string.match(line, ":[%s]*([%d]+)")) + -- Transmited bytes, 7 fields from end of the line + local send = tonumber(string.match(line, + "([%d]+)%s+%d+%s+%d+%s+%d+%s+%d+%s+%d+%s+%d+%s+%d$")) + + helpers.uformat(args, name .. " rx", recv, unit) + helpers.uformat(args, name .. " tx", send, unit) + + -- Operational state and carrier detection + local sysnet = helpers.pathtotable("/sys/class/net/" .. name) + args["{"..name.." carrier}"] = tonumber(sysnet.carrier) or 0 + + local now = os.time() + if nets[name] == nil then + -- Default values on the first run + nets[name] = {} + helpers.uformat(args, name .. " down", 0, unit) + helpers.uformat(args, name .. " up", 0, unit) + else -- Net stats are absolute, substract our last reading + local interval = now - nets[name].time + if interval <= 0 then interval = 1 end + + local down = (recv - nets[name][1]) / interval + local up = (send - nets[name][2]) / interval + + helpers.uformat(args, name .. " down", down, unit) + helpers.uformat(args, name .. " up", up, unit) + end + + nets[name].time = now + + -- Store totals + nets[name][1] = recv + nets[name][2] = send + end + end + + return args +end) +-- }}} diff --git a/widgets/notmuch_all.lua b/widgets/notmuch_all.lua new file mode 100644 index 0000000..47f5295 --- /dev/null +++ b/widgets/notmuch_all.lua @@ -0,0 +1,38 @@ +-- notmuch_all - count messages that match a notmuch query +-- Copyright (C) 2019 Enric Morales +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Affero General Public License for more details. +-- +-- You should have received a copy of the GNU Affero General Public License +-- along with Vicious. If not, see . + +local tonumber = tonumber +local helpers = require("vicious.helpers") +local spawn = require("vicious.spawn") + + +local notmuch = {} + +local function parse(stdout, stderr, exitreason, exitcode) + local output = { count = "N/A" } + if exitcode == 0 then output.count = tonumber(stdout) end + return output +end + +function notmuch.async(format, warg, callback) + local cmd = ("notmuch count '^%s'"):format(warg) + + spawn.easy_async(cmd, function (...) callback(parse(...)) end) +end + +return helpers.setasyncall(notmuch) diff --git a/widgets/org_all.lua b/widgets/org_all.lua new file mode 100644 index 0000000..b15fd16 --- /dev/null +++ b/widgets/org_all.lua @@ -0,0 +1,65 @@ +-- widget type providing agenda from Emacs org-mode +-- Copyright (C) 2010 Adrian C. +-- Copyright (C) 2010 org-awesome, Damien Leone +-- Copyright (C) 2017 mutlusun +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local io = { lines = io.lines } +local os = { time = os.time, date = os.date } +local helpers = require"vicious.helpers" +-- }}} + +-- {{{ OrgMode widget type +return helpers.setcall(function (format, warg) + if not warg then return end + + -- Compute delays + local today = os.time{ year = os.date("%Y"), month = os.date("%m"), + day = os.date("%d") } + local soon = today + 24*3600*3 -- 3 days ahead is close + local future = today + 24*3600*7 -- 7 days ahead is maximum + + -- Initialize counters + local count = { past = 0, today = 0, soon = 0, future = 0 } + + -- Get data from agenda files + for i = 1,#warg do + for line in io.lines(warg[i]) do + local scheduled = line:find"SCHEDULED:" + local deadline = line:find"DEADLINE:" + local closed = line:find"CLOSED:" + local b, _, y, m, d = line:find"(%d%d%d%d)-(%d%d)-(%d%d)" + + if (scheduled or deadline) and not closed and b then + local t = os.time{ year = y, month = m, day = d } + if t < today then + count.past = count.past + 1 + elseif t == today then + count.today = count.today + 1 + elseif t <= soon then + count.soon = count.soon + 1 + elseif t <= future then + count.future = count.future + 1 + end + end + end + end + + return { count.past, count.today, count.soon, count.future } +end) +-- }}} diff --git a/widgets/os_bsd.lua b/widgets/os_bsd.lua new file mode 100644 index 0000000..0ec0bac --- /dev/null +++ b/widgets/os_bsd.lua @@ -0,0 +1,58 @@ +-- operating system widget type for *BSD +-- Copyright (C) 2019 mutlusun +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local los = { getenv = os.getenv } +local string = { match = string.match } +local helpers = require("vicious.helpers") +local spawn = require("vicious.spawn") +-- }}} + +-- OS: provides operating system information +-- vicious.widgets.os +local os_bsd = {} + +-- {{{ Operating system widget type +local function parse(stdout, stderr, exitreason, exitcode) + local system = { + ["ostype"] = "N/A", + ["hostname"] = "N/A", + ["osrelease"] = "N/A", + ["username"] = "N/A", + ["entropy"] = "N/A", + ["entropy_p"] = "N/A" + } + + -- BSD manual page: uname(1) + system["ostype"], system["hostname"], system["osrelease"] = + string.match(stdout, "([%w]+)[%s]([%w%p]+)[%s]([%w%p]+)") + + -- Get user from the environment + system["username"] = los.getenv("USER") + + return {system["ostype"], system["osrelease"], system["username"], + system["hostname"], system["entropy"], system["entropy_p"]} +end + +function os_bsd.async(format, warg, callback) + spawn.easy_async("uname -snr", + function (...) callback(parse(...)) end) +end +-- }}} + +return helpers.setasyncall(os_bsd) diff --git a/widgets/os_linux.lua b/widgets/os_linux.lua new file mode 100644 index 0000000..a1528e5 --- /dev/null +++ b/widgets/os_linux.lua @@ -0,0 +1,66 @@ +-- operating system widget type for GNU/Linux +-- Copyright (C) 2010 Adrian C. +-- Copyright (C) 2019 Nguyễn Gia Phong +-- Copyright (C) 2019 mutlusun +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local pairs = pairs +local tonumber = tonumber +local math = { ceil = math.ceil } +local los = { getenv = os.getenv } +local string = { gsub = string.gsub } + +local helpers = require"vicious.helpers" +-- }}} + +-- {{{ Operating system widget type +return helpers.setcall(function () + local system = { + ["ostype"] = "N/A", + ["hostname"] = "N/A", + ["osrelease"] = "N/A", + ["username"] = "N/A", + ["entropy"] = "N/A", + ["entropy_p"] = "N/A" + } + + -- Linux manual page: uname(2) + local kernel = helpers.pathtotable("/proc/sys/kernel") + for k, _ in pairs(system) do + if kernel[k] then + system[k] = string.gsub(kernel[k], "[%s]*$", "") + end + end + + -- Linux manual page: random(4) + if kernel.random then + -- Linux 2.6 default entropy pool is 4096-bits + local poolsize = tonumber(kernel.random.poolsize) + + -- Get available entropy and calculate percentage + system["entropy"] = tonumber(kernel.random.entropy_avail) + system["entropy_p"] = math.ceil(system["entropy"] * 100 / poolsize) + end + + -- Get user from the environment + system["username"] = los.getenv("USER") + + return {system["ostype"], system["osrelease"], system["username"], + system["hostname"], system["entropy"], system["entropy_p"]} +end) +-- }}} diff --git a/widgets/pkg_all.lua b/widgets/pkg_all.lua new file mode 100644 index 0000000..609aaf8 --- /dev/null +++ b/widgets/pkg_all.lua @@ -0,0 +1,62 @@ +-- widget type providing number of pending updates +-- Copyright (C) 2010 Adrian C. +-- Copyright (C) 2017 Joerg Thalheim +-- Copyright (C) 2017 getzze +-- Copyright (C) 2017 mutlusun +-- Copyright (C) 2019 Nguyễn Gia Phong +-- Copyright (C) 2020 Elmeri Niemelä +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local spawn = require("vicious.spawn") +local helpers = require("vicious.helpers") +-- }}} + +-- Pkg: provides number of pending updates on UNIX systems +-- vicious.widgets.pkg +local pkg_all = {} + +local PKGMGR = { + ["Arch"] = { cmd = "pacman -Qu", sub = 0 }, + ["Arch C"] = { cmd = "checkupdates", sub = 0 }, + ["Arch S"] = { cmd = "yes | pacman -Sup", sub = 1 }, + ["Debian"] = { cmd = "apt list --upgradable", sub = 1 }, + ["Ubuntu"] = { cmd = "apt list --upgradable", sub = 1 }, + ["Fedora"] = { cmd = "dnf check-update", sub = 2 }, + ["FreeBSD"] = { cmd = "pkg version -I -l '<'", sub = 0 }, + ["Mandriva"] = { cmd = "urpmq --auto-select", sub = 0 } +} + +-- {{{ Packages widget type +function pkg_all.async(format, warg, callback) + if not warg then return callback{} end + local pkgmgr = PKGMGR[warg] + + local size, lines = -pkgmgr.sub, "" + spawn.with_line_callback_with_shell(pkgmgr.cmd, { + stdout = function (str) + size = size + 1 + if size > 0 then lines = lines .. str .. "\n" end + end, + output_done = function () + callback{ size, lines } + end + }) +end +-- }}} + +return helpers.setasyncall(pkg_all) diff --git a/widgets/raid_linux.lua b/widgets/raid_linux.lua new file mode 100644 index 0000000..7b4d23f --- /dev/null +++ b/widgets/raid_linux.lua @@ -0,0 +1,67 @@ +-- widget type providing RAID array information on GNU/Linux +-- Copyright (C) 2010 Hagen Schink +-- Copyright (C) 2017 mutlusun +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local io = { open = io.open } +local string = { + len = string.len, + sub = string.sub, + match = string.match, + gmatch = string.gmatch +} + +local helpers = require"vicious.helpers" +-- }}} + +-- Initialize function tables +local mddev = {} + +-- {{{ RAID widget type +return helpers.setcall(function (format, warg) + if not warg then return end + mddev[warg] = { + ["found"] = false, + ["active"] = 0, + ["assigned"] = 0 + } + + -- Linux manual page: md(4) + local f = io.open("/proc/mdstat") + for line in f:lines() do + if mddev[warg]["found"] then + local updev = string.match(line, "%[[_U]+%]") + + for _ in string.gmatch(updev, "U") do + mddev[warg]["active"] = mddev[warg]["active"] + 1 + end + + break + elseif string.sub(line, 1, string.len(warg)) == warg then + mddev[warg]["found"] = true + + for _ in string.gmatch(line, "%[[%d]%]") do + mddev[warg]["assigned"] = mddev[warg]["assigned"] + 1 + end + end + end + f:close() + + return {mddev[warg]["assigned"], mddev[warg]["active"]} +end) +-- }}} diff --git a/widgets/thermal_freebsd.lua b/widgets/thermal_freebsd.lua new file mode 100644 index 0000000..1bb346d --- /dev/null +++ b/widgets/thermal_freebsd.lua @@ -0,0 +1,52 @@ +-- temperature widget type for FreeBSD +-- Copyright (C) 2017,2019 mutlusun +-- Copyright (C) 2019 Nguyễn Gia Phong +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local string = { match = string.match } +local type = type + +local helpers = require("vicious.helpers") +-- }}} + +-- Thermal: provides temperature levels of ACPI and coretemp thermal zones +-- vicious.widgets.thermal +local thermal_freebsd = {} + +-- {{{ Thermal widget type +function thermal_freebsd.async(format, warg, callback) + if not warg then return callback{} end + if type(warg) ~= "table" then warg = { warg } end + + helpers.sysctl_async(warg, function(ret) + local thermals = {} + + for i=1,#warg do + if ret[warg[i]] ~= nil then + thermals[i] = string.match(ret[warg[i]], "[%d]+") + else + thermals[i] = "N/A" + end + end + + callback(thermals) + end) +end +-- }}} + +return helpers.setasyncall(thermal_freebsd) diff --git a/widgets/thermal_linux.lua b/widgets/thermal_linux.lua new file mode 100644 index 0000000..7ac8e69 --- /dev/null +++ b/widgets/thermal_linux.lua @@ -0,0 +1,54 @@ +-- temperature widget type for GNU/Linux +-- Copyright (C) 2010 Adrian C. +-- Copyright (C) 2017 mutlusun +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local type = type +local tonumber = tonumber +local string = { match = string.match } +local math = { floor = math.floor } +local helpers = require("vicious.helpers") +-- }}} + +-- {{{ Thermal widget type +return helpers.setcall(function (format, warg) + if not warg then return end + + local zone = { -- Known temperature data sources + ["sys"] = {"/sys/class/thermal/", file = "temp", div = 1000}, + ["core"] = {"/sys/devices/platform/", file = "temp2_input",div = 1000}, + ["hwmon"] = {"/sys/class/hwmon/", file = "temp1_input",div = 1000}, + ["proc"] = {"/proc/acpi/thermal_zone/",file = "temperature"} + } -- Default to /sys/class/thermal + warg = type(warg) == "table" and warg or { warg, "sys" } + + -- Get temperature from thermal zone + local _thermal = helpers.pathtotable(zone[warg[2]][1] .. warg[1]) + + local data = warg[3] and _thermal[warg[3]] or _thermal[zone[warg[2]].file] + if data then + if zone[warg[2]].div then + return {math.floor(data / zone[warg[2]].div)} + else -- /proc/acpi "temperature: N C" + return {tonumber(string.match(data, "[%d]+"))} + end + end + + return {0} +end) +-- }}} diff --git a/widgets/uptime_freebsd.lua b/widgets/uptime_freebsd.lua new file mode 100644 index 0000000..42b816d --- /dev/null +++ b/widgets/uptime_freebsd.lua @@ -0,0 +1,52 @@ +-- uptime widget type for FreeBSD +-- Copyright (C) 2017,2019 mutlusun +-- Copyright (C) 2019 Nguyễn Gia Phong +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local tonumber = tonumber +local math = { floor = math.floor } +local os = { time = os.time } + +local helpers = require("vicious.helpers") +-- }}} + +-- Uptime: provides system uptime and load information +-- vicious.widgets.uptime +local uptime_freebsd = {} + +-- {{{ Uptime widget type +function uptime_freebsd.async(format, warg, callback) + helpers.sysctl_async( + { "vm.loadavg", "kern.boottime" }, + function(ret) + local l1, l5, l15 = ret["vm.loadavg"]:match( + "{ ([%d]+%.[%d]+) ([%d]+%.[%d]+) ([%d]+%.[%d]+) }") + local up_t = os.time() - tonumber( + ret["kern.boottime"]:match"sec = ([%d]+)") + + -- Get system uptime + local up_d = math.floor(up_t / (3600 * 24)) + local up_h = math.floor((up_t % (3600 * 24)) / 3600) + local up_m = math.floor(((up_t % (3600 * 24)) % 3600) / 60) + + return callback({ up_d, up_h, up_m, l1, l5, l15 }) + end) +end +-- }}} + +return helpers.setasyncall(uptime_freebsd) diff --git a/widgets/uptime_linux.lua b/widgets/uptime_linux.lua new file mode 100644 index 0000000..7ff3580 --- /dev/null +++ b/widgets/uptime_linux.lua @@ -0,0 +1,41 @@ +-- uptime widget type for GNU/Linux +-- Copyright (C) 2009 Lucas de Vries +-- Copyright (C) 2010 Adrian C. +-- Copyright (C) 2017 mutlusun +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local math = { floor = math.floor } +local string = { match = string.match } +local helpers = require("vicious.helpers") +-- }}} + +-- {{{ Uptime widget type +return helpers.setcall(function () + local proc = helpers.pathtotable("/proc") + + -- Get system uptime + local up_t = math.floor(string.match(proc.uptime, "[%d]+")) + local up_d = math.floor(up_t / (3600 * 24)) + local up_h = math.floor((up_t % (3600 * 24)) / 3600) + local up_m = math.floor(((up_t % (3600 * 24)) % 3600) / 60) + + local l1, l5, l15 = -- Get load averages for past 1, 5 and 15 minutes + string.match(proc.loadavg, "([%d%.]+)[%s]([%d%.]+)[%s]([%d%.]+)") + return {up_d, up_h, up_m, l1, l5, l15} +end) +-- }}} diff --git a/widgets/volume_freebsd.lua b/widgets/volume_freebsd.lua new file mode 100644 index 0000000..e370917 --- /dev/null +++ b/widgets/volume_freebsd.lua @@ -0,0 +1,48 @@ +-- volume widget type for FreeBSD +-- Copyright (C) 2017,2019 mutlusun +-- Copyright (C) 2019 Nguyễn Gia Phong +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local tonumber = tonumber +local string = { match = string.match } +local helpers = require("vicious.helpers") +local spawn = require("vicious.spawn") +-- }}} + +-- Volume: provides volume levels and state of requested mixer +-- vicious.widgets.volume_freebsd +local volume_freebsd = {} + +-- {{{ Volume widget type +local STATE = { on = '🔉', off = '🔈' } + +local function parse(stdout, stderr, exitreason, exitcode) + -- Capture mixer control state, e.g. 42 : 42 + local voll, volr = string.match(stdout, "([%d]+):([%d]+)\n$") + if voll == "0" and volr == "0" then return { 0, 0, STATE.off } end + return { tonumber(voll), tonumber(volr), STATE.on } +end + +function volume_freebsd.async(format, warg, callback) + if not warg then return callback{} end + spawn.easy_async("mixer " .. helpers.shellquote(warg), + function (...) callback(parse(...)) end) +end +-- }}} + +return helpers.setasyncall(volume_freebsd) diff --git a/widgets/volume_linux.lua b/widgets/volume_linux.lua new file mode 100644 index 0000000..3d26fa3 --- /dev/null +++ b/widgets/volume_linux.lua @@ -0,0 +1,61 @@ +-- volume widget type for GNU/Linux +-- Copyright (C) 2010 Adrian C. +-- Copyright (C) 2017 Brandon Hartshorn +-- Copyright (C) 2017 mutlusun +-- Copyright (C) 2019 Nguyễn Gia Phong +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local type = type +local tonumber = tonumber +local string = { match = string.match } +local table = { concat = table.concat } + +local helpers = require("vicious.helpers") +local spawn = require("vicious.spawn") +-- }}} + +-- Volume: provides volume levels and state of requested ALSA mixers +-- vicious.widgets.volume +local volume_linux = {} + +-- {{{ Volume widget type +local STATE = { on = '🔉', off = '🔈' } + +local function parse(stdout, stderr, exitreason, exitcode) + -- Capture mixer control state, e.g. [ 42 % ] [ on ] + local volume, state = string.match(stdout, "%[([%d]+)%%%].*%[([%l]*)%]") + -- Handle mixers without data + if volume == nil then return {} end + + if state == "" and volume == "0" -- handle mixers without mute + or state == "off" then -- handle muted mixers + return { tonumber(volume), STATE.off } + else + return { tonumber(volume), STATE.on } + end +end + +function volume_linux.async(format, warg, callback) + if not warg then return callback{} end + if type(warg) ~= "table" then warg = { warg } end + spawn.easy_async("amixer -M get " .. table.concat(warg, " "), + function (...) callback(parse(...)) end) +end +-- }}} + +return helpers.setasyncall(volume_linux) diff --git a/widgets/weather_all.lua b/widgets/weather_all.lua new file mode 100644 index 0000000..051ecfe --- /dev/null +++ b/widgets/weather_all.lua @@ -0,0 +1,139 @@ +-- weather widget type fetching from from US NOAA +-- Copyright (C) 2010 Adrian C. +-- Copyright (C) 2017 mutlusun +-- Copyright (C) 2019 Arthur Axel 'fREW' Schmidt +-- Copyright (C) 2019 Nguyễn Gia Phong +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local tonumber = tonumber +local math = { ceil = math.ceil } +local os = { date = os.date, difftime = os.difftime, time = os.time } +local string = { format = string.format } + +local spawn = require"vicious.spawn" +local helpers = require"vicious.helpers" +-- }}} + +-- Weather: provides weather information for a requested station +-- vicious.widgets.weather +local weather_all = {} + +-- copied from http://lua-users.org/wiki/TimeZone +local function get_timezone_offset() + local ts = os.time() + local utcdate = os.date("!*t", ts) + local localdate = os.date("*t", ts) + localdate.isdst = false -- this is the trick + return os.difftime(os.time(localdate), os.time(utcdate)) +end + +-- {{{ Weather widget type +local function parse(stdout, stderr, exitreason, exitcode) + -- Initialize function tables + local _weather = { + ["{city}"] = "N/A", + ["{when}"] = "N/A", + ["{wind}"] = "N/A", + ["{windmph}"] = "N/A", + ["{windkmh}"] = "N/A", + ["{sky}"] = "N/A", + ["{weather}"] = "N/A", + ["{tempf}"] = "N/A", + ["{tempc}"] = "N/A", + ["{dewf}"] = "N/A", + ["{dewc}"] = "N/A", + ["{humid}"] = "N/A", + ["{press}"] = "N/A" + } + + -- Check if there was a timeout or a problem with the station + if stdout == '' then return _weather end + + _weather["{city}"] = -- City and/or area + stdout:match"^(.+)%,.*%([%u]+%)" + or _weather["{city}"] + _weather["{wind}"] = -- Wind direction and degrees if available + stdout:match"Wind:[%s][%a]+[%s][%a]+[%s](.+)[%s]at.+$" + or _weather["{wind}"] + _weather["{windmph}"] = -- Wind speed in MPH if available + stdout:match"Wind:[%s].+[%s]at[%s]([%d]+)[%s]MPH" + or _weather["{windmph}"] + _weather["{sky}"] = -- Sky conditions if available + stdout:match"Sky[%s]conditions:[%s](.-)[%c]" + or _weather["{sky}"] + _weather["{weather}"] = -- Weather conditions if available + stdout:match"Weather:[%s](.-)[%c]" + or _weather["{weather}"] + _weather["{tempf}"] = -- Temperature in fahrenheit + stdout:match"Temperature:[%s]([%-]?[%d%.]+).*[%c]" + or _weather["{tempf}"] + _weather["{dewf}"] = -- Dew Point in fahrenheit + stdout:match"Dew[%s]Point:[%s]([%-]?[%d%.]+).*[%c]" + or _weather["{dewf}"] + _weather["{humid}"] = -- Relative humidity in percent + stdout:match"Relative[%s]Humidity:[%s]([%d]+)%%" + or _weather["{humid}"] + _weather["{press}"] = -- Pressure in hPa + stdout:match"Pressure[%s].+%((.+)[%s]hPa%)" + or _weather["{press}"] + + local year, month, day, hour, min = + stdout:match"(%d%d%d%d).(%d%d).(%d%d) (%d%d)(%d%d) UTC" + if year ~= nil then + local utctable = { year = year, month = month, day = day, + hour = hour, min = min } + _weather["{when}"] = os.time(utctable) + get_timezone_offset() + end + + -- Wind speed in km/h if MPH was available + if _weather["{windmph}"] ~= "N/A" then + _weather["{windmph}"] = tonumber(_weather["{windmph}"]) + _weather["{windkmh}"] = math.ceil(_weather["{windmph}"] * 1.6) + end -- Temperature in °C if °F was available + if _weather["{tempf}"] ~= "N/A" then + _weather["{tempf}"] = tonumber(_weather["{tempf}"]) + _weather["{tempc}"] = math.ceil((_weather["{tempf}"] - 32) * 5/9) + end -- Dew Point in °C if °F was available + if _weather["{dewf}"] ~= "N/A" then + _weather["{dewf}"] = tonumber(_weather["{dewf}"]) + _weather["{dewc}"] = math.ceil((_weather["{dewf}"] - 32) * 5/9) + end -- Capitalize some stats so they don't look so out of place + if _weather["{sky}"] ~= "N/A" then + _weather["{sky}"] = helpers.capitalize(_weather["{sky}"]) + end + if _weather["{weather}"] ~= "N/A" then + _weather["{weather}"] = helpers.capitalize(_weather["{weather}"]) + end + + return _weather +end + +function weather_all.async(format, warg, callback) + if not warg then return callback{} end + + -- Get weather forceast by the station ICAO code, from: + -- * US National Oceanic and Atmospheric Administration + local url = string.format( + "https://tgftp.nws.noaa.gov/data/observations/metar/decoded/%s.TXT", + warg) + spawn.easy_async("curl -fs " .. url, + function (...) callback(parse(...)) end) +end +-- }}} + +return helpers.setasyncall(weather_all) diff --git a/widgets/wifi_linux.lua b/widgets/wifi_linux.lua new file mode 100644 index 0000000..7299fec --- /dev/null +++ b/widgets/wifi_linux.lua @@ -0,0 +1,67 @@ +-- Wi-Fi widget type for GNU/Linux using iwconfig +-- Copyright (C) 2010 Adrian C. +-- Copyright (C) 2017 mutlusun +-- Copyright (C) 2018-2019 Nguyễn Gia Phong +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local type = type +local tonumber = tonumber +local math = { floor = math.floor } + +local helpers = require"vicious.helpers" +local spawn = require"vicious.spawn" +-- }}} + +-- Wifi: provides wireless information for a requested interface using iwconfig +-- vicious.widgets.wifi +local wifi_linux = {} + +-- {{{ Wireless widget type +local function parser(stdout, stderr, exitreason, exitcode) + local winfo = {} + -- Output differs from system to system, stats can be separated by + -- either = or :. Some stats may not be supported by driver. + -- SSID can have almost anything in it. + winfo["{ssid}"] = stdout:match'ESSID[=:]"(.-)"' or "N/A" + -- Modes are simple, but also match the "-" in Ad-Hoc + winfo["{mode}"] = stdout:match"Mode[=:]([%w%-]+)" or "N/A" + winfo["{chan}"] = tonumber(stdout:match"Channel[=:](%d+)" or 0) + winfo["{rate}"] = -- Bitrate without unit (Mb/s) + tonumber(stdout:match"Bit Rate[=:]%s?([%d%.]+)" or 0) + winfo["{freq}"] = -- Frequency in MHz (is output always in GHz?) + tonumber(stdout:match"Frequency[=:]%s?([%d%.]+)" or 0) * 1000 + winfo["{txpw}"] = -- Transmission power in dBm + tonumber(stdout:match"Tx%-Power[=:](%d+)" or 0) + winfo["{link}"] = -- Link quality over 70 + tonumber(stdout:match"Link Quality[=:](%d+)" or 0) + winfo["{linp}"] = -- Link quality percentage if quality was available + winfo["{link}"] ~= 0 and math.floor(winfo["{link}"]/0.7 + 0.5) or 0 + -- Signal level without unit (dBm), can be negative value + winfo["{sign}"] = tonumber(stdout:match"Signal level[=:](%-?%d+)" or 0) + return winfo +end + +function wifi_linux.async(format, warg, callback) + if type(warg) ~= "string" then return callback{} end + spawn.easy_async_with_shell( + "PATH=$PATH:/sbin:/usr/sbin:/usr/local/sbin iwconfig " .. warg, + function (...) callback(parser(...)) end) +end +-- }}} + +return helpers.setasyncall(wifi_linux) diff --git a/widgets/wifiiw_linux.lua b/widgets/wifiiw_linux.lua new file mode 100644 index 0000000..45783d5 --- /dev/null +++ b/widgets/wifiiw_linux.lua @@ -0,0 +1,76 @@ +-- Wi-Fi widget type for GNU/Linux using iw +-- Copyright (C) 2016 Marius M. +-- Copyright (C) 2017 mutlusun +-- Copyright (C) 2019 Nguyễn Gia Phong +-- Copyright (C) 2019 Xaver Hellauer +-- +-- This file is part of Vicious. +-- +-- Vicious 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. +-- +-- Vicious 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 Vicious. If not, see . + +-- {{{ Grab environment +local type = type +local tonumber = tonumber + +local helpers = require("vicious.helpers") +local spawn = require("vicious.spawn") +-- }}} + +-- Wifiiw: provides wireless information for a requested interface +-- using iw instead of deprecated iwconfig +-- vicious.widgets.wifiiw +local wifiiw_linux = {} + +local LINK = "PATH=$PATH:/sbin/:/usr/sbin:/usr/local/sbin iw dev %s link" +local INFO = "PATH=$PATH:/sbin/:/usr/sbin:/usr/local/sbin iw dev %s info" + +-- {{{ Wireless widget type +function wifiiw_linux.async(format, warg, callback) + if type(warg) ~= "string" then return callback{} end + local winfo = {} + + local function parse_link(stdout) + winfo["{bssid}"] = stdout:match"Connected to ([%x:]*)" or "N/A" + winfo["{ssid}"] = stdout:match"SSID: ([^\n]*)" or "N/A" + winfo["{freq}"] = tonumber(stdout:match"freq: (%d+)" or 0) + winfo["{sign}"] = -- Signal level can be negative; w/o unit (dBm) + tonumber(stdout:match"signal: (%-?%d+)" or 0) + winfo["{linp}"] = -- Link Quality (-100dBm->0%, -50dBm->100%) + winfo["{sign}"] ~= 0 and 200 + winfo["{sign}"]*2 or 0 + winfo["{rate}"] = -- Transmission rate, without unit (Mb/s) + tonumber(stdout:match"tx bitrate: ([%d%.]+)" or 0) + end + + local function parse_info(stdout) + winfo["{mode}"] = stdout:match"type ([^\n]*)" or "N/A" + winfo["{chan}"] = tonumber(stdout:match"channel (%d+)" or 0) + -- Transmission power, without unit (dBm) + winfo["{txpw}"] = tonumber(stdout:match"txpower (%-?%d+)" or 0) + end + + spawn.easy_async_with_shell( + LINK:format(warg), + function (std_out, std_err, exit_reason, exit_code) + parse_link(std_out) + spawn.easy_async_with_shell( + INFO:format(warg), + function (stdout, stderr, exitreason, exitcode) + parse_info(stdout) + callback(winfo) + end) + end) +end +-- }}} + +return helpers.setasyncall(wifiiw_linux)