Clean up configs, remove bashrc conflicting with bash

This commit is contained in:
natemaia
2017-11-30 22:21:33 -08:00
parent 15fcfc4da1
commit eb0884e453
272 changed files with 7464 additions and 1239 deletions

View File

@ -0,0 +1,360 @@
---------------------------------------------------
Full changelog is available online:
http://git.sysphere.org/vicious/log/?showmsg=1
---------------------------------------------------
6e16a41 bat: fix broken battery remaining time (was always N/A)
bfbc1bd volume: get the normalized volume like alsamixer and DE's indicator
3ef0f11 Next release, tag 2.1.2
946271c bat: expose information on battery wear and tear
7626989 README: write a list of major contributors to the project
c51e13c init: stop allocating reg table functions with properties
52cbb64 Revert "init: emit timeout instead of forced update"
7961ca1 weather: add support for dew point
dc556e5 bat: Use a real minus sign for the charging status
f36997b README: explain wifi abbreviations linp and sign
71ea0f3 README: enable caching in vicious.widgets.cpu example
211d450 init: share timers when possible
75cd103 Revert "mpd: workaround command termination bug in mpd server v0.18"
a6a73f4 mpd: workaround command termination bug in mpd server v0.18
c795642 README: update awesome usage examples for v3.5
563cb6f pkg: revert to pacman as default Arch method, checkupdates optional
01b2302 pkg: use checkupdates on Arch Linux.
2641bf8 contrib: add Open Weather and ATi graphics widget types
0fd4fc5 mboxc: Fixed typo in setmetatable after ported to lua 5.2
052d19e README: explain vicious is a generic WM widget library
bb891d6 Next release, tag 2.1.1
fac688e wifi: add support for /usr/bin binary path
f7fdd90 README: usage examples are for awesome version 3.4
d63343e contrib: add buildbot monitoring widget
8f2f155 Next release, tag 2.1.0
da37c09 mdir: add support for maildir whitespaces
eba6eb0 README: prefer vicious in global space
c28bac5 uptime: metatable bugfix after lua52 port fixed by Jorg Thalheim
3b161ff contrib: document how to enable contributed widgets
e1c7c47 os: change os.getenv init to allow standalone usage of os widget
1a7b430 contrib: initialization bugfix after lua52 port
4caf95b README: update initialization instructions
80d523a bat: metatable bugfix after lua52 port
e929bea widgets: cleanup behind the last commit 363c03e
363c03e Fixed io.lines() bug
8e35a98 Ported vicious.contrib to lua 5.2
41cc2c0 Ported vicious.widgets module to lua 5.2
b6b5290 Ported vicious module to lua 5.2
0741531 cpufreq: skip redudant freq variable check
97e54d6 cpufreq: rework new exception handlers
30db4be cpufreq: handle not existing frequency/governer
13cec6d pkg: update 'Arch S' substitution lines
2d8d7d7 Next release, tag 2.0.4
f972955 README: update thermal documentation
5605030 thermal: change coretemp default, allow file as widget argument
7a3699c division by zero, if battery is full charged
b11bb78 hddtemp: fix curl hang in version 7.24.0
73db82b README: add format function example for overriding symbols
7e81bb8 cpufreq: differentiate between ondemand and conservative
6f42ee5 README: update wtype argument explanation
45c6eba bat: fix time calculation, bug introduced in 350e924
cd4b04d thermal: remove unnecessary read
350e924 bat: another workaround for broken ACPI implementations
eeb27a2 [cpu] fix division by zero
059442d net: sanitize time computation to avoid division by zero
25b375b [pulse] round volume to integer
18e1823 vicious: cleanup tabs and bad intendation
cf996f2 [net] calculate time in a more clean way
f1844de Allocate reg table with all values in one shot
b11d251 README: provide multigraph usage example
44aea37 bat: better fix for missing rate in 31d7b2f
e01a8eb vicious: document mem.bcuse
1384b69 mem: provide mem.bcuse for Gabriel
324563e Next release, tag 2.0.3
dcc2b60 vicious: load widget modules only when needed
06e8f7c mpd: allow named keys or positional arguments
729ceb7 dio: import string.match()
c2f7fbc dio: provide stats for all devices, rewritten by Joerg
6522f58 cpu: another 10 percent optimization from Joerg
753ce61 cpu: calculation and optimization fixes by Joerg
f85d944 gmail: fixed misleading comment
31d7b2f bat: fixed "no such device" for current and power
d385843 helpers: properly handle magic characters
6ddad31 bat: added power_now support
fd0718e contrib: added a README with pulse.lua documented
9f4302c contrib: rewrite pulse module
5160cfe Next release, tag 2.0.2
add54f8 README: added missing register() documentation
fe2e432 TODO: fix contrib/sensors for Ian
7625933 wifi: proper fix for SSID regexp
7f7a94d gmail: inbox is now default
82eb67b wifi: removed spurious bracket from SSID regexp
304fa33 wifi: properly handle disconnects
32a7be1 wifi: provide link quality in percent
c532c0b contrib: fixed email of pulse widget author
49b0913 wifi: improved SSID regular expression
932bd8d init: emit timeout instead of forced update
fbd2af7 init: add set_markup support
e51d8ac date: turn time widget argument into an offset
c6085ee date: accept time as a widget argument
a9d6ea2 init: add connect_signal support, patch by Uli
86a1394 README: update contrib information
fbfcc49 init: comment connect_signal for users of awesome master
1d7f646 pkg: description updated
88c3d0c contrib: use pcall for luasocket in pop
b200a80 contrib: dont import pop - requires luasocket
0350ec9 TODO: document contrib widgets
f8a8696 contrib: imported POP3 widget from Boris
1a443cd init: import contrib widgets, commented
26b0395 contrib: imported contrib widgets
0d6333e Next release, tag 2.0.1
1534951 mpd: added some optional stats, commented
4113d37 pkg: include FreeBSD support
fc46e7a TODO: solid multigraph support needed
d912663 net: add operational state support
8674c91 dio: add partition support
d6faae1 TODO: remove org-mode extension
6171734 TODO: added the carrier detection todo
6d6e98a TODO: included the todo file to encourage contribution
ca1d8d7 README: cut on the security crap
fdae848 raid: removed permanent stats storage
3e12875 pkg: include Mandriva support
64e5426 uptime: simplified system load regexp
448275a widgets: reuse existing datasets where appropriate
a9347ec raid: import raid state widget type by Hagen
9af29ce pkg: aptitude alternative to apt-show-versions
94a60fb bat: fixed module description
338a2ee Next release, tag 2.0.0
0299c15 widgets: minor cleanup before a tag in cpuinf, fs, mbox...
4fc6dff wifi: return 0 not N/A when channel is unavailable
f50ad21 thermal: function argument is already local
4f86e28 os: merge with entropy widget type
49b1b09 helpers: index subdirectories in pathtotable()
7cbf987 bat: fix battery detection
a4bd307 README: updated link to contrib
4dad2e3 API: missing warg should not break awesome
237470c API: transform widgets namespace table to a directory
9a82d41 init: never pass string to awful.widget widgets
3ac4fcf init: missing widget type should not break awesome
febc91d hddtemp: minor style changes
9338cb9 cpuinf: rewritten and optimized
96a8e55 Make use of io.lines() where appropriate
6441db0 README: added a list of needed utilities
06a455a README: add contributors to Authors section
178a7eb README: added contrib and vicious-fbsd information
0dd5b72 README: standardize quotes
8482b54 mpd: rewritten and now uses curl not mpc
dda51b1 net: remove redudant string.match call
1abb451 Next release, tag 1.0.26
6898d4a dio: add I/O scheduler support
71f21f3 fs: use -l not --local for portability
edb4619 os: import operating system information widget
de15934 fs: further improve the regexp
3178068 mem: rewrite and simplify
77d1a0b gmail: switch to ~/.netrc for login storage
1c0ceff gmail: don't break on non-escaped chars, quote login
abacd77 fs: include available space in percent, requested by Jon
94e7ed1 fs: rewritten, less prone to error
fa40e40 init: never pass nil into awful.widget widgets
6f79227 Next release, tag 1.0.25
f34bfaf fs: fix the bug reported by Alex Tergeland
5f41c7a init: do not use keyword type for user data
ee64644 API: added force() function to force update widgets
95e9a77 API: regregister() and update() are not exposed
ff050a0 README: link to the "Widgets in awesome" document
5226d10 API: function enable_caching() is now cache()
9371839 volume: added real mute support
dbd6c7b weather: capitalize weather and sky stats
2c6b969 weather: calculate degree Celsius from Fahrenheit
ad14818 helpers: import capitalize
0ab8311 wifi: properly handle iwconfig PATH differences
4fa87fa wifi: properly handle non-existant interface
9eef646 git: ignore file removed
4664bee mdir: fix module description
57004c0 Next release, tag 1.0.24
b96cb98 bat: added a note about Apple PMU and ACPI/procfs
23d09f0 README: added the Security section
e29ea62 helpers: uformat helper replaces formatting done by widgets
f4cd746 pkg: solve interactive pacman prompt patch from tdy
5e1d1e0 volume: suggest appending card ID or other options
24f23fa gmail: reworked feed handling and changed username format
6c34e85 thermal: added support for procfs and coretemp
355c838 Next release, tag 1.0.23
0f3123e README: added textbox fixed width example
3095ffb pkg: added apt and yum to pkg managers table
91925e6 pkg: pacman moved to generic packages module
71566e0 dio: further improved support for multiple devices
8fe4cf4 net: sanitize time computation to avoid division by zero
d2a00d7 Next release, tag 1.0.22
15dd6ea vicious: update copyright
9e37225 pacman: remove 3.2 example, add ignores+deps example
cedf171 wifi: return numbers without notations
fba4db6 weather: fix parsing of negative temperatures
2100ea1 Next release, tag 1.0.21
9b5efc7 cpu: simplified table initialization
9150063 dio: preliminary support for multiple devices
b4031d2 dio: return separated read and write statistics
2c900fa README: minor changes in Format functions
c870691 fs: switched to 1K blocks and new keys
5cbd759 net: move formatting out of the worker
14d69f6 net: minor coding style change
db82c2a Next release, tag 1.0.20
3834caa hddtemp: fixed support for multiple devices
1790abb hddtemp: adapt regex to match disks like /dev/sg0
2492945 fs: minor coding style change
f5b47dc mdir: support for multiple directories
1d0cfd3 Cleanup widgets that use pathtotable helper
76942d1 helpers: simplify pathtotable, patch by filmor
26c1f07 Next release, tag 1.0.19
2e9b811 README: document new truncate and scroll usage
d0aa48a entropy: widget rewritten, poolsize no longer an argument
493150c uptime: merged with load widget type
7be560b bat: widget rewritten for sysfs
a99c1cf cpufreq: widget rewritten, governor symbols enabled
5f9818f dio: widget type rewritten
92be5fb thermal: widget rewritten for sysfs
46d52fa helpers: import pathtotable
eb661f6 batsys: add note about other charge/rate sources
6fadee6 batsys: don't return 0 if only rate is missing
8e21d26 batat: widget type removed, now available in the contrib branch
e66e507 batsys: import battery widget that uses sysfs
896df4d De-obfuscate e-mail address
94cf0b7 Minor changes in mboxc and org
44d943d gmail: add scrolling support and truncate control
9e91408 mbox: add scrolling support and truncate control
5ff8927 mpd: add scrolling support and truncate control
589feb1 helpers: import scroll from Remy Clouard
0d73f6d Ensure returned numbers are of type number
b105ae2 README: updated net description
3dc7c5e net: unit formatting delegated to a function
5c6f544 README: more information in Power section
40d7881 Adapt to awful.widget.graph max_value of 1
25ce79e batat: better returns when handling insufficient data
baa3612 Next release, tag 1.0.18
97d2ecb bat: better returns when handling insufficient data
4d1af1e bat: better fix for ACPI, from bioe007
2cfdd5a bat: quick fix for broken BAT/ACPI implementations
64464ef README: removed obsolete widget names
11985f6 README: added padding example
feca5da mem: tonumber is redudant
7f3d31e uptime: fixed problem discovered by jabbas
65e57fa README: document tables returned by widget types
893a85c README: caching documentation improved
00e7b2d README: fixed disk i/o warg documentation
0eea1a1 cpu: simplified CPU identification
e252fd9 Next release, tag 1.0.17
a2b9de4 mem: widget type rewritten
c84f515 mbox: read a 30kb chunk by default
4602ca2 Lots of coding style changes
af4e85f bat: added a handler for incompetent users
fcf1027 cpufreq: use string library for find/match
b4e028b Removed some useless else statements
b65d509 Reworked counters in mboxc, mdir, org
b185e96 mbox: don't hide when there is no mail
724366c Reworked some variables in entropy, load, mpd, net, init
643ecb0 date: function simplified
3d97d44 cpuinf: prepend 'cpu' to CPU ID in table keys
0d5f07b README: included 'Other uses' section
9d518b8 volume: added mute support
ddf9646 thermal: added some comments
0e863a5 bat: coding style changes
44f95c3 cpufreq: widget type rewritten
aed9245 README: fixed some typos
f933566 README: updated gmail information
f99a74b gmail: count all unread messages
5dac6a4 mboxc: support for multiple mbox files
32fe770 README: fixes from git-diff --check
b0f737e Rewrote all headers
8e544fd Next release, tag 1.0.16
b8f288b README: clarified caching usage
46a7241 README: added more caching information
eadf0c9 Caching is controlled by users
0945724 README: minor fix in mem widget example
51d5255 README: include pbar/graph properties in examples
c9fba4e Widgets derived from wicked include a notice
ba6dfae Next release, tag 1.0.15
1707bc6 README: rewritten and now includes the "Power" section
e205d66 CHANGES: changelog update
9ca9e2f Next release, tag 1.0.14
68b30a3 init: Default to 2s timer
bc3cf45 README: mpd format function simplified
7410bc0 Entropy widget cleaned up
549c8bc Remote file systems in fs widget are optional
2a5126f General cleanup
4f3599d README: fixed CPU widget example
1603b5f Curl connect timeout 1s, max time 3s
a950bd9 Next release, tag 1.0.13
161607e Gmail widget included
e5c980e CHANGES file switched to 'oneline' short format
fbc3f14 Updated links in the README
13527a3 Match all punctuation chars in fs partitions and mount points.
0ca1fd3 Match punctuation characters in wifi SSID.
cb36f74 Next release, tag 1.0.12.
11b433c README updated in preparation for release 1.0.12.
2cc4e97 Removed deprecated data field.
e7c2e2c Updated graphs and pbars for the upcoming awesome 3.4
853fafc Updated timers for the upcoming awesome 3.4
53b2799 Next release, tag 1.0.11.
c283edc Updated link to wicked home.
0067dca Removed emtpy lines from mpd.lua
5e3b953 Link to wicked and add Joerg as contributor.
4330375 Next release, tag 1.0.10.
44cd53f Updated the CHANGES file in preparation for a new tag.
7ad474b Use of LANG=C and improved matching in fs.lua.
f8eb93a Mention ICAO as the wanted code in the weather widget.
376591e Added laptop-mode note to the README.
e5181cc Next release, tag 1.0.9.
03693e7 Renamed unused loop identifiers to _
9f75b0d Spurious folds removed from helpers.
41d4acb Truncate helper, txtlen is local.
3b3e0dc CHANGES file updated. Will probably switch to shortlog.
d4595bb All worker functions are local.
8b38567 Added missing poolsize info to the README + minor change in fs.lua
798036e Changed fs widget type description.
8d59a01 Next release, tag 1.0.8.
d2d244f CPU Information widget included.
cbd9a53 Next release, tag 1.0.7.
013dd08 HDD Temperature widget included.
515cc0d Weather widget switched to curl.
477b8d2 CHANGES file updated before the next push.
f14280b Pacman widget updated for pacman 3.3.
b46278a Lower the wget timeout to 3s in weather.lua
eaf8470 Cleaned up sample widgets in the README
44aea40 Remove a spurious comment from helpers.lua
3c76e0d Introduced the truncate helper.
2d0cbf5 Fixed the weather widget, wind km/h was broken.
4c74de7 Padding removed, along with deprecated helper functions.
2d4efa6 Minor cleanup in init.lua
22033ba Disallow using a string widget type.
756215f Next release, tag 1.0.6.
5909d9e Updated the CHANGES file in preparation for tag v1.0.6.
cef569b CPU frequency widget included.
3fe67d4 Coding style changes in entropy, load, thermal, uptime.
09fda0a Rewrite of the escape helper.
047dba0 Next release, version/tag 1.0.5.
cad950a Updated the CHANGES file in preparation for tag v1.0.5.
418151f Make all widgets return their worker functions when called.
1630b78 Added wind speed in km/h to the weather widget.
f841c54 Updated the CHANGES file.
3f851ff Added a note about weather symbols to the weather widget.
1b99061 Use utf symbols for some bat states. Mention LuaFileSystem in mdir.lua
f00558c Next release, version 1.0.4.
475da04 Mbox mail count widget included.
dad393b Updated the CHANGES file.
bc63706 Coding style fixes in battery widgets.
052748a Fix the key reference to custom keys in the README
9c973a5 Updated the CHANGES file.
51770fe Fixed README and moved the ChangeLog to CHANGES.
2ab90de Updated the ChangeLog file, for v1.0.3.
0471c6a Next release, version 1.0.3.
a8dc5c6 Added basic documentation as a README file.
b06a5b6 Make git ignore org-mode files and archives.
87d895f Next release, version 1.0.2.
5ff480d Updated the ChangeLog file in preparation for a new version.
2921200 Added handlers to batat and fixed the battery_state table.
e2d503e Added a new, standalone, battery widget.
19ca7b5 Replaced TODO with Todo in helpers.
9cd250b Fixed batat module name and expanded bat states and symbols.
b331cb1 Updated the ChangeLog file.
76b8978 Battery widget moved to batat.lua in preparation for a new bat widget.
9da374d Updated the ChangeLog file.
1f31527 Next release, version 1.0.1.
11baa09 Added the ChangeLog file.
3eefddf Weather widget written and included, by request.
95dd8de Make git ignore backup files and lua binaries.
98e26ee Import of vicious source tree.

View File

@ -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.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

View File

@ -0,0 +1,585 @@
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:
- http://git.sysphere.org/vicious/about/
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.
For now Vicious doesn't depend on any third party Lua libraries, to
make it easier to install and use. That means some system utilities
are used instead, where available:
- hddtemp for the HDD Temperature widget type
- alsa-utils for the Volume widget type
- wireless_tools for the Wireless widget type
- curl for widget types accessing network resources
Usage
-----
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 (ie. Ion, WMII). It is compatible with both Lua v5.1 and v5.2.
$ lua
> widgets = require("vicious.widgets")
> print(widgets.volume(nil, "Master")[1])
100
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):
$ 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:
vicious = require("vicious")
Once you create a widget (a textbox, graph or a progressbar) call
vicious.register() to register it with Vicious:
vicious.register(widget, wtype, format, interval, warg)
widget
- widget created with widget() or awful.widget() (in case of a
graph or a progressbar)
wtype
- widget type or a function
- any of the available (default, or custom) widget types can
be used here, see below for a list of those provided by
Vicious
- function
- custom functions from your own "awesome" configuration can
be registered as widget types, see the "Custom widget types"
section
format
- string argument or a function
- $1, $2, $3... will be replaced by their respective value
returned by the widget type, some widget types return tables
with string keys, in that case use: ${key}
- function
- function(widget, args) can be used to manipulate data
returned by the widget type, more about this below
interval
- number of seconds between updates of the widget, 2s by
default, also read the "Power" section below
warg
- some widget types require an argument to be passed, for example
the battery ID
Other functions
---------------
Unregister a widget:
vicious.unregister(widget, keep)
- if keep is true widget will be suspended, waiting to be
activated
Suspend all widgets:
vicious.suspend()
- example automation script for the "laptop-mode-tools" start-stop
module: http://sysphere.org/~anrxc/local/sources/lmt-vicious.sh
Restart suspended widgets:
vicious.activate(widget)
- if widget is provided only that widget will be activated
Enable caching of a widget type:
vicious.cache(wtype)
- enable caching of values returned by a widget type
Force update of widgets:
vicious.force({ widget, })
- widget argument is a table with one or more widgets that will be
updated
Widget types
------------
Widget types consist of worker functions that take the "format"
argument given to vicious.register as the first argument, "warg" as
the second, and return a table of values to insert in the format
string.
vicious.widgets.cpu
- provides CPU usage for all available CPUs/cores
- returns 1st value as usage of all CPUs/cores, 2nd as usage of
first CPU/core, 3rd as usage of second CPU/core etc.
vicious.widgets.cpuinf
- provides speed and cache information for all available CPUs/cores
- returns a table with string keys, using CPU ID as a base:
{cpu0 mhz}, {cpu0 ghz}, {cpu0 kb}, {cpu0 mb}, {cpu1 mhz} etc.
vicious.widgets.cpufreq
- provides freq, voltage and governor info for a requested CPU
- takes the CPU ID as an argument, i.e. "cpu0"
- returns 1st value as frequency of requested CPU in MHz, 2nd in
GHz, 3rd as voltage in mV, 4th as voltage in V and 5th as the
governor state
vicious.widgets.thermal
- provides temperature levels of ACPI and coretemp thermal zones
- takes the thermal zone as an argument, i.e. "thermal_zone0", or a
table with 1st field as thermal zone, 2nd as data source -
available data sources are "proc", "core" and "sys" (which is the
default when only the zone is provided) and 3rd optional argument
as a temperature input file to read
- returns 1st value as temperature of requested thermal zone
vicious.widgets.uptime
- provides system uptime and load information
- returns 1st value as uptime in days, 2nd as uptime in hours, 3rd
as uptime in minutes, 4th as load average for past 1 minute, 5th
for 5 minutes and 6th for 15 minutes
vicious.widgets.bat
- provides state, charge, remaining time and wear for a requested
battery
- takes battery ID as an argument, i.e. "BAT0"
- returns 1st value as state of requested battery, 2nd as charge
level in percent, 3rd as remaining (charging or discharging)
time and 4th as the wear level in percent
vicious.widgets.mem
- provides RAM and Swap usage statistics
- returns 1st value as memory usage in percent, 2nd as memory usage,
3rd as total system memory, 4th as free memory, 5th as swap usage
in percent, 6th as swap usage, 7th as total system swap, 8th as
free swap and 9th as memory usage with buffers and cache
vicious.widgets.os
- provides operating system information
- returns 1st value as the operating system in use, 2nd as the
release version, 3rd as your username, 4th the hostname, 5th as
available system entropy and 6th value as available entropy in
percent
vicious.widgets.fs
- provides file system disk space usage
- takes an (optional) argument which, if true, includes remote file
systems, only local file systems are included by default
- returns a table with string keys, using mount points as a base:
{/ size_mb}, {/ size_gb}, {/ used_mb}, {/ used_gb}, {/ used_p},
{/ avail_mb}, {/ avail_gb}, {/ avail_p}, {/home size_mb} etc.
vicious.widgets.dio
- provides I/O statistics for all available storage devices
- 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}, {sdb1 total_s} etc.
vicious.widgets.raid
- provides state information for a requested RAID array
- takes the RAID array ID as an argument
- returns 1st value as the number of assigned, and 2nd as active,
devices in the array
vicious.widgets.hddtemp
- provides hard drive temperatures using the hddtemp daemon
- takes the hddtemp listening port as an argument, or defaults to
port 7634
- returns a table with string keys, using hard drives as a base:
{/dev/sda} and {/dev/sdc} for example
vicious.widgets.net
- provides state and usage statistics of all network interfaces
- returns a table with string keys, using net interfaces as a base:
{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.
vicious.widgets.wifi
- provides wireless information for a requested interface
- takes the network interface as an argument, i.e. "wlan0"
- returns a table with string keys: {ssid}, {mode}, {chan}, {rate},
{link}, {linp} (link quality in percent) and {sign} (signal level)
vicious.widgets.mbox
- provides the subject of last e-mail in a mbox file
- takes the full path to the mbox as an argument, or a table with
1st field as path, 2nd as maximum length and 3rd (optional) as
widget name - if 3rd field is present scrolling will be used (note: the
path will be escaped so special variables like ~ will not work, use
os.getenv("HOME").."mail" instead to access environment variables)
- returns 1st value as the subject of the last e-mail
vicious.widgets.mboxc
- provides the count of total, old and new messages in mbox files
- takes a table with full paths to mbox files as an argument
- returns 1st value as the total count of messages, 2nd as the count
of old messages and 3rd as the count of new messages
vicious.widgets.mdir
- provides the number of new and unread messages in Maildir
structures/directories
- takes a table with full paths to Maildir structures as an argument
- returns 1st value as the count of new messages and 2nd as the
count of "old" messages lacking the Seen flag
vicious.widgets.gmail
- provides count of new and subject of last e-mail on Gmail
- takes an (optional) argument, if it's a number subject will be
truncated, if a table, with 1st field as maximum length and 2nd
the widget name (i.e. "gmailwidget"), scrolling will be used
- keeps login information in the ~/.netrc file, example:
machine mail.google.com login user password pass
- returns a table with string keys: {count} and {subject}
vicious.widgets.org
- provides agenda statistics for Emacs org-mode
- takes a table with full paths to agenda files, that will be
parsed, as an argument
- returns 1st value as count of tasks you forgot to do, 2nd as count
of tasks for today, 3rd as count of tasks for the next 3 days and
4th as count of tasks to do in the week
vicious.widgets.pkg
- provides number of pending updates on UNIX systems
- takes the distribution name as an argument, i.e. "Arch"
- returns 1st value as the count of available updates
vicious.widgets.mpd
- provides Music Player Daemon information
- takes a table as an argument, 1st field should be the password (or
nil), 2nd the hostname (or nil) and 3rd port (or nil) - if no
argument is provided connection attempt will be made to localhost
port 6600 with no password
- returns a table with string keys: {volume}, {state}, {Artist},
{Title}, {Album}, {Genre} and optionally {Name} and {file}
vicious.widgets.volume
- provides volume levels and state of requested ALSA mixers
- takes the ALSA mixer control as an argument, i.e. "Master",
optionally append the card ID or other options, i.e. "PCM -c 0"
- returns 1st value as the volume level and 2nd as the mute state of
the requested channel
vicious.widgets.weather
- provides weather information for a requested station
- takes the ICAO station code as an argument, i.e. "LDRI"
- returns a table with string keys: {city}, {wind}, {windmph},
{windkmh}, {sky}, {weather}, {tempf}, {tempc}, {humid}, {dewf},
{dewc}, {press}
vicious.widgets.date
- provides access to os.date, with optional time formatting provided
as the format string - using regular date sequences
- takes optional time offset, in seconds, as an argument for example
to calculate time zone differences, otherwise current time is
formatted
- returns the output of os.date, formatted by provided sequences
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
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.
Rudi Siegel, a FreeBSD user, published his FreeBSD branch. If you are
also a BSD user you can find his work here:
- https://bitbucket.org/mutluyum/vicious_bsd/
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
vicious.register. Your function can accept "format" and "warg"
arguments, just like workers.
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... 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.
Security
--------
At the moment only one widget type (Gmail) requires auth. 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 trough 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. Trough the GPG Passphrase Agent they could
then decrypt the file transparently while their session is active.
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
datewidget = wibox.widget.textbox()
vicious.register(datewidget, vicious.widgets.date, "%b %d, %R")
- updated every 2 seconds (the default interval), uses standard
date sequences as the format string
Memory widget
memwidget = wibox.widget.textbox()
vicious.cache(vicious.widgets.mem)
vicious.register(memwidget, vicious.widgets.mem, "$1 ($2MB/$3MB)", 13)
- updated every 13 seconds, appends "MB" to 2nd and 3rd returned
values and enables caching of this widget type
HDD temperature widget
hddtempwidget = wibox.widget.textbox()
vicious.register(hddtempwidget, vicious.widgets.hddtemp, "${/dev/sda} °C", 19)
- updated every 19 seconds, requests the temperature level of the
{/dev/sda} key/disk and appends "°C" to the returned value, does
not provide the port argument so default port is used
Mbox widget
mboxwidget = wibox.widget.textbox()
vicious.register(mboxwidget, vicious.widgets.mbox, "$1", 5, "/home/user/mail/Inbox")
- updated every 5 seconds, provides full path to the mbox as an
argument
Battery widget
batwidget = awful.widget.progressbar()
batwidget:set_width(8)
batwidget:set_height(10)
batwidget:set_vertical(true)
batwidget:set_background_color("#494B4F")
batwidget:set_border_color(nil)
batwidget:set_color({ type = "linear", from = { 0, 0 }, to = { 0, 10 },
stops = { { 0, "#AECF96" }, { 0.5, "#88A175" }, { 1, "#FF5656" }})
vicious.register(batwidget, vicious.widgets.bat, "$2", 61, "BAT0")
- updated every 61 seconds, requests the current battery charge
level and displays a progressbar, provides "BAT0" battery ID as an
argument
CPU usage widget
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.cache(vicious.widgets.cpu)
vicious.register(cpuwidget, vicious.widgets.cpu, "$1", 3)
- updated every 3 seconds, feeds the graph with total usage
percentage of all CPUs/cores and enable caching of this widget
type
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.
Example
mpdwidget = wibox.widget.textbox()
vicious.register(mpdwidget, vicious.widgets.mpd,
function (widget, args)
if args["{state}"] == "Stop" then return ""
else return '<span color="white">MPD:</span> '..
args["{Artist}"]..' - '.. args["{Title}"]
end
end)
- hides the mpd widget when no song is playing, updated every 2
seconds (the default interval)
Example
uptimewidget = wibox.widget.textbox()
vicious.register(uptimewidget, vicious.widgets.uptime,
function (widget, args)
return string.format("Uptime: %2dd %02d:%02d ", args[1], args[2], args[3])
end, 61)
- uses string.format for padding uptime values to a minimum amount
of digits, updated every 61 seconds
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).
Example
uptimewidget = wibox.widget.textbox()
uptimewidget:set_align("right")
vicious.register(uptimewidget, vicious.widgets.uptime, "$1 $2:$3", 61)
uptimewidget = wibox.layout.constraint(uptimewidget, "exact", 50, nil)
- forces a fixed width of 50px to the uptime widget, and aligns its
text to the right
Another use case are stacked graphs (aka multigraphs) which Vicious
does not handle on its own at the moment, as it's hard to pass on
color index arguments elegantly. But they are not unusable, far from
it.
Example
ctext = wibox.widget.textbox()
cgraph = awful.widget.graph()
cgraph:set_width(100):set_height(20)
cgraph:set_stack(true):set_max_value(100)
cgraph:set_background_color("#494B4F")
cgraph:set_stack_colors({ "#FF5656", "#88A175", "#AECF96" })
vicious.register(ctext, vicious.widgets.cpu,
function (widget, args)
cgraph:add_value(args[2], 1) -- Core 1, color 1
cgraph:add_value(args[3], 2) -- Core 2, color 2
cgraph:add_value(args[4], 3) -- Core 3, color 3
end, 3)
- enables graph stacking/multigraph and plots usage of all three CPU
cores on a single graph, the textbox "ctext" is just an empty
placeholder, graph is updated every 3 seconds
A lot of users are not happy with default symbols used in volume,
battery, cpufreq and other widget types. You can use your own symbols
without any need to modify modules.
volumewidget = wibox.widget.textbox()
vicious.register(volumewidget, vicious.widgets.volume,
function(widget, args)
local label = { ["♫"] = "O", ["♩"] = "M" }
return "Volume: " .. args[1] .. "% State: " .. label[args[2]]
end, 2, "PCM")
- uses a custom table map to modify symbols representing the mixer
state; on or off/mute
Other
-----
Read "awesome" manual pages:
- awesome(1) awesomerc(5)
Awesome widgets explained:
- http://awesome.naquadah.org/wiki/Widgets_in_awesome
Example "awesome" configuration:
- http://git.sysphere.org/awesome-configs/
Authors
-------
Wicked written by:
- Lucas de Vries <lucas glacicle.com>
Vicious written by:
- Adrian C. (anrxc) <anrxc sysphere.org>
Vicious major contributors:
- Benedikt Sauer <filmor gmail.com>
- Greg D. <jabbas jabbas.pl>
- Henning Glawe <glaweh debian.org>
- Rémy C. <shikamaru mandriva.org>
- Hiltjo Posthuma <hiltjo codemadness.org>
- Hagen Schink <troja84 googlemail.com>
- Jörg Thalheim <jthalheim gmail.com>
- Arvydas Sidorenko <asido4 gmail.com>
- Dodo The Last <dodo.the.last gmail.com>
- ...
- Consult git log for a complete list of contributors

View File

@ -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

View File

@ -0,0 +1,132 @@
Contrib
-------
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:
vicious = require("vicious")
vicious.contrib = require("vicious.contrib")
Widget types
------------
Most widget types consist of worker functions that take the "format"
argument given to 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.ati
- provides various info about ATI GPU status
- takes card ID as an argument, i.e. "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.batacpi
-
vicious.contrib.batpmu
-
vicious.contrib.batproc
-
vicious.contrib.dio
- provides I/O statistics for requested storage devices
- takes 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
- takes OpenWeatherMap city ID as an argument, i.e. "1275339"
- returns a table with string keys: {city}, {wind deg}, {wind aim},
{wind kmh}, {wind mps}, {sky}, {weather}, {temp c}, {humid}, {press}
vicious.contrib.nvsmi
- provides (very basic) information about Nvidia GPU status from SMI
- takes optional card ID as an argument, i.e. "1", or defaults to ID 0
- returns 1st value as 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
- takes the 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 use the command: pacmd
list-sinks | grep 'name:'
- returns 1st value as the volume level
- vicious.contrib.pulse.add(percent, sink)
- @percent is a number, which increments or decrements the volume
level by its value in percent
- @sink optional, same usage as in vicious.contrib.pulse
- returns the exit status of pacmd
- vicious.contrib.pulse.toggle(sink)
- inverts the volume state (mute -> unmute; unmute -> mute)
- @sink optional, same usage as in vicious.contrib.pulse
- returns the exit status of pacmd
vicious.contrib.rss
-
vicious.contrib.sensors
-
vicious.contrib.buildbot
- provides last build status for configured buildbot builders (http://trac.buildbot.net/)
- returns build status in the format: [<builderName>.<currentBuildNumber>.<lastSuccessfulBuildNumber>]
- if <currentBuildNumber> is the same as <lastSuccessfulBuildNumber> only one number is displayed
- <buildNumber> colors: red - failed, green - successful, yellow - in progress
- it depends on lua json parser (e.g. liblua5.1-json on Ubuntu 12.04)
Usage examples
---------------------------------
Pulse Audio widget
vol = wibox.widget.textbox()
vicious.register(vol, vicious.contrib.pulse, " $1%", 2, "alsa_output.pci-0000_00_1b.0.analog-stereo")
vol:buttons(awful.util.table.join(
awful.button({ }, 1, function () awful.util.spawn("pavucontrol") end),
awful.button({ }, 4, function () vicious.contrib.pulse.add(5,"alsa_output.pci-0000_00_1b.0.analog-stereo") end),
awful.button({ }, 5, function () vicious.contrib.pulse.add(-5,"alsa_output.pci-0000_00_1b.0.analog-stereo") end)
))
Buildbot widget
buildbotwidget = wibox.widget.textbox()
local buildbotwidget_warg = {
{builder="coverage", url="http://buildbot.buildbot.net"},
{builder="tarball-slave", url="http://buildbot.buildbot.net"}
}
vicious.register(buildbotwidget, vicious.contrib.buildbot, "$1,", 3600, buildbotwidget_warg)

View File

@ -0,0 +1,79 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2013, NormalRa <normalrawr gmail com>
---------------------------------------------------
-- {{{ 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.widgets.ati
local ati = {}
-- {{{ 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, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,52 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local tonumber = tonumber
local io = { popen = io.popen }
local setmetatable = setmetatable
local table = { insert = table.insert }
local string = { match = string.match }
-- }}}
-- Batacpi: provides state, charge, and remaining time for all batteries using acpitool
-- vicious.contrib.batacpi
local batacpi = {}
-- {{{ Battery widget type
local function worker(format)
local battery_info = {}
local battery_state = {
["full"] = "",
["unknown"] = "",
["charged"] = "",
["charging"] = "+",
["discharging"] = "-"
}
-- Get data from acpitool
local f = io.popen("acpitool -b")
for line in f:lines() do
-- Check if the battery is present
if string.match(line, "^[%s]+Battery.*") then
-- Store state and charge information
table.insert(battery_info, (battery_state[string.match(line, "([%a]*),") or "unknown"]))
table.insert(battery_info, (tonumber(string.match(line, "([%d]?[%d]?[%d])%.")) or 0))
-- Store remaining time information
table.insert(battery_info, (string.match(line, "%%,%s(.*)") or "N/A"))
else
return {battery_state["unknown"], 0, "N/A"}
end
end
f:close()
return battery_info
end
-- }}}
return setmetatable(batacpi, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,79 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ 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 = {}
-- {{{ 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, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,86 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ 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 = {}
-- {{{ 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, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,186 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2012, Andrzje Bieniek <andyhelp@gmail.com>
---------------------------------------------------
-- {{{ 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"}
module("vicious.contrib.buildbot")
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 .. ".<span color=\"orange\">can't find libluaX.X-json</span>]"
end
local err = b:getError()
if err then
return s .. ".<span color=\"orange\">" .. err .. "</span>]"
end
if b:getLastSuccessful() ~= 0 then
success_build_nr_str = "<span color=\"green\">".. b:getLastSuccessful() .."</span>"
else
success_build_nr_str = "-"
end
local current_build_color = bc[b:getCurrentStatus()]
current_build_nr_str = "<span color=\""..current_build_color.."\">"..b:getCurrent().."</span>"
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(_M, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,73 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ 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 = {}
-- 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, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,18 @@
---------------------------------------------------
-- Vicious widgets for the awesome window manager
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ 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 })

View File

@ -0,0 +1,48 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
-- * (c) 2009, Lucas de Vries <lucas@glacicle.com>
---------------------------------------------------
-- {{{ 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 = {}
-- {{{ 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 {helpers.escape(np)}
end
-- }}}
return setmetatable(mpc, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,139 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
-- * (c) 2009, Henning Glawe <glaweh@debian.org>
-- * (c) 2009, Lucas de Vries <lucas@glacicle.com>
---------------------------------------------------
-- {{{ 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 = {}
-- 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 ~= 0 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, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,35 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Radu A. <admiral0@tuxfamily.org>
---------------------------------------------------
-- {{{ 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 })

View File

@ -0,0 +1,42 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2014, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ 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 = {}
-- {{{ 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, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,94 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2013, NormalRa <normalrawr gmail com>
---------------------------------------------------
-- {{{ Grab environment
local tonumber = tonumber
local io = { popen = io.popen }
local setmetatable = setmetatable
local string = { match = string.match }
local math = {
ceil = math.ceil,
floor = math.floor
}
-- }}}
-- Openweather: provides weather information for a requested station
-- vicious.widgets.openweather
local openweather = {}
-- 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",
["{humid}"] = "N/A",
["{press}"] = "N/A"
}
-- {{{ Openweather widget type
local function worker(format, warg)
if not warg then return end
-- Get weather forceast using the city ID code, from:
-- * OpenWeatherMap.org
local openweather = "http://api.openweathermap.org/data/2.5/weather?id="..warg.."&mode=json&units=metric"
local f = io.popen("curl --connect-timeout 1 -fsm 3 '"..openweather.."'")
local ws = f:read("*all")
f:close()
-- Check if there was a timeout or a problem with the station
if ws == nil then return _wdata end
_wdata["{city}"] = -- City name
string.match(ws, '"name":"([%a%s%-]+)"') or _wdata["{city}"]
_wdata["{wind deg}"] = -- Wind degrees
string.match(ws, '"deg":([%d]+)') or _wdata["{wind deg}"]
_wdata["{wind mps}"] = -- Wind speed in meters per second
string.match(ws, '"speed":([%d%.]+)') or _wdata["{wind mps}"]
_wdata["{sky}"] = -- Sky conditions
string.match(ws, '"main":"([%a]+)"') or _wdata["{sky}"]
_wdata["{weather}"] = -- Weather description
string.match(ws, '"description":"([%a%s]+)"') or _wdata["{weather}"]
_wdata["{temp c}"] = -- Temperature in celsius
string.match(ws, '"temp":([%-]?[%d%.]+)') or _wdata["{temp c}"]
_wdata["{humid}"] = -- Relative humidity in percent
string.match(ws, '"humidity":([%d]+)') or _wdata["{humid}"]
_wdata["{press}"] = -- Pressure in hPa
string.match(ws, '"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
-- }}}
return setmetatable(openweather, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,54 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ 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 = {}
-- {{{ 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, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,55 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Boris Bolgradov <>
--
-- 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 = {}
-- {{{ 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, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,121 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, MrMagne <mr.magne@yahoo.fr>
-- * (c) 2010, Mic92 <jthalheim@gmail.com>
---------------------------------------------------
-- {{{ 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
}
-- }}}
-- Pulse: provides volume levels of requested pulseaudio sinks and methods to change them
-- vicious.contrib.pulse
local pulse = {}
-- {{{ 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
return { math.floor(vol/0x10000*100), "on"}
end
-- }}}
-- {{{ Volume control helper
function pulse.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
local cmd = string.format("pacmd set-sink-volume %s 0x%x >/dev/null", sink, vol)
return os.execute(cmd)
end
function pulse.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, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,68 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2009, olcc
--
-- 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 = {}
-- {{{ 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 .. ">(.-)</" .. object .. ">", i)
if not ob then break end
for _, v in pairs(fields) do
out[v][k] = ob:match("<" .. v .. ">(.*)</" .. v .. ">")
end
k = k+1
i = j+1
end
-- Update the entity count
out.count = k
return out
end
-- }}}
return setmetatable(rss, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,69 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Greg D. <jabbas@jabbas.pl>
---------------------------------------------------
-- {{{ 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 = {}
-- {{{ 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, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,162 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
-- * (c) 2009, Rémy C. <shikamaru@mandriva.org>
-- * (c) 2009, Benedikt Sauer <filmor@gmail.com>
-- * (c) 2009, Henning Glawe <glaweh@debian.org>
-- * (c) 2009, Lucas de Vries <lucas@glacicle.com>
---------------------------------------------------
-- {{{ Grab environment
local pairs = pairs
local rawget = rawget
local require = require
local tonumber = tonumber
local io = { open = io.open }
local setmetatable = setmetatable
local getmetatable = getmetatable
local string = {
upper = string.upper,
format = string.format
}
-- }}}
-- Helpers: provides helper functions for vicious widgets
-- vicious.helpers
local helpers = {}
-- {{{ Variable definitions
local scroller = {}
-- }}}
-- {{{ Helper functions
-- {{{ Loader of vicious modules
function helpers.wrequire(table, key)
local module = rawget(table, key)
return module or require(table._NAME .. "." .. key)
end
-- }}}
-- {{{ Expose path as a Lua table
function helpers.pathtotable(dir)
return setmetatable({ _path = dir },
{ __index = function(table, index)
local path = table._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(table))
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 = {
["\""] = "&quot;",
["&"] = "&amp;",
["'"] = "&apos;",
["<"] = "&lt;",
[">"] = "&gt;"
}
return text and text:gsub("[\"&'<>]", xml_entities)
end
-- }}}
-- {{{ Escape a string for save usage on the command line
function helpers.shellquote(s)
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
-- }}}
return helpers
-- }}}

View File

@ -0,0 +1,266 @@
---------------------------------------------------
-- Vicious widgets for the awesome window manager
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
-- * (c) 2009, Lucas de Vries <lucas@glacicle.com>
---------------------------------------------------
-- {{{ 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 t = os.time()
local data = {}
-- Check for chached output newer than the last update
if widget_cache[reg.wtype] ~= nil then
local c = widget_cache[reg.wtype]
if (c.time == nil or c.time <= t-reg.timer) or disablecache then
c.time, c.data = t, reg.wtype(reg.format, reg.warg)
end
data = c.data
else
data = reg.wtype and reg.wtype(reg.format, reg.warg)
end
if type(data) == "table" then
if type(reg.format) == "string" then
data = helpers.format(reg.format, data)
elseif type(reg.format) == "function" then
data = reg.format(widget, data)
end
end
if widget.add_value ~= nil then
widget:add_value(tonumber(data) and tonumber(data)/100)
elseif widget.set_value ~= nil then
widget:set_value(tonumber(data) and tonumber(data)/100)
elseif widget.set_markup ~= nil then
widget:set_markup(data)
else
widget.text = data
end
return data
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.timer > 0 then
local tm = timers[reg.timer] and timers[reg.timer].timer
tm = tm or timer({ timeout = reg.timer })
if tm.connect_signal then
tm:connect_signal("timeout", reg.update)
else
tm:add_signal("timeout", reg.update)
end
if not timers[reg.timer] then
timers[reg.timer] = { timer = tm, refs = 1 }
else
timers[reg.timer].refs = timers[reg.timer].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, timer, warg)
local widget = widget
local reg = {
-- Set properties
wtype = wtype,
format = format,
timer = timer,
warg = warg,
widget = widget,
}
-- Set functions
reg.update = function ()
update(widget, reg)
end
-- Default to 2s timer
if reg.timer == nil then
reg.timer = 2
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.timer]
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] = {}
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
-- }}}
return vicious
-- }}}

View File

@ -0,0 +1,94 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
-- * (c) 2013, NormalRa <normalrawr@gmail.com>
---------------------------------------------------
-- {{{ 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
}
-- }}}
-- Bat: provides state, charge, remaining time, and wear for a requested battery
-- vicious.widgets.bat
local bat = {}
-- {{{ Battery widget type
local function worker(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"] = ""
}
-- Check if the battery is present
if battery.present ~= "1\n" then
return {battery_state["Unknown\n"], 0, "N/A", 0}
end
-- Get state information
local state = battery_state[battery.status] or battery_state["Unknown\n"]
-- Get capacity information
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}
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
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}
end
-- Calculate remaining (charging or discharging) time
local time = "N/A"
if rate ~= nil and rate ~= 0 then
if state == "+" then
timeleft = (tonumber(capacity) - tonumber(remaining)) / tonumber(rate)
elseif state == "" then
timeleft = tonumber(remaining) / tonumber(rate)
else
return {state, percent, time, wear}
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}
end
-- }}}
return setmetatable(bat, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,80 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2011, Adrian C. <anrxc@sysphere.org>
-- * (c) 2009, Lucas de Vries <lucas@glacicle.com>
-- * (c) 2011, Jörg Thalheim <jthalheim@gmail.com>
---------------------------------------------------
-- {{{ Grab environment
local ipairs = ipairs
local io = { open = io.open }
local setmetatable = setmetatable
local math = { floor = math.floor }
local table = { insert = table.insert }
local string = {
sub = string.sub,
gmatch = string.gmatch
}
-- }}}
-- Cpu: provides CPU usage for all available CPUs/cores
-- vicious.widgets.cpu
local cpu = {}
-- Initialize function tables
local cpu_usage = {}
local cpu_total = {}
local cpu_active = {}
-- {{{ CPU widget type
local function worker(format)
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
-- }}}
return setmetatable(cpu, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,61 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local tonumber = tonumber
local setmetatable = setmetatable
local string = { match = string.match }
local helpers = require("vicious.helpers")
-- }}}
-- Cpufreq: provides freq, voltage and governor info for a requested CPU
-- vicious.widgets.cpufreq
local cpufreq = {}
-- {{{ CPU frequency widget type
local function worker(format, warg)
if not warg then return end
local _cpufreq = helpers.pathtotable("/sys/devices/system/cpu/"..warg.."/cpufreq")
local governor_state = {
["ondemand\n"] = "",
["powersave\n"] = "",
["userspace\n"] = "¤",
["performance\n"] = "",
["conservative\n"] = ""
}
-- 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(string.match(_cpufreq.scaling_voltages, 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
-- }}}
return setmetatable(cpufreq, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,44 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local tonumber = tonumber
local io = { lines = io.lines }
local setmetatable = setmetatable
local string = { gmatch = string.gmatch }
-- }}}
-- Cpuinf: provides speed and cache information for all available CPUs/cores
-- vicious.widgets.cpuinf
local cpuinf = {}
-- {{{ CPU Information widget type
local function worker(format)
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
-- }}}
return setmetatable(cpuinf, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,27 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
-- * (c) 2009, Lucas de Vries <lucas@glacicle.com>
---------------------------------------------------
-- {{{ Grab environment
local setmetatable = setmetatable
local os = {
date = os.date,
time = os.time
}
-- }}}
-- Date: provides access to os.date with optional time formatting
-- vicious.widgets.date
local date = {}
-- {{{ Date widget type
local function worker(format, warg)
return os.date(format or nil, warg and os.time()+warg or nil)
end
-- }}}
return setmetatable(date, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,73 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2011, Jörg T. <jthalheim@gmail.com>
---------------------------------------------------
-- {{{ Grab environment
local pairs = pairs
local io = { lines = io.lines }
local setmetatable = setmetatable
local string = { match = string.match }
local helpers = require("vicious.helpers")
local os = {
time = os.time,
difftime = os.difftime
}
-- }}}
-- Disk I/O: provides I/O statistics for requested storage devices
-- vicious.widgets.dio
local dio = {}
-- Initialize function tables
local disk_usage = {}
local disk_stats = {}
local disk_time = 0
-- Constant definitions
local unit = { ["s"] = 1, ["kb"] = 2, ["mb"] = 2048 }
-- {{{ Disk I/O widget type
local function worker(format)
local disk_lines = {}
for line in io.lines("/proc/diskstats") do
local device, read, write =
-- Linux kernel documentation: Documentation/iostats.txt
string.match(line, "([^%s]+) %d+ %d+ (%d+) %d+ %d+ %d+ (%d+)")
disk_lines[device] = { read, write }
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
last_stats[1], last_stats[2] = stats[1], stats[2]
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
-- 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)
end
disk_time = time
disk_stats = disk_lines
return disk_usage
end
-- }}}
return setmetatable(dio, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,52 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
-- * (c) 2009, Lucas de Vries <lucas@glacicle.com>
---------------------------------------------------
-- {{{ Grab environment
local tonumber = tonumber
local io = { popen = io.popen }
local setmetatable = setmetatable
local string = { match = string.match }
local helpers = require("vicious.helpers")
-- }}}
-- FS: provides file system disk space usage
-- vicious.widgets.fs
local fs = {}
-- Variable definitions
local unit = { ["mb"] = 1024, ["gb"] = 1024^2 }
-- {{{ Filesystem widget type
local function worker(format, warg)
-- Fallback to listing local filesystems
if warg then warg = "" else warg = "-l" end
local fs_info = {} -- Get data from df
local f = io.popen("LC_ALL=C df -kP " .. helpers.shellquote(warg))
for line in f:lines() do -- Match: (size) (used)(avail)(use%) (mount)
local s = string.match(line, "^.-[%s]([%d]+)")
local u,a,p = string.match(line, "([%d]+)[%D]+([%d]+)[%D]+([%d]+)%%")
local m = string.match(line, "%%[%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
f:close()
return fs_info
end
-- }}}
return setmetatable(fs, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,77 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local type = type
local tonumber = tonumber
local io = { popen = io.popen }
local setmetatable = setmetatable
local helpers = require("vicious.helpers")
local string = {
match = string.match
}
-- }}}
-- Gmail: provides count of new and subject of last e-mail on Gmail
-- vicious.widgets.gmail
local gmail = {}
-- {{{ Variable definitions
local rss = {
inbox = "https://mail.google.com/mail/feed/atom",
unread = "https://mail.google.com/mail/feed/atom/unread",
--labelname = "https://mail.google.com/mail/feed/atom/labelname",
}
-- Default is just Inbox
local feed = rss.inbox
local mail = {
["{count}"] = 0,
["{subject}"] = "N/A"
}
-- }}}
-- {{{ Gmail widget type
local function worker(format, warg)
-- Get info from the Gmail atom feed
local f = io.popen("curl --connect-timeout 1 -m 3 -fsn " .. feed)
-- Could be huge don't read it all at once, info we are after is at the top
local xml = f:read(2000)
if xml ~= nil then
return mail
end
mail["{count}"] = -- Count comes before messages and matches at least 0
tonumber(string.match(xml, "<fullcount>([%d]+)</fullcount>")) or mail["{count}"]
-- Find subject tag
local title = string.match(xml, "<entry>.-<title>(.-)</title>")
if title ~= nil then
-- Check if we should scroll, or maybe truncate
if warg then
if type(warg) == "table" then
title = helpers.scroll(title, warg[1], warg[2])
else
title = helpers.truncate(title, warg)
end
end
-- Spam sanitize the subject and store
mail["{subject}"] = helpers.escape(title)
end
f:close()
return mail
end
-- }}}
return setmetatable(gmail, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,40 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local tonumber = tonumber
local io = { popen = io.popen }
local setmetatable = setmetatable
local string = { gmatch = string.gmatch }
local helpers = require("vicious.helpers")
-- }}}
-- Hddtemp: provides hard drive temperatures using the hddtemp daemon
-- vicious.widgets.hddtemp
local hddtemp = {}
-- {{{ HDD Temperature widget type
local function worker(format, warg)
-- Fallback to default hddtemp port
if warg == nil then warg = 7634 end
local hdd_temp = {} -- Get info from the hddtemp daemon
local quoted = helpers.shellquote(warg)
local f = io.popen("echo | curl --connect-timeout 1 -fsm 3 telnet://127.0.0.1:"..quoted)
for line in f:lines() do
for d, t in string.gmatch(line, "|([%/%a%d]+)|.-|([%d]+)|[CF]+|") do
hdd_temp["{"..d.."}"] = tonumber(t)
end
end
f:close()
return hdd_temp
end
-- }}}
return setmetatable(hddtemp, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,18 @@
---------------------------------------------------
-- Vicious widgets for the awesome window manager
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Setup environment
local setmetatable = setmetatable
local wrequire = require("vicious.helpers").wrequire
-- Vicious: widgets for the awesome window manager
-- vicious.widgets
local widgets = { _NAME = "vicious.widgets" }
-- }}}
-- Load modules at runtime as needed
return setmetatable(widgets, { __index = wrequire })

View File

@ -0,0 +1,53 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local type = type
local io = { open = io.open }
local setmetatable = setmetatable
local string = { gfind = string.gfind }
local helpers = require("vicious.helpers")
-- }}}
-- Mbox: provides the subject of last e-mail in a mbox file
-- vicious.widgets.mbox
local mbox = {}
-- Initialize variables
local subject = "N/A"
-- {{{ Mailbox widget type
local function worker(format, warg)
if not warg then return end
-- mbox could be huge, get a 30kb chunk from EOF
if type(warg) ~= "table" then _mbox = warg end
-- * attachment could be much bigger than 30kb
local f = io.open(_mbox or warg[1])
f:seek("end", -30720)
local txt = f:read("*all")
f:close()
-- Find all Subject lines
for i in string.gfind(txt, "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 {helpers.escape(subject)}
end
-- }}}
return setmetatable(mbox, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,58 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local io = { open = io.open }
local setmetatable = setmetatable
local string = { find = string.find }
-- }}}
-- Mboxc: provides the count of total, old and new messages in mbox files
-- vicious.widgets.mboxc
local mboxc = {}
-- {{{ Mbox count widget type
local function worker(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 = string.find(lines, "^From[%s]")
if from ~= nil then count.total = count.total + 1 end
-- Read messages have the Status header
local _, status = string.find(lines, "^Status:[%s]RO$")
if status ~= nil then count.old = count.old + 1 end
-- Skip the folder internal data
local _, int = string.find(lines, "^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
-- }}}
return setmetatable(mboxc, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,43 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
-- * (c) Maildir Biff Widget, Fredrik Ax
---------------------------------------------------
-- {{{ Grab environment
local io = { popen = io.popen }
local setmetatable = setmetatable
local helpers = require("vicious.helpers")
-- }}}
-- Mdir: provides the number of new and unread messages in Maildir structures/dirs
-- vicious.widgets.mdir
local mdir = {}
-- {{{ Maildir widget type
local function worker(format, warg)
if not warg then return end
-- Initialize counters
local count = { new = 0, cur = 0 }
for i=1, #warg do
quoted_path = helpers.shellquote(warg[i])
-- Recursively find new messages
local f = io.popen("find "..quoted_path.." -type f -wholename '*/new/*'")
for line in f:lines() do count.new = count.new + 1 end
f:close()
-- Recursively find "old" messages lacking the Seen flag
local f = io.popen("find "..quoted_path.." -type f -regex '.*/cur/.*2,[^S]*$'")
for line in f:lines() do count.cur = count.cur + 1 end
f:close()
end
return {count.new, count.cur}
end
-- }}}
return setmetatable(mdir, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,52 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
-- * (c) 2009, Lucas de Vries <lucas@glacicle.com>
---------------------------------------------------
-- {{{ Grab environment
local io = { lines = io.lines }
local setmetatable = setmetatable
local math = { floor = math.floor }
local string = { gmatch = string.gmatch }
-- }}}
-- Mem: provides RAM and Swap usage statistics
-- vicious.widgets.mem
local mem = {}
-- {{{ Memory widget type
local function worker(format)
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 == "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.f + _mem.buf.b + _mem.buf.c
_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
-- }}}
return setmetatable(mem, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,64 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local tonumber = tonumber
local io = { popen = io.popen }
local setmetatable = setmetatable
local string = { gmatch = string.gmatch }
local helpers = require("vicious.helpers")
-- }}}
-- Mpd: provides Music Player Daemon information
-- vicious.widgets.mpd
local mpd = {}
-- {{{ MPD widget type
local function worker(format, warg)
local mpd_state = {
["{volume}"] = 0,
["{state}"] = "N/A",
["{Artist}"] = "N/A",
["{Title}"] = "N/A",
["{Album}"] = "N/A",
["{Genre}"] = "N/A",
--["{Name}"] = "N/A",
--["{file}"] = "N/A",
}
-- Fallback to MPD defaults
local pass = warg and (warg.password or warg[1]) or "\"\""
local host = warg and (warg.host or warg[2]) or "127.0.0.1"
local port = warg and (warg.port or warg[3]) or "6600"
-- Construct MPD client options
local mpdh = "telnet://"..host..":"..port
local echo = "echo 'password "..pass.."\nstatus\ncurrentsong\nclose'"
-- Get data from MPD server
local f = io.popen(echo.." | curl --connect-timeout 1 -fsm 3 "..mpdh)
for line in f:lines() do
for k, v in string.gmatch(line, "([%w]+):[%s](.*)$") do
if k == "volume" then mpd_state["{"..k.."}"] = v and tonumber(v)
elseif k == "state" then mpd_state["{"..k.."}"] = helpers.capitalize(v)
elseif k == "Artist" then mpd_state["{"..k.."}"] = helpers.escape(v)
elseif k == "Title" then mpd_state["{"..k.."}"] = helpers.escape(v)
elseif k == "Album" then mpd_state["{"..k.."}"] = helpers.escape(v)
elseif k == "Genre" then mpd_state["{"..k.."}"] = helpers.escape(v)
--elseif k == "Name" then mpd_state["{"..k.."}"] = helpers.escape(v)
--elseif k == "file" then mpd_state["{"..k.."}"] = helpers.escape(v)
end
end
end
f:close()
return mpd_state
end
-- }}}
return setmetatable(mpd, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,80 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
-- * (c) 2009, Lucas de Vries <lucas@glacicle.com>
---------------------------------------------------
-- {{{ Grab environment
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 state and usage statistics of all network interfaces
-- vicious.widgets.net
local net = {}
-- Initialize 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)
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
-- }}}
return setmetatable(net, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,62 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
-- * (c) org-awesome, Damien Leone
---------------------------------------------------
-- {{{ Grab environment
local io = { lines = io.lines }
local setmetatable = setmetatable
local string = { find = string.find }
local os = {
time = os.time,
date = os.date
}
-- }}}
-- Org: provides agenda statistics for Emacs org-mode
-- vicious.widgets.org
local org = {}
-- {{{ OrgMode widget type
local function worker(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 = string.find(line, "SCHEDULED:")
local closed = string.find(line, "CLOSED:")
local deadline = string.find(line, "DEADLINE:")
if (scheduled and not closed) or (deadline and not closed) then
local b, e, y, m, d = string.find(line, "(%d%d%d%d)-(%d%d)-(%d%d)")
if 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
end
return {count.past, count.today, count.soon, count.future}
end
-- }}}
return setmetatable(org, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,73 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local pairs = pairs
local tonumber = tonumber
local io = { popen = io.popen }
local math = { ceil = math.ceil }
local los = { getenv = os.getenv }
local setmetatable = setmetatable
local helpers = require("vicious.helpers")
local string = {
gsub = string.gsub,
match = string.match
}
-- }}}
-- OS: provides operating system information
-- vicious.widgets.os
local os = {}
-- {{{ Operating system widget type
local function worker(format)
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, v in pairs(system) do
if kernel[k] then
system[k] = string.gsub(kernel[k], "[%s]*$", "")
end
end
-- BSD manual page: uname(1)
if system["ostype"] == "N/A" then
local f = io.popen("uname -snr")
local uname = f:read("*line")
f:close()
system["ostype"], system["hostname"], system["osrelease"] =
string.match(uname, "([%w]+)[%s]([%w%p]+)[%s]([%w%p]+)")
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
-- }}}
return setmetatable(os, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,48 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local io = { popen = io.popen }
local math = { max = math.max }
local setmetatable = setmetatable
-- }}}
-- Pkg: provides number of pending updates on UNIX systems
-- vicious.widgets.pkg
local pkg = {}
-- {{{ Packages widget type
local function worker(format, warg)
if not warg then return end
-- Initialize counters
local updates = 0
local manager = {
["Arch"] = { cmd = "pacman -Qu" },
["Arch C"] = { cmd = "checkupdates" },
["Arch S"] = { cmd = "yes | pacman -Sup", sub = 1 },
["Debian"] = { cmd = "apt-show-versions -u -b" },
["Ubuntu"] = { cmd = "aptitude search '~U'" },
["Fedora"] = { cmd = "yum list updates", sub = 3 },
["FreeBSD"] ={ cmd = "pkg_version -I -l '<'" },
["Mandriva"]={ cmd = "urpmq --auto-select" }
}
-- Check if updates are available
local _pkg = manager[warg]
local f = io.popen(_pkg.cmd)
for line in f:lines() do
updates = updates + 1
end
f:close()
return {_pkg.sub and math.max(updates-_pkg.sub, 0) or updates}
end
-- }}}
return setmetatable(pkg, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,60 @@
-----------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Hagen Schink <troja84@googlemail.com>
-----------------------------------------------------
-- {{{ Grab environment
local io = { open = io.open }
local setmetatable = setmetatable
local string = {
len = string.len,
sub = string.sub,
match = string.match,
gmatch = string.gmatch
}
-- }}}
-- Raid: provides state information for a requested RAID array
-- vicious.widgets.raid
local raid = {}
-- Initialize function tables
local mddev = {}
-- {{{ RAID widget type
local function worker(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 i 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 i 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
-- }}}
return setmetatable(raid, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,47 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local type = type
local tonumber = tonumber
local setmetatable = setmetatable
local string = { match = string.match }
local helpers = require("vicious.helpers")
-- }}}
-- Thermal: provides temperature levels of ACPI and coretemp thermal zones
-- vicious.widgets.thermal
local thermal = {}
-- {{{ Thermal widget type
local function worker(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},
["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 {data / zone[warg[2]].div}
else -- /proc/acpi "temperature: N C"
return {tonumber(string.match(data, "[%d]+"))}
end
end
return {0}
end
-- }}}
return setmetatable(thermal, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,36 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
-- * (c) 2009, Lucas de Vries <lucas@glacicle.com>
---------------------------------------------------
-- {{{ Grab environment
local setmetatable = setmetatable
local math = { floor = math.floor }
local string = { match = string.match }
local helpers = require("vicious.helpers")
-- }}}
-- Uptime: provides system uptime and load information
-- vicious.widgets.uptime
local uptime = {}
-- {{{ Uptime widget type
local function worker(format)
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
-- }}}
return setmetatable(uptime, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,54 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local tonumber = tonumber
local io = { popen = io.popen }
local setmetatable = setmetatable
local string = { match = string.match }
local helpers = require("vicious.helpers")
-- }}}
-- Volume: provides volume levels and state of requested ALSA mixers
-- vicious.widgets.volume
local volume = {}
-- {{{ 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("amixer -M get " .. helpers.shellquote(warg))
local mixer = f:read("*all")
f:close()
-- Capture mixer control state: [5%] ... ... [on]
local volu, mute = string.match(mixer, "([%d]+)%%.*%[([%l]*)")
-- Handle mixers without data
if volu == nil then
return {0, mixer_state["off"]}
end
-- Handle mixers without mute
if mute == "" and volu == "0"
-- Handle mixers that are muted
or mute == "off" then
mute = mixer_state["off"]
else
mute = mixer_state["on"]
end
return {tonumber(volu), mute}
end
-- }}}
return setmetatable(volume, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,94 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ Grab environment
local tonumber = tonumber
local io = { popen = io.popen }
local setmetatable = setmetatable
local math = { ceil = math.ceil }
local string = { match = string.match }
local helpers = require("vicious.helpers")
-- }}}
-- Weather: provides weather information for a requested station
-- vicious.widgets.weather
local weather = {}
-- Initialize function tables
local _weather = {
["{city}"] = "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"
}
-- {{{ Weather widget type
local function worker(format, warg)
if not warg then return end
-- Get weather forceast by the station ICAO code, from:
-- * US National Oceanic and Atmospheric Administration
local url = "http://weather.noaa.gov/pub/data/observations/metar/decoded/"..warg
local f = io.popen("curl --connect-timeout 1 -fsm 3 "..helpers.shellquote(url)..".TXT")
local ws = f:read("*all")
f:close()
-- Check if there was a timeout or a problem with the station
if ws == nil then return _weather end
_weather["{city}"] = -- City and/or area
string.match(ws, "^(.+)%,.*%([%u]+%)") or _weather["{city}"]
_weather["{wind}"] = -- Wind direction and degrees if available
string.match(ws, "Wind:[%s][%a]+[%s][%a]+[%s](.+)[%s]at.+$") or _weather["{wind}"]
_weather["{windmph}"] = -- Wind speed in MPH if available
string.match(ws, "Wind:[%s].+[%s]at[%s]([%d]+)[%s]MPH") or _weather["{windmph}"]
_weather["{sky}"] = -- Sky conditions if available
string.match(ws, "Sky[%s]conditions:[%s](.-)[%c]") or _weather["{sky}"]
_weather["{weather}"] = -- Weather conditions if available
string.match(ws, "Weather:[%s](.-)[%c]") or _weather["{weather}"]
_weather["{tempf}"] = -- Temperature in fahrenheit
string.match(ws, "Temperature:[%s]([%-]?[%d%.]+).*[%c]") or _weather["{tempf}"]
_weather["{dewf}"] = -- Dew Point in fahrenheit
string.match(ws, "Dew[%s]Point:[%s]([%-]?[%d%.]+).*[%c]") or _weather["{dewf}"]
_weather["{humid}"] = -- Relative humidity in percent
string.match(ws, "Relative[%s]Humidity:[%s]([%d]+)%%") or _weather["{humid}"]
_weather["{press}"] = -- Pressure in hPa
string.match(ws, "Pressure[%s].+%((.+)[%s]hPa%)") or _weather["{press}"]
-- 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
-- }}}
return setmetatable(weather, { __call = function(_, ...) return worker(...) end })

View File

@ -0,0 +1,92 @@
---------------------------------------------------
-- Licensed under the GNU General Public License v2
-- * (c) 2010, Adrian C. <anrxc@sysphere.org>
---------------------------------------------------
-- {{{ 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
-- vicious.widgets.wifi
local wifi = {}
-- {{{ Variable definitions
local iwconfig = "iwconfig"
local iwcpaths = { "/sbin", "/usr/sbin", "/usr/local/sbin", "/usr/bin" }
-- }}}
-- {{{ Wireless widget type
local function worker(format, warg)
if not warg then return end
-- Default values
local winfo = {
["{ssid}"] = "N/A",
["{mode}"] = "N/A",
["{chan}"] = 0,
["{rate}"] = 0,
["{link}"] = 0,
["{linp}"] = 0,
["{sign}"] = 0
}
-- Sbin paths aren't in user PATH, search for the binary
if iwconfig == "iwconfig" then
for _, p in ipairs(iwcpaths) do
local f = io.open(p .. "/iwconfig", "rb")
if f then
iwconfig = p .. "/iwconfig"
f:close()
break
end
end
end
-- Get data from iwconfig where available
local f = io.popen(iwconfig .." ".. helpers.shellquote(warg) .. " 2>&1")
local iw = f:read("*all")
f:close()
-- iwconfig wasn't found, isn't executable, or non-wireless interface
if iw == nil or string.find(iw, "No such device") then
return winfo
end
-- Output differs from system to system, some stats can be
-- separated by =, and not all drivers report all stats
winfo["{ssid}"] = -- SSID can have almost anything in it
helpers.escape(string.match(iw, 'ESSID[=:]"(.-)"') or winfo["{ssid}"])
winfo["{mode}"] = -- Modes are simple, but also match the "-" in Ad-Hoc
string.match(iw, "Mode[=:]([%w%-]*)") or winfo["{mode}"]
winfo["{chan}"] = -- Channels are plain digits
tonumber(string.match(iw, "Channel[=:]([%d]+)") or winfo["{chan}"])
winfo["{rate}"] = -- Bitrate can start with a space, we don't want to display Mb/s
tonumber(string.match(iw, "Bit Rate[=:]([%s]?[%d%.]*)") or winfo["{rate}"])
winfo["{link}"] = -- Link quality can contain a slash (32/70), match only the first number
tonumber(string.match(iw, "Link Quality[=:]([%d]+)") or winfo["{link}"])
winfo["{sign}"] = -- Signal level can be a negative value, don't display decibel notation
tonumber(string.match(iw, "Signal level[=:]([%-]?[%d]+)") or winfo["{sign}"])
-- Link quality percentage if quality was available
if winfo["{link}"] ~= 0 then winfo["{linp}"] = math.ceil(winfo["{link}"] / 0.7) end
return winfo
end
-- }}}
return setmetatable(wifi, { __call = function(_, ...) return worker(...) end })