]> git.madduck.net Git - etc/awesome.git/commitdiff

madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

All patches and comments are welcome. Please squash your changes to logical commits before using git-format-patch and git-send-email to patches@git.madduck.net. If you'd read over the Git project's submission guidelines and adhered to them, I'd be especially grateful.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

Merge pull request #1 from copycat-killer/master
authortrap000d <trap000d@gmail.com>
Thu, 8 Dec 2016 01:38:29 +0000 (14:38 +1300)
committerGitHub <noreply@github.com>
Thu, 8 Dec 2016 01:38:29 +0000 (14:38 +1300)
Merge upstream

29 files changed:
LICENSE [new file with mode: 0644]
README.rst
asyncshell.lua
helpers.lua
lain-git.rockspec [new file with mode: 0644]
scripts/dfs
util/init.lua
util/quake.lua [new file with mode: 0644]
widgets/alsa.lua
widgets/alsabar.lua
widgets/base.lua
widgets/bat.lua
widgets/calendar.lua
widgets/contrib/ccurr.lua [deleted file]
widgets/contrib/gpmdp.lua [new file with mode: 0644]
widgets/contrib/kbdlayout.lua
widgets/contrib/redshift.lua
widgets/contrib/task.lua
widgets/contrib/tpbat/smapi.lua
widgets/fs.lua
widgets/imap.lua
widgets/maildir.lua
widgets/mem.lua
widgets/mpd.lua
widgets/net.lua
widgets/pulseaudio.lua
widgets/pulsebar.lua
widgets/weather.lua
wiki

diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
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., <http://fsf.org/>
+ 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.
index be3e37792665f99b39496d99d9b463bee9398bc3..87e11b599abf85028e0a2eebddae8a89f3d4d228 100644 (file)
@@ -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
index a8e3676017168038797a9b28db8aeacec3299172..61336db5920ba705e94d4d746e637ff4e62805fd 100644 (file)
@@ -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)
index 8fb794c1f0e1cb790d02f65e27fb80825ff85b53..6c0c3c46ec155fc8fbc485186567f2f5874ca171 100644 (file)
@@ -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 (file)
index 0000000..b8d7abb
--- /dev/null
@@ -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" }
+}
index bc27b816a552fffcc17ff4b716dd2bdf2f129037..1de819a94ba2813867b8fb2d19197fafc8e0a871 100755 (executable)
 # -------------------------------------------------------------------------
 #   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=<fstype> | --exclude-type=<fstype>]"
 
 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
 # -------------------------------------------------------------------------
index 70d8e52aa717f27b493a73557a4e3ab100a29413..5fe8213dfae623bc906cefde22eb3ad137810cd9 100644 (file)
@@ -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 (file)
index 0000000..771741e
--- /dev/null
@@ -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
index d85a0802e81d4a39ec83237c46409a74cf1bfd20..7500097b05eac9e0cc3bcc695d9f4b44551e5522 100644 (file)
@@ -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
index 39bf39460234c1bf62fea20204a42b986058e5d4..b20bc4025b1287712bb1929e182aa7e7ebb2481f 100644 (file)
@@ -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)
index e1ce297f93b303938c3c33cb1b6dbdc5f2d6223f..cd84e361220f79f2d24c98d3a1e5fe198437ddce 100644 (file)
@@ -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
index 3b9cca07bc290d0854dd5f4b92ff6319eb1637bc..f63b1fa2700644e1ce7e695f7cab2c2bc56daee5 100644 (file)
@@ -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
index 1a481cab64243a792c9c81fbabedf543d8244744..1728dfc2aad3ebd76bf8c0cfc0c6ce124887256d 100644 (file)
@@ -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 (file)
index 980e19b..0000000
+++ /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 (file)
index 0000000..fa51440
--- /dev/null
@@ -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 })
index 64ea6b30fa83b93bd059d5c47f9ad1771abfc1da..23dc6e3cb975b15e34c7f93883ab045478559de3 100644 (file)
@@ -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
 
index 5ed930026d23fcb210e315f3773af1e2ee53b5a8..69247ee7716b2a55038e9630d41fc53db39d99ec 100644 (file)
@@ -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
index 6f131e216fe4f925deecc5b7ac65771688959fb1..fc438d9bfce40fc7534a91765473391657e77197 100644 (file)
@@ -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 = "<span font='"
              .. task.font .. " "
              .. task.font_size .. "'>"
-             .. f:read("*all"):gsub("\n*$", "")
+             .. awful.util.escape(f:read("*all"):gsub("\n*$", ""))
              .. "</span>"
     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<span font='"
                    .. task.font .. " "
                    .. task.font_size .. "'>"
-                   .. f:read("*all")
+                   .. awful.util.escape(f:read("*all"))
                    .. "</span>"
           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 = "<span font='"
                        .. task.font .. " "
                        .. task.font_size .. "'>"
-                       .. c_text
+                       .. awful.util.escape(c_text)
                        .. "</span>"
           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 })
index c7f093e1a6b58cc96993e38475664c8dea79f2c4..271d9c2d4c721223ddaef53729140708ab2808c5 100644 (file)
@@ -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?)
index a1d5d95beaa896362f403a59f8c2b81cf7ce77c7..6038746cc0d797c48c18d00841ebfe3dbae0de4c 100644 (file)
@@ -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)
 
index ea763dfa4127ff4baf1cdbd642b7abf61f789a1b..de2f7b454d5493191aca1c3c7d00e6425b9c328d 100644 (file)
@@ -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
 
index c9937b47e0d2f4d2eb15a00cc945aab6e8920b4b..e963fc64c9a383e41da3d21ae2deffdb41020f4b 100644 (file)
@@ -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
 
index f6213b236fe01483002e97f63d90da812ebdb912..aede803c0c9a50fe8aef6f051efcc72e19a767ad 100644 (file)
@@ -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()
index 19720509c8d247a955aad076360974ef22841d92..8568764aeeaa69089f15cf1c5656783e1c08c493 100644 (file)
@@ -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)
 
index ee2cfa7a5fe95e98ee654ba8405ffeddd8810fa0..18831685d1a11c73aab9f6ab757e7c54e2e7fd35 100644 (file)
@@ -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)
index 17fdb9c2c24266871eb702b88fb443dd4f7089ae..15bea8b3bddfc08b3baa6142b5de775a1afa97d1 100644 (file)
@@ -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()
index 5004be6415e4608b3e7a20a36e5c64ccfadf7a6b..7f226420d3b43c18eff4f9a3edb2b106f47d0f32 100644 (file)
@@ -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)
index d2ed300be3ce0e5c6c00c52a1630eab25a3eb72b..545e6d1a3c6ca6e7a6867150689f45c4e62100f9 100644 (file)
@@ -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 25dd1a2ec44da832d06ded29f393d716e4b54783..d0df450d05655c5d8f724c42dc6b5d18b3676a60 160000 (submodule)
--- a/wiki
+++ b/wiki
@@ -1 +1 @@
-Subproject commit 25dd1a2ec44da832d06ded29f393d716e4b54783
+Subproject commit d0df450d05655c5d8f724c42dc6b5d18b3676a60