From: trap000d Date: Thu, 8 Dec 2016 01:38:29 +0000 (+1300) Subject: Merge pull request #1 from copycat-killer/master X-Git-Url: https://git.madduck.net/etc/awesome.git/commitdiff_plain/b8adf932213faa797fcd69df28bb8bf9451cf5df?hp=8e91cc60b6b4f8e9d67138fdbd70c22cd5f85f0f Merge pull request #1 from copycat-killer/master Merge upstream --- diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..23cb790 --- /dev/null +++ b/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + {description} + Copyright (C) {year} {fullname} + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + {signature of Ty Coon}, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/README.rst b/README.rst index be3e377..87e11b5 100644 --- a/README.rst +++ b/README.rst @@ -43,5 +43,5 @@ Screenshots .. _GNU-GPL2: http://www.gnu.org/licenses/gpl-2.0.html .. _awesome-vain: https://github.com/vain/awesome-vain -.. _Awesome: http://awesome.naquadah.org/ +.. _Awesome: https://github.com/awesomeWM/awesome .. _wiki: https://github.com/copycat-killer/lain/wiki diff --git a/asyncshell.lua b/asyncshell.lua index a8e3676..61336db 100644 --- a/asyncshell.lua +++ b/asyncshell.lua @@ -52,7 +52,7 @@ function asyncshell.request(command, callback, timeout) id, formatted_command ) - if type(awful.spawn) == 'table' then + if type(awful.spawn) == 'table' and awful.spawn.with_shell then awful.spawn.with_shell(req) else awful.util.spawn_with_shell(req) diff --git a/helpers.lua b/helpers.lua index 8fb794c..6c0c3c4 100644 --- a/helpers.lua +++ b/helpers.lua @@ -8,6 +8,7 @@ local debug = require("debug") +local assert = assert local capi = { timer = (type(timer) == 'table' and timer or require ("gears.timer")) } local io = { open = io.open, lines = io.lines, @@ -61,9 +62,9 @@ end function helpers.lines_match(regexp, file) local lines = {} for index,line in pairs(helpers.lines_from(file)) do - if string.match(line, regexp) then + if string.match(line, regexp) then lines[index] = line - end + end end return lines end @@ -93,9 +94,9 @@ function helpers.newtimer(_name, timeout, fun, nostart) local name = timeout if not helpers.timer_table[name] then helpers.timer_table[name] = capi.timer({ timeout = timeout }) + helpers.timer_table[name]:start() end helpers.timer_table[name]:connect_signal("timeout", fun) - helpers.timer_table[name]:start() if not nostart then helpers.timer_table[name]:emit_signal("timeout") end @@ -105,7 +106,7 @@ end -- {{{ Pipe operations --- read the full output of a pipe (command) +-- read the full output of a command output function helpers.read_pipe(cmd) local f = assert(io.popen(cmd)) local output = f:read("*all") @@ -113,6 +114,16 @@ function helpers.read_pipe(cmd) return output end +-- return line iterator of a command output +function helpers.pipelines(...) + local f = assert(io.popen(...)) + return function () -- iterator + local data = f:read() + if data == nil then f:close() end + return data + end +end + -- }}} -- {{{ A map utility @@ -148,4 +159,5 @@ function helpers.spairs(t) end --}}} + return helpers diff --git a/lain-git.rockspec b/lain-git.rockspec new file mode 100644 index 0000000..b8d7abb --- /dev/null +++ b/lain-git.rockspec @@ -0,0 +1,28 @@ +package = "lain" +version = "git" +source = { + url = "https://github.com/copycat-killer/lain", + tag = "git" +} +description = { + summary = "Layout, widgets and utilities for Awesome WM", + detailed = [[ + Successor of awesome-vain, this module provides new layouts, a set of widgets and utility functions, in order to improve Awesome usability and configurability. + + Optional dependencies: alsa-utils (for alsamixer); curl; imagemagick. + ]], + homepage = "https://github.com/copycat-killer/lain", + license = "GPL v2" +} +dependencies = { + "lua >= 5.1", + "awesome >= 3.5", + "alsa-utils", + "curl", + "imagemagick" +} +supported_platforms = { "linux" } +build = { + type = "builtin", + modules = { lain = "init.lua" } +} diff --git a/scripts/dfs b/scripts/dfs index bc27b81..1de819a 100755 --- a/scripts/dfs +++ b/scripts/dfs @@ -11,10 +11,11 @@ # ------------------------------------------------------------------------- # Decoding options # ------------------------------------------------------------------------- -USAGE="Usage: $0 [-h(elp)] | [-n(arrow mode)] | [-w(eb output)]" +USAGE="Usage: $0 [-h(elp)] | [-n(arrow mode)] | [-w(eb output) | --type= | --exclude-type=]" NARROW_MODE=0 WEB_OUTPUT=0 +DF_OPTIONS="" while [ $# -gt 0 ]; do case "$1" in @@ -31,6 +32,12 @@ NARROW_MODE=1 "-w" ) WEB_OUTPUT=1 ;; +--type=*) +DF_OPTIONS+=" $1" +;; +--exclude-type=*) +DF_OPTIONS+=" $1" +;; * ) echo $USAGE exit @@ -58,6 +65,9 @@ AWK_COMMAND="/usr/bin/env gawk" ;; esac +# Add additional df options +DF_COMMAND+=$DF_OPTIONS + # ------------------------------------------------------------------------- # Grabbing "df" result # ------------------------------------------------------------------------- diff --git a/util/init.lua b/util/init.lua index 70d8e52..5fe8213 100644 --- a/util/init.lua +++ b/util/init.lua @@ -98,7 +98,7 @@ function util.mc(c) g.height = math.sqrt(mwfact) * mg.height g.x = mg.x + (mg.width - g.width) / 2 g.y = mg.y + (mg.height - g.height) / 2 - c:geometry(g) + if c then c:geometry(g) end -- if c is still a valid object end -- Read the nice value of pid from /proc. diff --git a/util/quake.lua b/util/quake.lua new file mode 100644 index 0000000..771741e --- /dev/null +++ b/util/quake.lua @@ -0,0 +1,162 @@ + +--[[ + + Licensed under GNU General Public License v2 + * (c) 2016, Luke Bonham + +--]] + +local awful = require("awful") +local capi = { client = client, + mouse = mouse, + screen = screen, + timer = timer } +local string = string + +local pairs = pairs +local setmetatable = setmetatable +local tostring = tostring + +-- Quake-like Dropdown application spawn +-- Original version: https://awesomewm.org/wiki/Drop-down_terminal#Another_solution +local quake = {} + +-- If you have a rule like "awful.client.setslave" for your terminals, +-- ensure you use an exception for QuakeDD. Otherwise, you may +-- run into problems with focus. + +function quake:display() + -- First, we locate the client + local client = nil + local i = 0 + for c in awful.client.iterate(function (c) + -- c.name may be changed! + return c.instance == self.name + end, nil, self.screen) + do + i = i + 1 + if i == 1 then + client = c + else + -- Additional matching clients, let's remove the sticky bit + -- which may persist between awesome restarts. We don't close + -- them as they may be valuable. They will just turn into + -- normal clients. + c.sticky = false + c.ontop = false + c.above = false + end + end + + if not client and not self.visible then return end + + if not client then + -- The client does not exist, we spawn it + awful.util.spawn(string.format("%s %s %s", self.app, + string.format(self.argname, self.name), self.extra), + false, self.screen) + self.notexist = true + return + end + + -- Resize + awful.client.floating.set(client, true) + client.border_width = self.border + client.size_hints_honor = false + if self.notexist then + client:geometry(self.geometry) + self.notexist = false + end + + -- Not sticky and on top + client.ontop = true + client.above = true + client.skip_taskbar = true + client.sticky = false + + -- Toggle display + if self.visible then + client.hidden = false + client:raise() + self.last_tag = tostring(awful.tag.selected(self.screen)) + client:tags({awful.tag.selected(self.screen)}) + capi.client.focus = client + else + client.hidden = true + local ctags = client:tags() + for i, t in pairs(ctags) do + ctags[i] = nil + end + client:tags(ctags) + end + + return client +end + +function quake:new(config) + local conf = config or {} + + conf.app = conf.app or "xterm" -- application to spawn + conf.name = conf.name or "QuakeDD" -- window name + conf.argname = conf.argname or "-name %s" -- how to specify window name + conf.extra = conf.extra or "" -- extra arguments + conf.visible = conf.visible or false -- initially not visible + conf.screen = conf.screen or capi.mouse.screen + conf.border = conf.border or 1 + + -- If width or height <= 1 this is a proportion of the workspace + wibox_height = conf.wibox_height or 18 -- statusbar weight + height = conf.height or 0.25 -- height + width = conf.width or 1 -- width + vert = conf.vert or "top" -- top, bottom or center + horiz = conf.horiz or "center" -- left, right or center + + -- Compute size + local geom = capi.screen[conf.screen].workarea + if width <= 1 then width = geom.width * width end + if height <= 1 then height = geom.height * height end + local x, y + if horiz == "left" then x = geom.x + elseif horiz == "right" then x = geom.width + geom.x - width + else x = geom.x + (geom.width - width)/2 end + if vert == "top" then y = geom.y + elseif vert == "bottom" then y = geom.height + geom.y - height + else y = geom.y + (geom.height - height)/2 end + conf.geometry = { x = x, y = y + wibox_height, width = width, height = height } + + local console = setmetatable(conf, { __index = quake }) + capi.client.connect_signal("manage", function(c) + if c.instance == console.name and c.screen == console.screen then + console:display() + end + end) + capi.client.connect_signal("unmanage", function(c) + if c.instance == console.name and c.screen == console.screen then + console.visible = false + end + end) + + -- "Reattach" currently running quake application. This is in case awesome is restarted. + local reattach = capi.timer { timeout = 0 } + reattach:connect_signal("timeout", function() + reattach:stop() + console:display() + end) + reattach:start() + + return console +end + +function quake:toggle() + current_tag = awful.tag.selected(self.screen) + if self.last_tag ~= tostring(current_tag) and self.visible then + awful.client.movetotag(current_tag, self:display()) + else + self.visible = not self.visible + self:display() + end +end + +setmetatable(quake, { __call = function(_, ...) return quake:new(...) end }) + +return quake diff --git a/widgets/alsa.lua b/widgets/alsa.lua index d85a080..7500097 100644 --- a/widgets/alsa.lua +++ b/widgets/alsa.lua @@ -26,14 +26,20 @@ local function worker(args) local timeout = args.timeout or 5 local settings = args.settings or function() end - alsa.cmd = args.cmd or "amixer" - alsa.channel = args.channel or "Master" - alsa.widget = wibox.widget.textbox('') + alsa.cmd = args.cmd or "amixer" + alsa.channel = args.channel or "Master" + alsa.togglechannel = args.togglechannel + alsa.widget = wibox.widget.textbox('') function alsa.update() mixer = read_pipe(string.format("%s get %s", alsa.cmd, alsa.channel)) l,s = string.match(mixer, "([%d]+)%%.*%[([%l]*)") + -- HDMIs can have a channel different from Master for toggling mute + if alsa.togglechannel then + s = string.match(read_pipe(string.format("%s get %s", alsa.cmd, alsa.togglechannel)), "%[(%a+)%]") + end + if alsa.last_level ~= l or alsa.last_status ~= s then volume_now = { level = l, status = s } alsa.last_level = l diff --git a/widgets/alsabar.lua b/widgets/alsabar.lua index 39bf394..b20bc40 100644 --- a/widgets/alsabar.lua +++ b/widgets/alsabar.lua @@ -27,7 +27,7 @@ local setmetatable = setmetatable -- lain.widgets.alsabar local alsabar = { channel = "Master", - step = "2%", + step = "1%", colors = { background = beautiful.bg_normal, @@ -104,6 +104,7 @@ local function worker(args) alsabar.cmd = args.cmd or "amixer" alsabar.channel = args.channel or alsabar.channel + alsabar.togglechannel = args.togglechannel alsabar.step = args.step or alsabar.step alsabar.colors = args.colors or alsabar.colors alsabar.notifications = args.notifications or alsabar.notifications @@ -127,9 +128,14 @@ local function worker(args) -- Capture mixer control state: [5%] ... ... [on] local volu, mute = string.match(mixer, "([%d]+)%%.*%[([%l]*)") + -- HDMIs can have a channel different from Master for toggling mute + if alsabar.togglechannel then + mute = string.match(read_pipe(string.format("%s get %s", alsabar.cmd, alsabar.togglechannel)), "%[(%a+)%]") + end + if (volu and tonumber(volu) ~= alsabar._current_level) or (mute and string.match(mute, "on") ~= alsabar._muted) then - alsabar._current_level = tonumber(volu) + alsabar._current_level = tonumber(volu) or alsabar._current_level alsabar.bar:set_value(alsabar._current_level / 100) if not mute and tonumber(volu) == 0 or mute == "off" then @@ -149,19 +155,23 @@ local function worker(args) end end - alsabar.bar:buttons (awful.util.table.join ( - awful.button ({}, 1, function() + alsabar.bar:buttons(awful.util.table.join ( + awful.button({}, 1, function() awful.util.spawn(alsabar.mixer) end), - awful.button ({}, 3, function() + awful.button({}, 2, function() + awful.util.spawn(string.format("%s set %s 100%%", alsabar.cmd, alsabar.channel)) + pulsebar.update() + end), + awful.button({}, 3, function() awful.util.spawn(string.format("%s set %s toggle", alsabar.cmd, alsabar.channel)) alsabar.update() end), - awful.button ({}, 4, function() + awful.button({}, 4, function() awful.util.spawn(string.format("%s set %s %s+", alsabar.cmd, alsabar.channel, alsabar.step)) alsabar.update() end), - awful.button ({}, 5, function() + awful.button({}, 5, function() awful.util.spawn(string.format("%s set %s %s-", alsabar.cmd, alsabar.channel, alsabar.step)) alsabar.update() end) diff --git a/widgets/base.lua b/widgets/base.lua index e1ce297..cd84e36 100644 --- a/widgets/base.lua +++ b/widgets/base.lua @@ -26,8 +26,8 @@ local function worker(args) base.widget = wibox.widget.textbox('') function base.update() + output = read_pipe(cmd) if output ~= base.prev then - output = read_pipe(cmd) widget = base.widget settings() base.prev = output diff --git a/widgets/bat.lua b/widgets/bat.lua index 3b9cca0..f63b1fa 100644 --- a/widgets/bat.lua +++ b/widgets/bat.lua @@ -13,10 +13,14 @@ local first_line = require("lain.helpers").first_line local naughty = require("naughty") local wibox = require("wibox") -local math = { floor = math.floor, min = math.min } +local math = { abs = math.abs, + floor = math.floor, + log10 = math.log10, + min = math.min } local string = { format = string.format } -local tonumber = tonumber +local type = type +local tonumber = tonumber local setmetatable = setmetatable -- Battery infos @@ -50,7 +54,7 @@ local function worker(args) } bat_now = { - status = "Not present", + status = "N/A", ac_status = "N/A", perc = "N/A", time = "N/A", @@ -58,28 +62,30 @@ local function worker(args) } bat_now.n_status = {} + bat_now.n_perc = {} for i = 1, #batteries do - bat_now.n_status[i] = "Not present" + bat_now.n_status[i] = "N/A" + bat_now.n_perc[i] = 0 end - function update() + function bat.update() local sum_rate_current = 0 local sum_rate_voltage = 0 - local sum_rate_power = 0 - local sum_energy_now = 0 - local sum_energy_full = 0 - local sum_energy_percentage = 0 + local sum_rate_power = 0 + local sum_rate_energy = 0 + local sum_energy_now = 0 + local sum_energy_full = 0 + local pspath = "/sys/class/power_supply/" for i, battery in ipairs(batteries) do - local bstr = "/sys/class/power_supply/" .. battery + local bstr = pspath .. battery local present = first_line(bstr .. "/present") - if present == "1" - then + if tonumber(present) == 1 then -- current_now(I)[uA], voltage_now(U)[uV], power_now(P)[uW] - local rate_current = tonumber(first_line(bstr .. "/current_now")) - local rate_voltage = tonumber(first_line(bstr .. "/voltage_now")) - local rate_power = tonumber(first_line(bstr .. "/power_now")) + local rate_current = tonumber(first_line(bstr .. "/current_now")) + local rate_voltage = tonumber(first_line(bstr .. "/voltage_now")) + local rate_power = tonumber(first_line(bstr .. "/power_now")) -- energy_now(P)[uWh], charge_now(I)[uAh] local energy_now = tonumber(first_line(bstr .. "/energy_now") or @@ -92,64 +98,74 @@ local function worker(args) local energy_percentage = tonumber(first_line(bstr .. "/capacity")) or math.floor((energy_now / energy_full) * 100) - if bat_now.n_status[i] ~= "Charging" and bat_now.n_status[i] ~= "Discharging" - then - bat_now.n_status[i] = first_line(bstr .. "/status") or "N/A" - end + bat_now.n_status[i] = first_line(bstr .. "/status") or "N/A" + bat_now.n_perc[i] = energy_percentage or bat_now.n_perc[i] - sum_rate_current = sum_rate_current + (rate_current or 0) - sum_rate_voltage = sum_rate_voltage + rate_voltage - sum_rate_power = sum_rate_power + (rate_power or ((rate_voltage * rate_current) / 1e6)) - sum_energy_now = sum_energy_now + energy_now - sum_energy_full = sum_energy_full + energy_full - sum_energy_percentage = sum_energy_percentage + energy_percentage + sum_rate_current = sum_rate_current + (rate_current or 0) + sum_rate_voltage = sum_rate_voltage + (rate_voltage or 0) + sum_rate_power = sum_rate_power + (rate_power or 0) + sum_rate_energy = sum_rate_energy + (rate_power or (((rate_voltage or 0) * (rate_current or 0)) / 1e6)) + sum_energy_now = sum_energy_now + (energy_now or 0) + sum_energy_full = sum_energy_full + (energy_full or 0) end end bat_now.status = bat_now.n_status[1] - bat_now.ac_status = first_line(string.format("/sys/class/power_supply/%s/online", ac)) or "N/A" + bat_now.ac_status = tonumber(first_line(string.format("%s%s/online", pspath, ac))) or "N/A" + + if bat_now.status ~= "N/A" then + -- update {perc,time,watt} iff battery not full and rate > 0 + if bat_now.status ~= "Full" and (sum_rate_power > 0 or sum_rate_current > 0) then + local rate_time = 0 + local div = (sum_rate_power > 0 and sum_rate_power) or sum_rate_current + + if bat_now.status == "Charging" then + rate_time = (sum_energy_full - sum_energy_now) / div + else -- Discharging + rate_time = sum_energy_now / div + end - -- update {perc,time,watt} iff rate > 0 and battery not full - if (sum_rate_current > 0 or sum_rate_power > 0) and not (bat_now.status == "Full") - then - local rate_time = 0 + if 0 < rate_time and rate_time < 0.01 then -- check for magnitude discrepancies (#199) + rate_time_magnitude = math.abs(math.floor(math.log10(rate_time))) + rate_time = rate_time * 10^(rate_time_magnitude - 2) + end - if bat_now.status == "Charging" then - rate_time = (sum_energy_full - sum_energy_now) / (sum_rate_power or sum_rate_current) - elseif bat_now.status == "Discharging" then - rate_time = sum_energy_now / (sum_rate_power or sum_rate_current) + local hours = math.floor(rate_time) + local minutes = math.floor((rate_time - hours) * 60) + bat_now.perc = math.floor(math.min(100, (sum_energy_now / sum_energy_full) * 100)) + bat_now.time = string.format("%02d:%02d", hours, minutes) + bat_now.watt = tonumber(string.format("%.2f", sum_rate_energy / 1e6)) + elseif bat_now.status ~= "Full" and sum_rate_power == 0 and bat_now.ac_status == 1 then + bat_now.perc = math.floor(math.min(100, (sum_energy_now / sum_energy_full) * 100)) + bat_now.time = "00:00" + bat_now.watt = 0 + elseif bat_now.status == "Full" then + bat_now.perc = 100 + bat_now.time = "00:00" + bat_now.watt = 0 end - - local hours = math.floor(rate_time) - local minutes = math.floor((rate_time - hours) * 60) - local watt = sum_rate_power / 1e6 - - bat_now.perc = string.format("%d", math.min(100, sum_energy_percentage / #batteries)) - bat_now.time = string.format("%02d:%02d", hours, minutes) - bat_now.watt = string.format("%.2fW", watt) end widget = bat.widget settings() -- notifications for low and critical states - if bat_now.status == "Discharging" and notify == "on" and bat_now.perc then - local nperc = tonumber(bat_now.perc) or 100 - if nperc <= 5 then + if notify == "on" and type(bat_now.perc) == "number" and bat_now.status == "Discharging" then + if bat_now.perc <= 5 then bat.id = naughty.notify({ preset = bat_notification_critical_preset, - replaces_id = bat.id, + replaces_id = bat.id }).id - elseif nperc <= 15 then + elseif bat_now.perc <= 15 then bat.id = naughty.notify({ preset = bat_notification_low_preset, - replaces_id = bat.id, + replaces_id = bat.id }).id end end end - newtimer(battery, timeout, update) + newtimer(battery, timeout, bat.update) return setmetatable(bat, { __index = bat.widget }) end diff --git a/widgets/calendar.lua b/widgets/calendar.lua index 1a481ca..1728dfc 100644 --- a/widgets/calendar.lua +++ b/widgets/calendar.lua @@ -27,15 +27,15 @@ local setmetatable = setmetatable local calendar = {} local cal_notification = nil -function calendar:hide() +function calendar.hide() if cal_notification ~= nil then naughty.destroy(cal_notification) cal_notification = nil end end -function calendar:show(t_out, inc_offset, scr) - calendar:hide() +function calendar.show(t_out, inc_offset, scr) + calendar.hide() local f, c_text local offs = inc_offset or 0 @@ -96,7 +96,7 @@ function calendar:show(t_out, inc_offset, scr) }) end -function calendar:attach(widget, args) +function calendar.attach(widget, args) local args = args or {} calendar.cal = args.cal or "/usr/bin/cal" @@ -116,16 +116,16 @@ function calendar:attach(widget, args) calendar.offset = 0 calendar.notify_icon = nil - widget:connect_signal("mouse::enter", function () calendar:show(0, 0, calendar.scr_pos) end) - widget:connect_signal("mouse::leave", function () calendar:hide() end) + widget:connect_signal("mouse::enter", function () calendar.show(0, 0, calendar.scr_pos) end) + widget:connect_signal("mouse::leave", function () calendar.hide() end) widget:buttons(awful.util.table.join(awful.button({ }, 1, function () - calendar:show(0, -1, calendar.scr_pos) end), + calendar.show(0, -1, calendar.scr_pos) end), awful.button({ }, 3, function () - calendar:show(0, 1, calendar.scr_pos) end), + calendar.show(0, 1, calendar.scr_pos) end), awful.button({ }, 4, function () - calendar:show(0, -1, calendar.scr_pos) end), + calendar.show(0, -1, calendar.scr_pos) end), awful.button({ }, 5, function () - calendar:show(0, 1, calendar.scr_pos) end))) + calendar.show(0, 1, calendar.scr_pos) end))) end return setmetatable(calendar, { __call = function(_, ...) return create(...) end }) diff --git a/widgets/contrib/ccurr.lua b/widgets/contrib/ccurr.lua deleted file mode 100644 index 980e19b..0000000 --- a/widgets/contrib/ccurr.lua +++ /dev/null @@ -1,82 +0,0 @@ - ---[[ - - Licensed under GNU General Public License v2 - * (c) 2014, Aaron Lebo - ---]] - -local newtimer = require("lain.helpers").newtimer -local json = require("lain.util").dkjson - -local wibox = require("wibox") - -local string = { format = string.format } -local tonumber = tonumber - --- Crypto currencies widget --- lain.widgets.contrib.ccurr -local ccurr = {} - --- Currently gets --- * BTC/USD --- * DOGE/USD --- using Coinbase and Cryptsy APIs. - --- requires http://dkolf.de/src/dkjson-lua.fsl/home --- based upon http://awesome.naquadah.org/wiki/Bitcoin_Price_Widget - -local function get(url) - local f = io.popen('curl -m 5 -s "' .. url .. '"') - if not f then - return 0 - else - local s = f:read("*all") - f:close() - return s - end -end - -local function parse(j) - local obj, pos, err = json.decode(j, 1, nil) - if err then - return nil - else - return obj - end -end - -local function worker(args) - local args = args or {} - local timeout = args.timeout or 600 - local btc_url = args.btc_url or "https://coinbase.com/api/v1/prices/buy" - local doge_url = args.doge_url or "http://pubapi.cryptsy.com/api.php?method=singlemarketdata&marketid=132" - local settings = args.settings or function() end - - ccurr.widget = wibox.widget.textbox('') - - local function update() - price_now = { - btc = "N/A", - doge = "N/A" - } - - btc = parse(get(btc_url)) - doge = parse(get(doge_url)) - - if btc and doge then - price_now.btc = tonumber(btc["subtotal"]["amount"]) - price_now.doge = tonumber(doge["return"]["markets"]["DOGE"]["lasttradeprice"]) - price_now.doge = string.format("%.4f", price_now.btc * price_now.doge) - end - - widget = ccurr.widget - settings() - end - - newtimer("ccurr", timeout, update) - - return ccurr.widget -end - -return setmetatable(ccurr, { __call = function(_, ...) return worker(...) end }) diff --git a/widgets/contrib/gpmdp.lua b/widgets/contrib/gpmdp.lua new file mode 100644 index 0000000..fa51440 --- /dev/null +++ b/widgets/contrib/gpmdp.lua @@ -0,0 +1,95 @@ + +--[[ + + Licensed under GNU General Public License v2 + * (c) 2016, Alexandre Terrien + +--]] + +local helpers = require("lain.helpers") +local json = require("lain.util.dkjson") +local pread = require("awful.util").pread +local naughty = require("naughty") +local wibox = require("wibox") +local mouse = mouse +local os = { getenv = os.getenv } + +local setmetatable = setmetatable + +-- Google Play Music Desktop infos +-- lain.widget.contrib.gpmdp +local gpmdp = {} + +local function worker(args) + local args = args or {} + local timeout = args.timeout or 2 + local notify = args.notify or "off" + local followmouse = args.followmouse or false + local file_location = args.file_location or + os.getenv("HOME") .. "/.config/Google Play Music Desktop Player/json_store/playback.json" + local settings = args.settings or function() end + + gpmdp.widget = wibox.widget.textbox('') + + gpmdp_notification_preset = { + title = "Now playing", + timeout = 6 + } + + helpers.set_map("gpmdp_current", nil) + + function gpmdp.update() + file, err = io.open(file_location, "r") + if not file + then + gpm_now = { running = false, playing = false } + else + dict, pos, err = json.decode(file:read "*a", 1, nil) + file:close() + gpm_now = {} + gpm_now.artist = dict.song.artist + gpm_now.album = dict.song.album + gpm_now.title = dict.song.title + gpm_now.cover_url = dict.song.albumArt + gpm_now.playing = dict.playing + end + + if (pread("pidof 'Google Play Music Desktop Player'") ~= '') then + gpm_now.running = true + else + gpm_now.running = false + end + + gpmdp_notification_preset.text = string.format("%s (%s) - %s", gpm_now.artist, gpm_now.album, gpm_now.title) + widget = gpmdp.widget + settings() + + if gpm_now.playing + then + if notify == "on" and gpm_now.title ~= helpers.get_map("gpmdp_current") + then + helpers.set_map("gpmdp_current", gpm_now.title) + os.execute("curl " .. gpm_now.cover_url .. " -o /tmp/gpmcover.png") + + if followmouse then + gpmdp_notification_preset.screen = mouse.screen + end + + gpmdp.id = naughty.notify({ + preset = gpmdp_notification_preset, + icon = "/tmp/gpmcover.png", + replaces_id = gpmdp.id, + }).id + end + elseif not gpm_now.running + then + helpers.set_map("gpmdp_current", nil) + end + end + + helpers.newtimer("gpmdp", timeout, gpmdp.update) + + return setmetatable(gpmdp, { __index = gpmdp.widget }) +end + +return setmetatable(gpmdp, { __call = function(_, ...) return worker(...) end }) diff --git a/widgets/contrib/kbdlayout.lua b/widgets/contrib/kbdlayout.lua index 64ea6b3..23dc6e3 100644 --- a/widgets/contrib/kbdlayout.lua +++ b/widgets/contrib/kbdlayout.lua @@ -19,7 +19,7 @@ local setmetatable = setmetatable -- Keyboard layout switcher -- lain.widgets.contrib.kblayout -local function worker (args) +local function worker(args) local kbdlayout = {} kbdlayout.widget = wibox.widget.textbox('') @@ -36,21 +36,21 @@ local function worker (args) awful.button({ }, 1, function () kbdlayout.next() end), awful.button({ }, 3, function () kbdlayout.prev() end))) - local function run_settings (layout, variant) + local function run_settings(layout, variant) widget = kbdlayout.widget kbdlayout_now = { layout=string.match(layout, "[^,]+"), -- Make sure to match the primary layout only. variant=variant } settings() end - function kbdlayout.update () + function kbdlayout.update() local status = read_pipe('setxkbmap -query') run_settings(string.match(status, "layout:%s*([^\n]*)"), string.match(status, "variant:%s*([^\n]*)")) end - function kbdlayout.set (i) + function kbdlayout.set(i) idx = ((i - 1) % #layouts) + 1 -- Make sure to wrap around as needed. local to_execute = 'setxkbmap ' .. layouts[idx].layout @@ -67,11 +67,11 @@ local function worker (args) end end - function kbdlayout.next () + function kbdlayout.next() kbdlayout.set(idx + 1) end - function kbdlayout.prev () + function kbdlayout.prev() kbdlayout.set(idx - 1) end diff --git a/widgets/contrib/redshift.lua b/widgets/contrib/redshift.lua index 5ed9300..69247ee 100644 --- a/widgets/contrib/redshift.lua +++ b/widgets/contrib/redshift.lua @@ -16,11 +16,10 @@ local setmetatable = setmetatable -- lain.widgets.contrib.redshift local redshift = {} -local attached = false -- true if attached to a widget -local active = false -- true if redshift is active -local running = false -- true if redshift was initialized -local update_fnct = function() end -- Function that is run each time redshift is toggled. See redshift:attach(). - +local attached = false -- true if attached to a widget +local active = false -- true if redshift is active +local running = false -- true if redshift was initialized +local update_fnct = function() end -- Function that is run each time redshift is toggled. See redshift:attach(). local function init() -- As there is no way to determine if redshift was previously diff --git a/widgets/contrib/task.lua b/widgets/contrib/task.lua index 6f131e2..fc438d9 100644 --- a/widgets/contrib/task.lua +++ b/widgets/contrib/task.lua @@ -25,29 +25,34 @@ local task = {} local task_notification = nil -function task:hide() +function findLast(haystack, needle) + local i=haystack:match(".*"..needle.."()") + if i==nil then return nil else return i-1 end +end + +function task.hide() if task_notification ~= nil then naughty.destroy(task_notification) task_notification = nil end end -function task:show(scr_pos) - task:hide() +function task.show(scr_pos) + task.hide() - local f, c_text + local f, c_text, scrp if task.followmouse then - local scrp = mouse.screen + scrp = mouse.screen else - local scrp = scr_pos or task.scr_pos + scrp = scr_pos or task.scr_pos end - f = io.popen('task') + f = io.popen('task ' .. task.cmdline) c_text = "" - .. f:read("*all"):gsub("\n*$", "") + .. awful.util.escape(f:read("*all"):gsub("\n*$", "")) .. "" f:close() @@ -62,7 +67,7 @@ function task:show(scr_pos) }) end -function task:prompt_add() +function task.prompt_add() awful.prompt.run({ prompt = "Add task: " }, mypromptbox[mouse.screen].widget, function (...) @@ -70,7 +75,7 @@ function task:prompt_add() c_text = "\n" - .. f:read("*all") + .. awful.util.escape(f:read("*all")) .. "" f:close() @@ -87,7 +92,7 @@ function task:prompt_add() awful.util.getdir("cache") .. "/history_task_add") end -function task:prompt_search() +function task.prompt_search() awful.prompt.run({ prompt = "Search task: " }, mypromptbox[mouse.screen].widget, function (...) @@ -102,7 +107,7 @@ function task:prompt_search() c_text = "" - .. c_text + .. awful.util.escape(c_text) .. "" end @@ -121,24 +126,25 @@ function task:prompt_search() awful.util.getdir("cache") .. "/history_task") end -function task:attach(widget, args) +function task.attach(widget, args) local args = args or {} task.font_size = tonumber(args.font_size) or 12 - task.font = beautiful.font:sub(beautiful.font:find(""), - beautiful.font:find(" ")) + task.font = args.font or beautiful.font:sub(beautiful.font:find(""), + findLast(beautiful.font, " ")) task.fg = args.fg or beautiful.fg_normal or "#FFFFFF" task.bg = args.bg or beautiful.bg_normal or "#FFFFFF" task.position = args.position or "top_right" task.timeout = args.timeout or 7 task.scr_pos = args.scr_pos or 1 task.followmouse = args.followmouse or false + task.cmdline = args.cmdline or "next" task.notify_icon = icons_dir .. "/taskwarrior/task.png" task.notify_icon_small = icons_dir .. "/taskwarrior/tasksmall.png" - widget:connect_signal("mouse::enter", function () task:show(task.scr_pos) end) - widget:connect_signal("mouse::leave", function () task:hide() end) + widget:connect_signal("mouse::enter", function () task.show(task.scr_pos) end) + widget:connect_signal("mouse::leave", function () task.hide() end) end return setmetatable(task, { __call = function(_, ...) return create(...) end }) diff --git a/widgets/contrib/tpbat/smapi.lua b/widgets/contrib/tpbat/smapi.lua index c7f093e..271d9c2 100644 --- a/widgets/contrib/tpbat/smapi.lua +++ b/widgets/contrib/tpbat/smapi.lua @@ -16,7 +16,6 @@ local tonumber = tonumber local setmetatable = setmetatable local smapi = {} - local apipath = "/sys/devices/platform/smapi" -- Most are readable values, but some can be written to (not implemented, yet?) diff --git a/widgets/fs.lua b/widgets/fs.lua index a1d5d95..6038746 100644 --- a/widgets/fs.lua +++ b/widgets/fs.lua @@ -28,26 +28,29 @@ local setmetatable = setmetatable local fs = {} local fs_notification = nil -function fs:hide() +function fs.hide() if fs_notification ~= nil then naughty.destroy(fs_notification) fs_notification = nil end end -function fs:show(t_out) - fs:hide() +function fs.show(seconds, options, scr) + fs.hide() - local ws = helpers.read_pipe(helpers.scripts_dir .. "dfs"):gsub("\n*$", "") + local cmd = (options and string.format("dfs %s", options)) or "dfs" + local ws = helpers.read_pipe(helpers.scripts_dir .. cmd):gsub("\n*$", "") if fs.followmouse then fs.notification_preset.screen = mouse.screen + elseif scr then + fs.notification_preset.screen = scr end fs_notification = naughty.notify({ preset = fs.notification_preset, text = ws, - timeout = t_out + timeout = seconds or 5 }) end @@ -58,6 +61,8 @@ local function worker(args) local args = args or {} local timeout = args.timeout or 600 local partition = args.partition or "/" + local showpopup = args.showpopup or "on" + local notify = args.notify or "on" local settings = args.settings or function() end fs.followmouse = args.followmouse or false @@ -92,10 +97,11 @@ local function worker(args) fs_now.size_mb = tonumber(fs_info[partition .. " size_mb"]) or 0 fs_now.size_gb = tonumber(fs_info[partition .. " size_gb"]) or 0 + notification_preset = fs.notification_preset widget = fs.widget settings() - if fs_now.used >= 99 and not helpers.get_map(partition) + if notify == "on" and fs_now.used >= 99 and not helpers.get_map(partition) then naughty.notify({ title = "warning", @@ -110,8 +116,10 @@ local function worker(args) end end - fs.widget:connect_signal('mouse::enter', function () fs:show(0) end) - fs.widget:connect_signal('mouse::leave', function () fs:hide() end) + if showpopup == "on" then + fs.widget:connect_signal('mouse::enter', function () fs:show(0) end) + fs.widget:connect_signal('mouse::leave', function () fs:hide() end) + end helpers.newtimer(partition, timeout, update) diff --git a/widgets/imap.lua b/widgets/imap.lua index ea763df..de2f7b4 100644 --- a/widgets/imap.lua +++ b/widgets/imap.lua @@ -41,8 +41,7 @@ local function worker(args) helpers.set_map(mail, 0) - if not is_plain - then + if not is_plain then password = helpers.read_pipe(password):gsub("\n", "") end diff --git a/widgets/maildir.lua b/widgets/maildir.lua index c9937b4..e963fc6 100644 --- a/widgets/maildir.lua +++ b/widgets/maildir.lua @@ -39,8 +39,7 @@ local function worker(args) maildir.widget = wibox.widget.textbox('') function update() - if ext_mail_cmd ~= nil - then + if ext_mail_cmd then awful.util.spawn(ext_mail_cmd) end @@ -75,6 +74,7 @@ local function worker(args) p:close() newmail = "no mail" + -- Count the total number of mails irrespective of where it was found total = 0 diff --git a/widgets/mem.lua b/widgets/mem.lua index f6213b2..aede803 100644 --- a/widgets/mem.lua +++ b/widgets/mem.lua @@ -7,15 +7,15 @@ --]] -local newtimer = require("lain.helpers").newtimer +local newtimer = require("lain.helpers").newtimer -local wibox = require("wibox") +local wibox = require("wibox") -local io = { lines = io.lines } -local math = { floor = math.floor } -local string = { gmatch = string.gmatch } +local io = { lines = io.lines } +local math = { floor = math.floor } +local string = { gmatch = string.gmatch } -local setmetatable = setmetatable +local setmetatable = setmetatable -- Memory usage (ignoring caches) -- lain.widgets.mem @@ -46,6 +46,7 @@ local function worker(args) mem_now.used = mem_now.total - (mem_now.free + mem_now.buf + mem_now.cache) mem_now.swapused = mem_now.swap - mem_now.swapf + mem_now.perc = math.floor(mem_now.used / mem_now.total * 100) widget = mem.widget settings() diff --git a/widgets/mpd.lua b/widgets/mpd.lua index 1972050..8568764 100644 --- a/widgets/mpd.lua +++ b/widgets/mpd.lua @@ -14,13 +14,13 @@ local escape_f = require("awful.util").escape local naughty = require("naughty") local wibox = require("wibox") -local os = { execute = os.execute, - getenv = os.getenv } -local math = { floor = math.floor } +local os = { execute = os.execute, + getenv = os.getenv } +local math = { floor = math.floor } local mouse = mouse -local string = { format = string.format, - match = string.match, - gmatch = string.gmatch } +local string = { format = string.format, + match = string.match, + gmatch = string.gmatch } local setmetatable = setmetatable @@ -37,6 +37,7 @@ local function worker(args) local music_dir = args.music_dir or os.getenv("HOME") .. "/Music" local cover_size = args.cover_size or 100 local default_art = args.default_art or "" + local notify = args.notify or "on" local followmouse = args.followmouse or false local echo_cmd = args.echo_cmd or "echo" local settings = args.settings or function() end @@ -57,28 +58,40 @@ local function worker(args) function mpd.update() async.request(echo .. " | curl --connect-timeout 1 -fsm 3 " .. mpdh, function (f) mpd_now = { - state = "N/A", - file = "N/A", - name = "N/A", - artist = "N/A", - title = "N/A", - album = "N/A", - date = "N/A", - time = "N/A", - elapsed = "N/A" + random_mode = false, + single_mode = false, + repeat_mode = false, + consume_mode = false, + pls_pos = "N/A", + pls_len = "N/A", + state = "N/A", + file = "N/A", + name = "N/A", + artist = "N/A", + title = "N/A", + album = "N/A", + date = "N/A", + time = "N/A", + elapsed = "N/A" } for line in string.gmatch(f, "[^\n]+") do for k, v in string.gmatch(line, "([%w]+):[%s](.*)$") do - if k == "state" then mpd_now.state = v - elseif k == "file" then mpd_now.file = v - elseif k == "Name" then mpd_now.name = escape_f(v) - elseif k == "Artist" then mpd_now.artist = escape_f(v) - elseif k == "Title" then mpd_now.title = escape_f(v) - elseif k == "Album" then mpd_now.album = escape_f(v) - elseif k == "Date" then mpd_now.date = escape_f(v) - elseif k == "Time" then mpd_now.time = v - elseif k == "elapsed" then mpd_now.elapsed = string.match(v, "%d+") + if k == "state" then mpd_now.state = v + elseif k == "file" then mpd_now.file = v + elseif k == "Name" then mpd_now.name = escape_f(v) + elseif k == "Artist" then mpd_now.artist = escape_f(v) + elseif k == "Title" then mpd_now.title = escape_f(v) + elseif k == "Album" then mpd_now.album = escape_f(v) + elseif k == "Date" then mpd_now.date = escape_f(v) + elseif k == "Time" then mpd_now.time = v + elseif k == "elapsed" then mpd_now.elapsed = string.match(v, "%d+") + elseif k == "song" then mpd_now.pls_pos = v + elseif k == "playlistlength" then mpd_now.pls_len = v + elseif k == "repeat" then mpd_now.repeat_mode = v ~= "0" + elseif k == "single" then mpd_now.single_mode = v ~= "0" + elseif k == "random" then mpd_now.random_mode = v ~= "0" + elseif k == "consume" then mpd_now.consume_mode = v ~= "0" end end end @@ -90,7 +103,7 @@ local function worker(args) if mpd_now.state == "play" then - if mpd_now.title ~= helpers.get_map("current mpd track") + if notify == "on" and mpd_now.title ~= helpers.get_map("current mpd track") then helpers.set_map("current mpd track", mpd_now.title) diff --git a/widgets/net.lua b/widgets/net.lua index ee2cfa7..1883168 100644 --- a/widgets/net.lua +++ b/widgets/net.lua @@ -128,12 +128,12 @@ local function worker(args) net_now.sent = string.gsub(string.format('%.1f', net_now.sent), ',', '.') net_now.received = string.gsub(string.format('%.1f', net_now.received), ',', '.') - widget = net.widget - settings() - net.last_t = total_t net.last_r = total_r end + + widget = net.widget + settings() end helpers.newtimer(iface, timeout, update) diff --git a/widgets/pulseaudio.lua b/widgets/pulseaudio.lua index 17fdb9c..15bea8b 100644 --- a/widgets/pulseaudio.lua +++ b/widgets/pulseaudio.lua @@ -6,14 +6,15 @@ --]] -local read_pipe = require("lain.helpers").read_pipe -local newtimer = require("lain.helpers").newtimer -local wibox = require("wibox") +local read_pipe = require("lain.helpers").read_pipe +local newtimer = require("lain.helpers").newtimer +local wibox = require("wibox") -local string = { match = string.match, - format = string.format } +local string = { gmatch = string.gmatch, + match = string.match, + format = string.format } -local setmetatable = setmetatable +local setmetatable = setmetatable -- PulseAudio volume -- lain.widgets.pulseaudio @@ -25,7 +26,7 @@ local function worker(args) local settings = args.settings or function() end local scallback = args.scallback - pulseaudio.cmd = args.cmd or string.format("pacmd list-sinks | sed -n -e '0,/*/d' -e '/base volume/d' -e '/volume:/p' -e '/muted:/p'") + pulseaudio.cmd = args.cmd or string.format("pacmd list-sinks | sed -n -e '0,/*/d' -e '/base volume/d' -e '/volume:/p' -e '/muted:/p' -e '/device\\.string/p'") pulseaudio.widget = wibox.widget.textbox('') function pulseaudio.update() @@ -33,9 +34,19 @@ local function worker(args) local s = read_pipe(pulseaudio.cmd) volume_now = {} - volume_now.left = tonumber(string.match(s, ":.-(%d+)%%")) - volume_now.right = tonumber(string.match(s, ":.-(%d+)%%")) - volume_now.muted = string.match(s, "muted: (%S+)") + volume_now.index = string.match(s, "index: (%S+)") or "N/A" + volume_now.sink = string.match(s, "device.string = \"(%S+)\"") or "N/A" + volume_now.muted = string.match(s, "muted: (%S+)") or "N/A" + + local ch = 1 + volume_now.channel = {} + for v in string.gmatch(s, ":.-(%d+)%%") do + volume_now.channel[ch] = v + ch = ch + 1 + end + + volume_now.left = volume_now.channel[1] or "N/A" + volume_now.right = volume_now.channel[2] or "N/A" widget = pulseaudio.widget settings() diff --git a/widgets/pulsebar.lua b/widgets/pulsebar.lua index 5004be6..7f22642 100644 --- a/widgets/pulsebar.lua +++ b/widgets/pulsebar.lua @@ -27,6 +27,7 @@ local setmetatable = setmetatable -- lain.widgets.pulsebar local pulsebar = { sink = 0, + step = "1%", colors = { background = beautiful.bg_normal, @@ -105,6 +106,7 @@ local function worker(args) pulsebar.colors = args.colors or pulsebar.colors pulsebar.notifications = args.notifications or pulsebar.notifications pulsebar.sink = args.sink or 0 + pulsebar.step = args.step or pulsebar.step pulsebar.followmouse = args.followmouse or false pulsebar.bar = awful.widget.progressbar() @@ -148,10 +150,26 @@ local function worker(args) end end - pulsebar.bar:buttons (awful.util.table.join ( - awful.button ({}, 1, function() + pulsebar.bar:buttons(awful.util.table.join ( + awful.button({}, 1, function() awful.util.spawn(pulsebar.mixer) - end) + end), + awful.button({}, 2, function() + awful.util.spawn(string.format("pactl set-sink-volume %d 100%%", pulsebar.sink)) + pulsebar.update() + end), + awful.button({}, 3, function() + awful.util.spawn(string.format("pactl set-sink-mute %d toggle", pulsebar.sink)) + pulsebar.update() + end), + awful.button({}, 4, function() + awful.util.spawn(string.format("pactl set-sink-volume %d +%s", pulsebar.sink, pulsebar.step)) + pulsebar.update() + end), + awful.button({}, 5, function() + awful.util.spawn(string.format("pactl set-sink-volume %d -%s", pulsebar.sink, pulsebar.step)) + pulsebar.update() + end) )) timer_id = string.format("pulsebar-%s", pulsebar.sink) diff --git a/widgets/weather.lua b/widgets/weather.lua index d2ed300..545e6d1 100644 --- a/widgets/weather.lua +++ b/widgets/weather.lua @@ -108,7 +108,7 @@ local function worker(args) local pos, err weather_now, pos, err = json.decode(f, 1, nil) - if not err and weather_now and tonumber(weather_now["cod"]) == 200 then + if not err and type(weather_now) == "table" and tonumber(weather_now["cod"]) == 200 then weather.notification_text = '' for i = 1, weather_now["cnt"] do weather.notification_text = weather.notification_text .. @@ -128,7 +128,7 @@ local function worker(args) local pos, err, icon weather_now, pos, err = json.decode(f, 1, nil) - if not err and weather_now and tonumber(weather_now["cod"]) == 200 then + if not err and type(weather_now) == "table" and tonumber(weather_now["cod"]) == 200 then -- weather icon based on localtime local now = os.time() local sunrise = tonumber(weather_now["sys"]["sunrise"]) diff --git a/wiki b/wiki index 25dd1a2..d0df450 160000 --- a/wiki +++ b/wiki @@ -1 +1 @@ -Subproject commit 25dd1a2ec44da832d06ded29f393d716e4b54783 +Subproject commit d0df450d05655c5d8f724c42dc6b5d18b3676a60