]> 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 #2 from copycat-killer/master
authortrap000d <trap000d@gmail.com>
Mon, 6 Feb 2017 01:34:55 +0000 (14:34 +1300)
committerGitHub <noreply@github.com>
Mon, 6 Feb 2017 01:34:55 +0000 (14:34 +1300)
Merge to master

61 files changed:
ISSUE_TEMPLATE.md [new file with mode: 0644]
README.rst
asyncshell.lua [deleted file]
helpers.lua
icons/layout/default/cascadetile.png [moved from icons/layout/default/cascadebrowse.png with 100% similarity]
icons/layout/default/cascadetilew.png [moved from icons/layout/default/cascadebrowsew.png with 100% similarity]
icons/layout/default/centerhwork.png [deleted file]
icons/layout/default/centerhworkw.png [deleted file]
icons/layout/default/centerworkh.png [new file with mode: 0644]
icons/layout/default/centerworkhw.png [new file with mode: 0644]
icons/layout/zenburn/cascadetile.png [moved from icons/layout/zenburn/cascadebrowse.png with 100% similarity]
icons/layout/zenburn/centerworkh.png [new file with mode: 0644]
icons/redshift/redshift_off.png [deleted file]
icons/redshift/redshift_on.png [deleted file]
icons/taskwarrior.png [moved from icons/taskwarrior/task.png with 100% similarity]
icons/taskwarrior/tasksmall.png [deleted file]
init.lua
lain-git.rockspec
layout/cascade.lua
layout/cascadetile.lua [deleted file]
layout/centerfair.lua [deleted file]
layout/centerhwork.lua [deleted file]
layout/centerwork.lua
layout/centerworkd.lua [deleted file]
layout/termfair.lua
layout/uselessfair.lua [deleted file]
layout/uselesspiral.lua [deleted file]
layout/uselesstile.lua [deleted file]
scripts/dfs
scripts/mpdcover [deleted file]
util/init.lua
util/markup.lua
util/quake.lua
util/separators.lua
widgets/abase.lua
widgets/alsa.lua
widgets/alsabar.lua
widgets/base.lua [deleted file]
widgets/bat.lua
widgets/borderbox.lua [deleted file]
widgets/calendar.lua
widgets/contrib/gpmdp.lua
widgets/contrib/kbdlayout.lua
widgets/contrib/moc.lua
widgets/contrib/redshift.lua
widgets/contrib/task.lua
widgets/contrib/tpbat/init.lua
widgets/contrib/tpbat/smapi.lua
widgets/cpu.lua
widgets/fs.lua
widgets/imap.lua
widgets/maildir.lua [deleted file]
widgets/mem.lua
widgets/mpd.lua
widgets/net.lua
widgets/pulseaudio.lua
widgets/pulsebar.lua
widgets/sysload.lua
widgets/temp.lua
widgets/weather.lua
wiki

diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md
new file mode 100644 (file)
index 0000000..e024e8b
--- /dev/null
@@ -0,0 +1,7 @@
+**Please read the [wiki](https://github.com/copycat-killer/lain/wiki) and search the [Issues section](https://github.com/copycat-killer/lain/issues) first.**
+
+If you can't find a solution there, then go ahead and provide:
+
+* output of `awesome -v` and `lua -v`
+* expected behavior and actual behavior
+* steps to reproduce the problem
index 87e11b599abf85028e0a2eebddae8a89f3d4d228..d9e78b24d774321e55ce811edfafd038b9355650 100644 (file)
@@ -1,19 +1,24 @@
 Lain
 ====
 
---------------------------------------------------
-Layouts, widgets and utilities for Awesome WM 3.5+
---------------------------------------------------
+-------------------------------------------------
+Layouts, widgets and utilities for Awesome WM 4.x
+-------------------------------------------------
 
 :Author: Luke Bonham <dada [at] archlinux [dot] info>
 :Version: git
 :License: GNU-GPL2_
 :Source: https://github.com/copycat-killer/lain
 
+Warning
+-------
+
+If you still have to use branch 3.5.x, you can refer to the commit 301faf5_, but be aware that it's no longer supported.
+
 Description
 -----------
 
-Successor of awesome-vain_, this module provides new layouts, a set of widgets and utility functions, in order to improve Awesome_ usability and configurability.
+Successor of awesome-vain_, this module provides new layouts, asynchronous widgets and utility functions, with the aim of improving Awesome_ usability and configurability.
 
 Read the wiki_ for all the info.
 
@@ -26,7 +31,7 @@ Just make sure that:
 
 - Your code fits with the general style of the module. In particular, you should use the same indentation pattern that the code uses, and also avoid adding space at the ends of lines.
 
-- Your code its easy to understand, maintainable, and modularized. You should also avoid code duplication wherever possible by adding functions or using ``lain.helpers``. If something is unclear, and you can't write it in such a way that it will be clear, explain it with a comment.
+- Your code its easy to understand, maintainable, and modularized. You should also avoid code duplication wherever possible by adding functions or using lain.helpers_. If something is unclear, and you can't write it in such a way that it will be clear, explain it with a comment.
 
 - You test your changes before submitting to make sure that not only your code works, but did not break other parts of the module too!
 
@@ -42,6 +47,8 @@ Screenshots
 .. image:: http://i.imgur.com/STCPcaJ.png
 
 .. _GNU-GPL2: http://www.gnu.org/licenses/gpl-2.0.html
+.. _301faf5: https://github.com/copycat-killer/lain/tree/301faf5370d045e94c9c344acb0fdac84a2f25a6
 .. _awesome-vain: https://github.com/vain/awesome-vain
 .. _Awesome: https://github.com/awesomeWM/awesome
 .. _wiki: https://github.com/copycat-killer/lain/wiki
+.. _lain.helpers: https://github.com/copycat-killer/lain/blob/master/helpers.lua
diff --git a/asyncshell.lua b/asyncshell.lua
deleted file mode 100644 (file)
index 61336db..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-
---[[
-                                                  
-     Licensed under GNU General Public License v2 
-      * (c) 2015, worron                          
-      * (c) 2013, Alexander Yakushev              
-                                                  
---]]
-
--- Asynchronous io.popen for Awesome WM.
--- How to use:
--- asyncshell.request('wscript -Kiev', function(output) wwidget.text = output end)
-
--- Grab environment
-local awful = require('awful')
-
--- Avoid discrepancies across multiple shells
-awful.util.shell = '/bin/sh'
-
--- Initialize tables for module
-asyncshell = { request_table = {}, id_counter = 0 }
-
--- Request counter
-local function next_id()
-    asyncshell.id_counter = (asyncshell.id_counter + 1) % 10000
-    return asyncshell.id_counter
-end
-
--- Remove given request
-function asyncshell.clear(id)
-    if asyncshell.request_table[id] then
-        if asyncshell.request_table[id].timer then
-            asyncshell.request_table[id].timer:stop()
-            asyncshell.request_table[id].timer = nil
-        end
-        asyncshell.request_table[id] = nil
-    end
-end
-
--- Sends an asynchronous request for an output of the shell command
--- @param command Command to be executed and taken output from
--- @param callback Function to be called when the command finishes
--- @param timeout Maximum amount of time to wait for the result (optional)
-function asyncshell.request(command, callback, timeout)
-    local id = next_id()
-    asyncshell.request_table[id] = { callback = callback }
-
-    local formatted_command = string.gsub(command, '"','\"')
-
-    local req = string.format(
-        "echo \"asyncshell.deliver(%s, [[\\\"$(%s)\\\"]])\" | awesome-client &",
-        id, formatted_command
-    )
-
-    if type(awful.spawn) == 'table' and awful.spawn.with_shell then
-        awful.spawn.with_shell(req)
-    else
-        awful.util.spawn_with_shell(req)
-    end
-
-    if timeout then
-        asyncshell.request_table[id].timer = timer({ timeout = timeout })
-        asyncshell.request_table[id].timer:connect_signal("timeout", function() asyncshell.clear(id) end)
-        asyncshell.request_table[id].timer:start()
-    end
-end
-
--- Calls the remembered callback function on the output of the shell command
--- @param id Request ID
--- @param output Shell command output to be delievered
-function asyncshell.deliver(id, output)
-    local output = string.sub(output, 2, -2)
-    if asyncshell.request_table[id] then
-        asyncshell.request_table[id].callback(output)
-        asyncshell.clear(id)
-    end
-end
-
-return asyncshell
index 6c0c3c46ec155fc8fbc485186567f2f5874ca171..4e5ce1facc505c5a78617fa40181120b4f1f5826 100644 (file)
@@ -6,15 +6,14 @@
                                                   
 --]]
 
-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,
-                 popen = io.popen }
-local rawget = rawget
-local table  = { sort   = table.sort }
+local easy_async = require("awful.spawn").easy_async
+local timer      = require("gears.timer")
+local debug      = require("debug")
+local io         = { lines = io.lines,
+                     open  = io.open }
+local rawget     = rawget
+local table      = { sort  = table.sort }
 
 -- Lain helper functions for internal use
 -- lain.helpers
@@ -90,38 +89,33 @@ end
 
 helpers.timer_table = {}
 
-function helpers.newtimer(_name, timeout, fun, nostart)
-    local name = timeout
+function helpers.newtimer(name, timeout, fun, nostart, stoppable)
+    if not name or #name == 0 then return end
+    name = (stoppable and name) or timeout
     if not helpers.timer_table[name] then
-        helpers.timer_table[name] = capi.timer({ timeout = timeout })
+        helpers.timer_table[name] = timer({ timeout = timeout })
         helpers.timer_table[name]:start()
     end
     helpers.timer_table[name]:connect_signal("timeout", fun)
     if not nostart then
         helpers.timer_table[name]:emit_signal("timeout")
     end
+    return stoppable and helpers.timer_table[name]
 end
 
 -- }}}
 
 -- {{{ Pipe operations
 
--- read the full output of a command output
-function helpers.read_pipe(cmd)
-   local f = assert(io.popen(cmd))
-   local output = f:read("*all")
-   f:close()
-   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
+-- run a command and execute a function on its output (asynchronous pipe)
+-- @param cmd the input command
+-- @param callback function to execute on cmd output
+-- @return cmd PID
+function helpers.async(cmd, callback)
+    return easy_async(cmd,
+    function (stdout, stderr, reason, exit_code)
+        callback(stdout)
+    end)
 end
 
 -- }}}
@@ -140,7 +134,19 @@ end
 
 -- }}}
 
---{{{ Iterate over table of records sorted by keys
+-- {{{ Misc
+
+-- check if an element exist on a table
+function helpers.element_in_table(element, tbl)
+    for _, i in pairs(tbl) do
+        if i == element then
+            return true
+        end
+    end
+    return false
+end
+
+-- iterate over table of records sorted by keys
 function helpers.spairs(t)
     -- collect the keys
     local keys = {}
@@ -157,7 +163,7 @@ function helpers.spairs(t)
         end
     end
 end
---}}}
 
+-- }}}
 
 return helpers
diff --git a/icons/layout/default/centerhwork.png b/icons/layout/default/centerhwork.png
deleted file mode 100644 (file)
index 59c90f8..0000000
Binary files a/icons/layout/default/centerhwork.png and /dev/null differ
diff --git a/icons/layout/default/centerhworkw.png b/icons/layout/default/centerhworkw.png
deleted file mode 100644 (file)
index 6866f44..0000000
Binary files a/icons/layout/default/centerhworkw.png and /dev/null differ
diff --git a/icons/layout/default/centerworkh.png b/icons/layout/default/centerworkh.png
new file mode 100644 (file)
index 0000000..4fb4fdd
Binary files /dev/null and b/icons/layout/default/centerworkh.png differ
diff --git a/icons/layout/default/centerworkhw.png b/icons/layout/default/centerworkhw.png
new file mode 100644 (file)
index 0000000..fd27766
Binary files /dev/null and b/icons/layout/default/centerworkhw.png differ
diff --git a/icons/layout/zenburn/centerworkh.png b/icons/layout/zenburn/centerworkh.png
new file mode 100644 (file)
index 0000000..00beeb5
Binary files /dev/null and b/icons/layout/zenburn/centerworkh.png differ
diff --git a/icons/redshift/redshift_off.png b/icons/redshift/redshift_off.png
deleted file mode 100644 (file)
index a92200f..0000000
Binary files a/icons/redshift/redshift_off.png and /dev/null differ
diff --git a/icons/redshift/redshift_on.png b/icons/redshift/redshift_on.png
deleted file mode 100644 (file)
index 205e0a7..0000000
Binary files a/icons/redshift/redshift_on.png and /dev/null differ
diff --git a/icons/taskwarrior/tasksmall.png b/icons/taskwarrior/tasksmall.png
deleted file mode 100755 (executable)
index 2de6946..0000000
Binary files a/icons/taskwarrior/tasksmall.png and /dev/null differ
index 50864359a58e3c928a11ac5bd176c0b52c1c9b6b..5640861cde7050353acaf418e2918d5bc15be1fa 100644 (file)
--- a/init.lua
+++ b/init.lua
@@ -9,13 +9,8 @@
                                                    
 --]]
 
-package.loaded.lain = nil
-
-local lain =
-{
+return {
     layout  = require("lain.layout"),
     util    = require("lain.util"),
     widgets = require("lain.widgets")
 }
-
-return lain
index b8d7abb3c27b7f89dc7d61e497a92f36f1ff73e3..a264384d5b97fb2ee3351f9bb839e6b6602a4b0f 100644 (file)
@@ -7,19 +7,17 @@ source = {
 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.
+        Successor of awesome-vain, this module provides new layouts, a set of widgets and utility functions, with the aim of improving Awesome usability and configurability.
 
-        Optional dependencies: alsa-utils (for alsamixer); curl; imagemagick.
+        Optional dependency: curl (for IMAP and weather widgets).
     ]],
    homepage = "https://github.com/copycat-killer/lain",
    license = "GPL v2"
 }
 dependencies = {
-   "lua >= 5.1",
-   "awesome >= 3.5",
-   "alsa-utils",
-   "curl",
-   "imagemagick"
+   "lua >= 5.3",
+   "awesome >= 4.0",
+   "curl"
 }
 supported_platforms = { "linux" }
 build = {
index 3d7598b4c9380c3833424a7e9ff02d2c26ecf05c..204ce4017bc211f532c750ae5f2e6c3979590486 100644 (file)
                                                   
 --]]
 
-local tag       = require("awful.tag")
-local beautiful = require("beautiful")
+local floor  = math.floor
+local screen = screen
 
-local cascade =
-{
+local cascade = {
     name     = "cascade",
     nmaster  = 0,
     offset_x = 32,
-    offset_y = 8
+    offset_y = 8,
+    tile     = {
+        name          = "cascadetile",
+        nmaster       = 0,
+        ncol          = 0,
+        mwfact        = 0,
+        offset_x      = 5,
+        offset_y      = 32,
+        extra_padding = 0
+    }
 }
 
-function cascade.arrange(p)
+local function do_cascade(p, tiling)
+    local t = p.tag or screen[p.screen].selected_tag
+    local wa = p.workarea
+    local cls = p.clients
 
-    -- Cascade windows.
+    if #cls == 0 then return end
 
-    -- A global border can be defined with
-    -- beautiful.global_border_width.
-    local global_border = tonumber(beautiful.global_border_width) or 0
-    if global_border < 0 then global_border = 0 end
+    if not tiling then
+        -- Cascade windows.
 
-    -- Screen.
-    local wa = p.workarea
-    local cls = p.clients
+        local num_c
+        if cascade.nmaster > 0 then
+            num_c = cascade.nmaster
+        else
+            num_c = t.master_count
+        end
+
+        -- Opening a new window will usually force all existing windows to
+        -- get resized. This wastes a lot of CPU time. So let's set a lower
+        -- bound to "how_many": This wastes a little screen space but you'll
+        -- get a much better user experience.
+        local how_many = (#cls >= num_c and #cls) or num_c
+
+        local current_offset_x = cascade.offset_x * (how_many - 1)
+        local current_offset_y = cascade.offset_y * (how_many - 1)
+
+        -- Iterate.
+        for i = 1,#cls,1 do
+            local c = cls[i]
+            local g = {}
 
-    wa.height = wa.height - (global_border * 2)
-    wa.width = wa.width - (global_border * 2)
-    wa.x = wa.x + global_border
-    wa.y = wa.y + global_border
-
-    -- Opening a new window will usually force all existing windows to
-    -- get resized. This wastes a lot of CPU time. So let's set a lower
-    -- bound to "how_many": This wastes a little screen space but you'll
-    -- get a much better user experience.
-    local t = tag.selected(p.screen)
-    local num_c
-    if cascade.nmaster > 0
-    then
-        num_c = cascade.nmaster
+            g.x      = wa.x + (how_many - i) * cascade.offset_x
+            g.y      = wa.y + (i - 1) * cascade.offset_y
+            g.width  = wa.width - current_offset_x
+            g.height = wa.height - current_offset_y
+
+            if g.width  < 1 then g.width  = 1 end
+            if g.height < 1 then g.height = 1 end
+
+            p.geometries[c] = g
+        end
     else
-        num_c = tag.getnmaster(t)
-    end
+        -- Layout with one fixed column meant for a master window. Its
+        -- width is calculated according to mwfact. Other clients are
+        -- cascaded or "tabbed" in a slave column on the right.
 
-    local how_many = #cls
-    if how_many < num_c
-    then
-        how_many = num_c
-    end
+        --         (1)                 (2)                 (3)                 (4)
+        --   +----------+---+    +----------+---+    +----------+---+    +----------+---+
+        --   |          |   |    |          | 3 |    |          | 4 |    |         +---+|
+        --   |          |   | -> |          |   | -> |         +---++ -> |        +---+|+
+        --   |  1       | 2 |    |  1      +---++    |  1      | 3 ||    |  1    +---+|+|
+        --   |          |   |    |         | 2 ||    |        +---++|    |      +---+|+ |
+        --   |          |   |    |         |   ||    |        | 2 | |    |      | 2 |+  |
+        --   +----------+---+    +---------+---++    +--------+---+-+    +------+---+---+
+
+        local mwfact
+        if cascade.tile.mwfact > 0 then
+            mwfact = cascade.tile.mwfact
+        else
+            mwfact = t.master_width_factor
+        end
+
+        -- Make slave windows overlap main window? Do this if ncol is 1.
+        local overlap_main
+        if cascade.tile.ncol > 0 then
+            overlap_main = cascade.tile.ncol
+        else
+            overlap_main = t.column_count
+        end
+
+        -- Minimum space for slave windows? See cascade.tile.lua.
+        local num_c
+        if cascade.tile.nmaster > 0 then
+            num_c = cascade.tile.nmaster
+        else
+            num_c = t.master_count
+        end
 
-    local current_offset_x = cascade.offset_x * (how_many - 1)
-    local current_offset_y = cascade.offset_y * (how_many - 1)
+        local how_many = (#cls - 1 >= num_c and (#cls - 1)) or num_c
 
-    -- Iterate.
-    for i = 1,#cls,1
-    do
-        local c = cls[i]
+        local current_offset_x = cascade.tile.offset_x * (how_many - 1)
+        local current_offset_y = cascade.tile.offset_y * (how_many - 1)
+
+        if #cls <= 0 then return end
+
+        -- Main column, fixed width and height.
+        local c = cls[1]
         local g = {}
+        -- Rounding is necessary to prevent the rendered size of slavewid
+        -- from being 1 pixel off when the result is not an integer.
+        local mainwid = floor(wa.width * mwfact)
+        local slavewid = wa.width - mainwid
+
+        if overlap_main == 1 then
+            g.width = wa.width
+
+            -- The size of the main window may be reduced a little bit.
+            -- This allows you to see if there are any windows below the
+            -- main window.
+            -- This only makes sense, though, if the main window is
+            -- overlapping everything else.
+            g.width = g.width - cascade.tile.extra_padding
+        else
+            g.width = mainwid
+        end
 
-        g.x = wa.x + (how_many - i) * cascade.offset_x
-        g.y = wa.y + (i - 1) * cascade.offset_y
-        g.width = wa.width - current_offset_x - 2*c.border_width
-        g.height = wa.height - current_offset_y - 2*c.border_width
-        if g.width < 1 then g.width = 1 end
+        g.height = wa.height
+        g.x = wa.x
+        g.y = wa.y
+
+        if g.width < 1  then g.width  = 1 end
         if g.height < 1 then g.height = 1 end
 
-        c:geometry(g)
+        p.geometries[c] = g
+
+        -- Remaining clients stacked in slave column, new ones on top.
+        if #cls <= 1 then return end
+        for i = 2,#cls do
+            c = cls[i]
+            g = {}
+
+            g.width  = slavewid - current_offset_x
+            g.height = wa.height - current_offset_y
+
+            g.x = wa.x + mainwid + (how_many - (i - 1)) * cascade.tile.offset_x
+            g.y = wa.y + (i - 2) * cascade.tile.offset_y
+
+            if g.width < 1  then g.width  = 1 end
+            if g.height < 1 then g.height = 1 end
+
+            p.geometries[c] = g
+        end
     end
 end
 
+function cascade.tile.arrange(p)
+    return do_cascade(p, true)
+end
+
+function cascade.arrange(p)
+    return do_cascade(p, false)
+end
+
 return cascade
diff --git a/layout/cascadetile.lua b/layout/cascadetile.lua
deleted file mode 100644 (file)
index 3baf3e9..0000000
+++ /dev/null
@@ -1,174 +0,0 @@
-
---[[
-                                                  
-     Licensed under GNU General Public License v2 
-      * (c) 2014,      projektile                 
-      * (c) 2013,      Luke Bonham                
-      * (c) 2010-2012, Peter Hofmann              
-                                                  
---]]
-
-local tag       = require("awful.tag")
-local beautiful = require("beautiful")
-local tonumber  = tonumber
-
-local cascadetile =
-{
-    name          = "cascadetile",
-    nmaster       = 0,
-    ncol          = 0,
-    mwfact        = 0,
-    offset_x      = 5,
-    offset_y      = 32,
-    extra_padding = 0
-}
-
-function cascadetile.arrange(p)
-
-    -- Layout with one fixed column meant for a master window. Its
-    -- width is calculated according to mwfact. Other clients are
-    -- cascaded or "tabbed" in a slave column on the right.
-
-    -- It's a bit hard to demonstrate the behaviour with ASCII-images...
-    --
-    --         (1)                 (2)                 (3)                 (4)
-    --   +----------+---+    +----------+---+    +----------+---+    +----------+---+
-    --   |          |   |    |          | 3 |    |          | 4 |    |         +---+|
-    --   |          |   | -> |          |   | -> |         +---++ -> |        +---+|+
-    --   |  1       | 2 |    |  1      +---++    |  1      | 3 ||    |  1    +---+|+|
-    --   |          |   |    |         | 2 ||    |        +---++|    |      +---+|+ |
-    --   |          |   |    |         |   ||    |        | 2 | |    |      | 2 |+  |
-    --   +----------+---+    +---------+---++    +--------+---+-+    +------+---+---+
-
-    -- A useless gap (like the dwm patch) can be defined with
-    -- beautiful.useless_gap_width.
-    local useless_gap = tonumber(beautiful.useless_gap_width) or 0
-    if useless_gap < 0 then useless_gap = 0 end
-
-    -- A global border can be defined with
-    -- beautiful.global_border_width
-    local global_border = tonumber(beautiful.global_border_width) or 0
-    if global_border < 0 then global_border = 0 end
-
-    -- Screen.
-    local wa = p.workarea
-    local cls = p.clients
-
-    -- Borders are factored in.
-    wa.height = wa.height - (global_border * 2)
-    wa.width = wa.width - (global_border * 2)
-    wa.x = wa.x + global_border
-    wa.y = wa.y + global_border
-
-    -- Width of main column?
-    local t = tag.selected(p.screen)
-    local mwfact
-    if cascadetile.mwfact > 0
-    then
-        mwfact = cascadetile.mwfact
-    else
-        mwfact = tag.getmwfact(t)
-    end
-
-    -- Make slave windows overlap main window? Do this if ncol is 1.
-    local overlap_main
-    if cascadetile.ncol > 0
-    then
-        overlap_main = cascadetile.ncol
-    else
-        overlap_main = tag.getncol(t)
-    end
-
-    -- Minimum space for slave windows? See cascade.lua.
-    local num_c
-    if cascadetile.nmaster > 0
-    then
-        num_c = cascadetile.nmaster
-    else
-        num_c = tag.getnmaster(t)
-    end
-
-    local how_many = #cls - 1
-    if how_many < num_c
-    then
-        how_many = num_c
-    end
-    local current_offset_x = cascadetile.offset_x * (how_many - 1)
-    local current_offset_y = cascadetile.offset_y * (how_many - 1)
-
-    if #cls > 0
-    then
-        -- Main column, fixed width and height.
-        local c = cls[1]
-        local g = {}
-        -- Subtracting the useless_gap width from the work area width here
-        -- makes this mwfact calculation work the same as in uselesstile.
-        -- Rounding is necessary to prevent the rendered size of slavewid
-        -- from being 1 pixel off when the result is not an integer.
-        local mainwid = math.floor((wa.width - useless_gap) * mwfact)
-        local slavewid = wa.width - mainwid
-
-        if overlap_main == 1
-        then
-            g.width = wa.width - 2*c.border_width
-
-            -- The size of the main window may be reduced a little bit.
-            -- This allows you to see if there are any windows below the
-            -- main window.
-            -- This only makes sense, though, if the main window is
-            -- overlapping everything else.
-            g.width = g.width - cascadetile.extra_padding
-        else
-            g.width = mainwid - 2*c.border_width
-        end
-
-        g.height = wa.height - 2*c.border_width
-        g.x = wa.x
-        g.y = wa.y
-        if useless_gap > 0
-        then
-            -- Reduce width once and move window to the right. Reduce
-            -- height twice, however.
-            g.width = g.width - useless_gap
-            g.height = g.height - 2 * useless_gap
-            g.x = g.x + useless_gap
-            g.y = g.y + useless_gap
-
-            -- When there's no window to the right, add an additional
-            -- gap.
-            if overlap_main == 1
-            then
-                g.width = g.width - useless_gap
-            end
-        end
-        if g.width < 1 then g.width = 1 end
-        if g.height < 1 then g.height = 1 end
-        c:geometry(g)
-
-        -- Remaining clients stacked in slave column, new ones on top.
-        if #cls > 1
-        then
-            for i = 2,#cls
-            do
-                c = cls[i]
-                g = {}
-                g.width = slavewid - current_offset_x - 2*c.border_width
-                g.height = wa.height - current_offset_y - 2*c.border_width
-                g.x = wa.x + mainwid + (how_many - (i - 1)) * cascadetile.offset_x
-                g.y = wa.y + (i - 2) * cascadetile.offset_y
-                if useless_gap > 0
-                then
-                    g.width = g.width - 2 * useless_gap
-                    g.height = g.height - 2 * useless_gap
-                    g.x = g.x + useless_gap
-                    g.y = g.y + useless_gap
-                end
-                if g.width < 1 then g.width = 1 end
-                if g.height < 1 then g.height = 1 end
-                c:geometry(g)
-            end
-        end
-    end
-end
-
-return cascadetile
diff --git a/layout/centerfair.lua b/layout/centerfair.lua
deleted file mode 100644 (file)
index 5022726..0000000
+++ /dev/null
@@ -1,164 +0,0 @@
-
---[[
-                                                 
-     Licensed under GNU General Public License v2
-      * (c) 2014,      projektile                
-      * (c) 2013,      Luke Bonham               
-      * (c) 2010,      Nicolas Estibals          
-      * (c) 2010-2012, Peter Hofmann             
-                                                 
---]]
-
-local tag       = require("awful.tag")
-local beautiful = require("beautiful")
-local math      = { ceil  = math.ceil,
-                    floor = math.floor,
-                    max   = math.max }
-local tonumber  = tonumber
-
-local centerfair  = { name = "centerfair" }
-
-function centerfair.arrange(p)
-    -- Layout with fixed number of vertical columns (read from nmaster).
-    -- Cols are centerded until there is nmaster columns, then windows
-    -- are stacked in the slave columns, with at most ncol clients per
-    -- column if possible.
-
-    -- with nmaster=3 and ncol=1 you'll have
-    --        (1)                (2)                (3)
-    --   +---+---+---+      +-+---+---+-+      +---+---+---+
-    --   |   |   |   |      | |   |   | |      |   |   |   |
-    --   |   | 1 |   |  ->  | | 1 | 2 | | ->   | 1 | 2 | 3 |  ->
-    --   |   |   |   |      | |   |   | |      |   |   |   |
-    --   +---+---+---+      +-+---+---+-+      +---+---+---+
-
-    --        (4)                (5)
-    --   +---+---+---+      +---+---+---+
-    --   |   |   | 3 |      |   | 2 | 4 |
-    --   + 1 + 2 +---+  ->  + 1 +---+---+
-    --   |   |   | 4 |      |   | 3 | 5 |
-    --   +---+---+---+      +---+---+---+
-
-    -- A useless gap (like the dwm patch) can be defined with
-    -- beautiful.useless_gap_width .
-    local useless_gap = tonumber(beautiful.useless_gap_width) or 0
-    if useless_gap < 0 then useless_gap = 0 end
-
-    -- A global border can be defined with
-    -- beautiful.global_border_width
-    local global_border = tonumber(beautiful.global_border_width) or 0
-    if global_border < 0 then global_border = 0 end
-
-    -- Screen.
-    local wa = p.workarea
-    local cls = p.clients
-
-    -- Borders are factored in.
-    wa.height = wa.height - (global_border * 2)
-    wa.width = wa.width - (global_border * 2)
-    wa.x = wa.x + global_border
-    wa.y = wa.y + global_border
-
-    -- How many vertical columns? Read from nmaster on the tag.
-    local t = tag.selected(p.screen)
-    local num_x = centerfair.nmaster or tag.getnmaster(t)
-    local ncol = centerfair.ncol or tag.getncol(t)
-    if num_x <= 2 then num_x = 2 end
-
-    local width = math.floor((wa.width - (num_x + 1)*useless_gap) / num_x)
-
-    if #cls < num_x
-    then
-        -- Less clients than the number of columns, let's center it!
-        local offset_x = wa.x + (wa.width - #cls*width - (#cls - 1)*useless_gap) / 2
-        local g = {}
-        g.y = wa.y + useless_gap
-        for i = 1, #cls do
-            local c = cls[i]
-            g.width = width - 2*c.border_width
-            g.height = wa.height - 2*useless_gap - 2*c.border_width
-            if g.width < 1 then g.width = 1 end
-            if g.height < 1 then g.height = 1 end
-            g.x = offset_x + (i - 1) * (width + useless_gap)
-            c:geometry(g)
-        end
-    else
-        -- More clients than the number of columns, let's arrange it!
-        -- Master client deserves a special treatement
-        local c = cls[1]
-        local g = {}
-        g.width = wa.width - (num_x - 1)*width - (num_x + 1)*useless_gap - 2*c.border_width
-        g.height = wa.height - 2*useless_gap - 2*c.border_width
-        if g.width < 1 then g.width = 1 end
-        if g.height < 1 then g.height = 1 end
-        g.x = wa.x + useless_gap
-        g.y = wa.y + useless_gap
-
-        c:geometry(g)
-
-        -- Treat the other clients
-
-        -- Compute distribution of clients among columns
-        local num_y ={}
-        do
-            local remaining_clients = #cls-1
-            local ncol_min = math.ceil(remaining_clients/(num_x-1))
-            if ncol >= ncol_min
-            then
-                for i = (num_x-1), 1, -1 do
-                    if (remaining_clients-i+1) < ncol
-                    then
-                        num_y[i] = remaining_clients-i + 1
-                    else
-                        num_y[i] = ncol
-                    end
-                    remaining_clients = remaining_clients - num_y[i]
-                end
-            else
-                local rem = remaining_clients % (num_x-1)
-                if rem ==0
-                then
-                    for i = 1, num_x-1 do
-                        num_y[i] = ncol_min
-                    end
-                else
-                    for i = 1, num_x-1 do
-                        num_y[i] = ncol_min - 1
-                    end
-                    for i = 0, rem-1 do
-                        num_y[num_x-1-i] = num_y[num_x-1-i] + 1
-                    end
-                end
-            end
-        end
-
-        -- Compute geometry of the other clients
-        local nclient = 2 -- we start with the 2nd client
-        g.x = g.x + g.width + useless_gap + 2*c.border_width
-
-        for i = 1, (num_x-1) do
-            local height = math.floor((wa.height - (num_y[i] + 1)*useless_gap) / num_y[i])
-            g.y = wa.y + useless_gap
-            for j = 0, (num_y[i]-2) do
-                local c = cls[nclient]
-                g.height = height - 2*c.border_width
-                g.width = width - 2*c.border_width
-                if g.width < 1 then g.width = 1 end
-                if g.height < 1 then g.height = 1 end
-                c:geometry(g)
-                nclient = nclient + 1
-                g.y = g.y + height + useless_gap
-            end
-            local c = cls[nclient]
-            g.height = wa.height - (num_y[i] + 1)*useless_gap - (num_y[i] - 1)*height - 2*c.border_width
-            g.width = width - 2*c.border_width
-            if g.width < 1 then g.width = 1 end
-            if g.height < 1 then g.height = 1 end
-            c:geometry(g)
-            nclient = nclient + 1
-            g.x = g.x + width + useless_gap
-        end
-    end
-end
-
-return centerfair
diff --git a/layout/centerhwork.lua b/layout/centerhwork.lua
deleted file mode 100644 (file)
index 14b1b01..0000000
+++ /dev/null
@@ -1,136 +0,0 @@
-
---[[
-                                                  
-     Licensed under GNU General Public License v2 
-      * (c) 2015,      Joerg Jaspert              
-      * (c) 2014,      projektile                 
-      * (c) 2013,      Luke Bonham                
-      * (c) 2010-2012, Peter Hofmann              
-                                                  
---]]
-
-local awful     = require("awful")
-local beautiful = require("beautiful")
-local tonumber  = tonumber
-
-local centerhwork =
-{
-    name         = "centerhwork",
-    top_left     = 0,
-    top_right    = 1,
-    bottom_left  = 2,
-    bottom_right = 3
-}
-
-function centerhwork.arrange(p)
-    -- A useless gap (like the dwm patch) can be defined with
-    -- beautiful.useless_gap_width .
-    local useless_gap = tonumber(beautiful.useless_gap_width) or 0
-
-    -- A global border can be defined with
-    -- beautiful.global_border_width
-    local global_border = tonumber(beautiful.global_border_width) or 0
-    if global_border < 0 then global_border = 0 end
-
-    -- Screen.
-    local wa = p.workarea
-    local cls = p.clients
-
-    -- Borders are factored in.
-    wa.height = wa.height - (global_border * 2)
-    wa.width = wa.width - (global_border * 2)
-    wa.x = wa.x + global_border
-    wa.y = wa.y + global_border
-
-    -- Width of main column?
-    local t = awful.tag.selected(p.screen)
-    local mwfact = awful.tag.getmwfact(t)
-
-    if #cls > 0
-    then
-        -- Main column, fixed width and height.
-        local c = cls[1]
-        local g = {}
-        local mainhei  = math.floor(wa.height * mwfact)
-        local slaveLwid = math.floor(wa.width / 2 )
-        local slaveRwid = wa.width - slaveLwid
-        local slavehei = wa.height - mainhei
-        local slaveThei = math.floor(slavehei / 2)
-        local slaveBhei = slavehei - slaveThei
-        local Lhalfgap = math.floor(useless_gap / 2)
-        local Rhalfgap = useless_gap - Lhalfgap
-
-        g.height = mainhei - 2*c.border_width
-        g.width  = wa.width - 2*useless_gap - 2*c.border_width
-        g.x = wa.x + useless_gap
-        g.y = wa.y + slaveThei
-
-        if g.width < 1 then g.width = 1 end
-        if g.height < 1 then g.height = 1 end
-        c:geometry(g)
-
-        -- Auxiliary windows.
-        if #cls > 1
-        then
-            local at = 0
-            for i = 2,#cls
-            do
-                -- It's all fixed. If there are more than 5 clients,
-                -- those additional clients will float. This is
-                -- intentional.
-                if at == 4
-                then
-                    break
-                end
-
-                c = cls[i]
-                g = {}
-
-                if i - 2 == centerhwork.top_left
-                then
-                    -- top left
-                    g.x = wa.x + useless_gap
-                    g.y = wa.y + useless_gap
-                    g.width = slaveLwid - useless_gap - Lhalfgap - 2*c.border_width
-                    g.height = slaveThei - 2*useless_gap - 2*c.border_width
-                elseif i - 2 == centerhwork.top_right
-                then
-                    -- top right
-                    g.x = wa.x + slaveLwid + Rhalfgap
-                    g.y = wa.y + useless_gap
-                    g.width = slaveRwid - useless_gap - Rhalfgap - 2*c.border_width
-                    g.height = slaveThei - 2*useless_gap - 2*c.border_width
-                elseif i - 2 == centerhwork.bottom_left
-                then
-                    -- bottom left
-                    g.x = wa.x + useless_gap
-                    g.y = wa.y + mainhei + slaveThei + useless_gap
-                    g.width = slaveLwid - useless_gap - Lhalfgap - 2*c.border_width
-                    g.height = slaveBhei - 2*useless_gap - 2*c.border_width
-                elseif i - 2 == centerhwork.bottom_right
-                then
-                    -- bottom right
-                    g.x = wa.x + slaveLwid + Rhalfgap
-                    g.y = wa.y + mainhei + slaveThei + useless_gap
-                    g.width = slaveRwid - useless_gap - Rhalfgap - 2*c.border_width
-                    g.height = slaveBhei - 2*useless_gap - 2*c.border_width
-                end
-
-                if g.width < 1 then g.width = 1 end
-                if g.height < 1 then g.height = 1 end
-                c:geometry(g)
-
-                at = at + 1
-            end
-
-            -- Set remaining clients to floating.
-            for i = (#cls - 1 - 4),1,-1
-            do
-                c = cls[i]
-                awful.client.floating.set(c, true)
-            end
-        end
-    end
-end
-
-return centerhwork
index 954826e0793d72c60d1962f14d48c0e870d9716c..eafab78ce5c5054f1834e616b97e555305f6294c 100644 (file)
 --[[
                                                   
      Licensed under GNU General Public License v2 
+      * (c) 2016,      Henrik Antonsson           
+      * (c) 2015,      Joerg Jaspert              
       * (c) 2014,      projektile                 
       * (c) 2013,      Luke Bonham                
       * (c) 2010-2012, Peter Hofmann              
                                                   
 --]]
 
-local awful     = require("awful")
-local beautiful = require("beautiful")
-local tonumber  = tonumber
-local math      = { floor = math.floor }
+local floor  = math.floor
+local screen = screen
 
-local centerwork =
-{
+local centerwork = {
     name         = "centerwork",
-    top_right    = 0,
-    bottom_right = 1,
-    bottom_left  = 2,
-    top_left     = 3
+    horizontal   = { name = "centerworkh" }
 }
 
-function centerwork.arrange(p)
-    -- A useless gap (like the dwm patch) can be defined with
-    -- beautiful.useless_gap_width .
-    local useless_gap = tonumber(beautiful.useless_gap_width) or 0
-
-    -- A global border can be defined with
-    -- beautiful.global_border_width
-    local global_border = tonumber(beautiful.global_border_width) or 0
-    if global_border < 0 then global_border = 0 end
-
-    -- Screen.
+local function do_centerwork(p, orientation)
+    local t = p.tag or screen[p.screen].selected_tag
     local wa = p.workarea
     local cls = p.clients
 
-    -- Borders are factored in.
-    wa.height = wa.height - (global_border * 2)
-    wa.width = wa.width - (global_border * 2)
-    wa.x = wa.x + global_border
-    wa.y = wa.y + global_border
+    if #cls == 0 then return end
 
-    -- Width of main column?
-    local t = awful.tag.selected(p.screen)
-    local mwfact = awful.tag.getmwfact(t)
+    local c = cls[1]
+    local g = {}
+
+    -- Main column, fixed width and height.
+    local mwfact          = t.master_width_factor
+    local mainhei         = floor(wa.height * mwfact)
+    local mainwid         = floor(wa.width * mwfact)
+    local slavewid        = wa.width - mainwid
+    local slaveLwid       = floor(slavewid / 2)
+    local slaveRwid       = slavewid - slaveLwid
+    local slavehei        = wa.height - mainhei
+    local slaveThei       = floor(slavehei / 2)
+    local slaveBhei       = slavehei - slaveThei
+    local nbrFirstSlaves  = floor(#cls / 2)
+    local nbrSecondSlaves = floor((#cls - 1) / 2)
+
+    local slaveFirstDim, slaveSecondDim = 0, 0
+
+    if orientation == "vertical" then
+        if nbrFirstSlaves  > 0 then slaveFirstDim  = floor(wa.height / nbrFirstSlaves) end
+        if nbrSecondSlaves > 0 then slaveSecondDim = floor(wa.height / nbrSecondSlaves) end
+
+        g.height = wa.height
+        g.width  = mainwid
 
-    if #cls > 0
-    then
-        -- Main column, fixed width and height.
-        local c = cls[1]
-        local g = {}
-        local mainwid = math.floor(wa.width * mwfact)
-        local slavewid = wa.width - mainwid
-        local slaveLwid = math.floor(slavewid / 2)
-        local slaveRwid = slavewid - slaveLwid
-        local slaveThei = math.floor(wa.height / 2)
-        local slaveBhei = wa.height - slaveThei
-        local Thalfgap = math.floor(useless_gap / 2)
-        local Bhalfgap = useless_gap - Thalfgap
-
-        g.height = wa.height - 2*useless_gap - 2*c.border_width
-        g.width = mainwid - 2*c.border_width
         g.x = wa.x + slaveLwid
-        g.y = wa.y + useless_gap
+        g.y = wa.y
+    else
+        if nbrFirstSlaves  > 0 then slaveFirstDim  = floor(wa.width / nbrFirstSlaves) end
+        if nbrSecondSlaves > 0 then slaveSecondDim = floor(wa.width / nbrSecondSlaves) end
 
-        if g.width < 1 then g.width = 1 end
-        if g.height < 1 then g.height = 1 end
-        c:geometry(g)
-
-        -- Auxiliary windows.
-        if #cls > 1
-        then
-            local at = 0
-            for i = 2,#cls
-            do
-                -- It's all fixed. If there are more than 5 clients,
-                -- those additional clients will float. This is
-                -- intentional.
-                if at == 4
-                then
-                    break
-                end
+        g.height  = mainhei
+        g.width = wa.width
 
-                c = cls[i]
-                g = {}
-
-                if i - 2 == centerwork.top_left
-                then
-                    -- top left
-                    g.x = wa.x + useless_gap
-                    g.y = wa.y + useless_gap
-                    g.width = slaveLwid - 2*useless_gap - 2*c.border_width
-                    g.height = slaveThei - useless_gap - Thalfgap - 2*c.border_width
-                elseif i - 2 == centerwork.top_right
-                then
-                    -- top right
-                    g.x = wa.x + slaveLwid + mainwid + useless_gap
-                    g.y = wa.y + useless_gap
-                    g.width = slaveRwid - 2*useless_gap - 2*c.border_width
-                    g.height = slaveThei - useless_gap - Thalfgap - 2*c.border_width
-                elseif i - 2 == centerwork.bottom_left
-                then
-                    -- bottom left
-                    g.x = wa.x + useless_gap
-                    g.y = wa.y + slaveThei + Bhalfgap
-                    g.width = slaveLwid - 2*useless_gap - 2*c.border_width
-                    g.height = slaveBhei - useless_gap - Bhalfgap - 2*c.border_width
-                elseif i - 2 == centerwork.bottom_right
-                then
-                    -- bottom right
-                    g.x = wa.x + slaveLwid + mainwid + useless_gap
-                    g.y = wa.y + slaveThei + Bhalfgap
-                    g.width = slaveRwid - 2*useless_gap - 2*c.border_width
-                    g.height = slaveBhei - useless_gap - Bhalfgap - 2*c.border_width
-                end
+        g.x = wa.x
+        g.y = wa.y + slaveThei
+    end
+
+    if g.width  < 1 then g.width  = 1 end
+    if g.height < 1 then g.height = 1 end
+
+    p.geometries[c] = g
+
+    -- Auxiliary windows.
+    if #cls <= 1 then return end
+    for i = 2,#cls do
+        local c = cls[i]
+        local g = {}
 
-                if g.width < 1 then g.width = 1 end
-                if g.height < 1 then g.height = 1 end
-                c:geometry(g)
+        local rowIndex = floor(i/2)
 
-                at = at + 1
+        if orientation == "vertical" then
+            if i % 2 == 0 then
+                -- left slave
+                g.x = wa.x
+                g.y = wa.y + (rowIndex-1)*slaveFirstDim
+
+                g.width = slaveLwid
+
+                -- if last slave in left row use remaining space for that slave
+                if rowIndex == nbrFirstSlaves then
+                    g.height = wa.y + wa.height - g.y
+                else
+                    g.height = slaveFirstDim
+                end
+            else
+                -- right slave
+                g.x = wa.x + slaveLwid + mainwid
+                g.y = wa.y + (rowIndex-1)*slaveSecondDim
+
+                g.width = slaveRwid
+
+                -- if last slave in right row use remaining space for that slave
+                if rowIndex == nbrSecondSlaves then
+                    g.height = wa.y + wa.height - g.y
+                else
+                    g.height = slaveSecondDim
+                end
             end
+        else
+            if i % 2 == 0 then
+                -- top slave
+                g.x = wa.x + (rowIndex-1)*slaveFirstDim
+                g.y = wa.y
+
+                g.height = slaveThei
+
+                -- if last slave in top row use remaining space for that slave
+                if rowIndex == nbrFirstSlaves then
+                    g.width = wa.x + wa.width - g.x
+                else
+                    g.width = slaveFirstDim
+                end
+            else
+                -- bottom slave
+                g.x = wa.x + (rowIndex-1)*slaveSecondDim
+                g.y = wa.y + slaveThei + mainhei
+
+                g.height = slaveBhei
+
+                -- if last slave in bottom row use remaining space for that slave
+                if rowIndex == nbrSecondSlaves then
+                    g.width = wa.x + wa.width - g.x
+                else
+                    g.width = slaveSecondDim
+                end
 
-            -- Set remaining clients to floating.
-            for i = (#cls - 1 - 4),1,-1
-            do
-                c = cls[i]
-                awful.client.floating.set(c, true)
             end
         end
+
+        if g.width  < 1 then g.width  = 1 end
+        if g.height < 1 then g.height = 1 end
+
+        p.geometries[c] = g
     end
 end
 
+
+function centerwork.horizontal.arrange(p)
+    return do_centerwork(p, "horizontal")
+end
+
+function centerwork.arrange(p)
+    return do_centerwork(p, "vertical")
+end
+
 return centerwork
diff --git a/layout/centerworkd.lua b/layout/centerworkd.lua
deleted file mode 100644 (file)
index e66a15a..0000000
+++ /dev/null
@@ -1,123 +0,0 @@
-
---[[
-
-     Licensed under GNU General Public License v2
-      * (c) 2016,      Henrik Antonsson
-      * (c) 2014,      projektile
-      * (c) 2013,      Luke Bonham
-      * (c) 2010-2012, Peter Hofmann
-
-     Based on centerwork.lua
---]]
-
-local awful     = require("awful")
-local beautiful = require("beautiful")
-local tonumber  = tonumber
-local math      = { floor = math.floor }
-
-local centerworkd =
-{
-    name         = "centerworkd",
-}
-
-function centerworkd.arrange(p)
-    -- A useless gap (like the dwm patch) can be defined with
-    -- beautiful.useless_gap_width .
-    local useless_gap = tonumber(beautiful.useless_gap_width) or 0
-
-    -- A global border can be defined with
-    -- beautiful.global_border_width
-    local global_border = tonumber(beautiful.global_border_width) or 0
-    if global_border < 0 then global_border = 0 end
-
-    -- Screen.
-    local wa = p.workarea
-    local cls = p.clients
-
-    -- Borders are factored in.
-    wa.height = wa.height - (global_border * 2)
-    wa.width = wa.width - (global_border * 2)
-    wa.x = wa.x + global_border
-    wa.y = wa.y + global_border
-
-    -- Width of main column?
-    local t = awful.tag.selected(p.screen)
-    local mwfact = awful.tag.getmwfact(t)
-
-    if #cls > 0
-    then
-        -- Main column, fixed width and height.
-        local c = cls[1]
-        local g = {}
-        local mainwid = math.floor(wa.width * mwfact)
-        local slavewid = wa.width - mainwid
-        local slaveLwid = math.floor(slavewid / 2)
-        local slaveRwid = slavewid - slaveLwid
-        local nbrLeftSlaves = math.floor(#cls / 2)
-        local nbrRightSlaves = math.floor((#cls - 1) / 2)
-
-        local slaveLeftHeight = 0
-        if nbrLeftSlaves > 0 then slaveLeftHeight = math.floor(wa.height / nbrLeftSlaves) end
-        if nbrRightSlaves > 0 then slaveRightHeight = math.floor(wa.height / nbrRightSlaves) end
-
-        g.height = wa.height - 2*useless_gap - 2*c.border_width
-        g.width = mainwid - 2*c.border_width
-        g.x = wa.x + slaveLwid
-        g.y = wa.y + useless_gap
-
-        if g.width < 1 then g.width = 1 end
-        if g.height < 1 then g.height = 1 end
-        c:geometry(g)
-
-        -- Auxiliary windows.
-        if #cls > 1
-        then
-            for i = 2,#cls
-            do
-                c = cls[i]
-                g = {}
-
-                local rowIndex = math.floor(i/2)
-
-                -- If i is even it should be placed on the left side
-                if i % 2 == 0
-                then
-                    -- left slave
-                    g.x = wa.x + useless_gap
-                    g.y = wa.y + useless_gap + (rowIndex-1)*slaveLeftHeight
-
-                    g.width = slaveLwid - 2*useless_gap - 2*c.border_width
-
-                    -- if last slave in left row use remaining space for that slave
-                    if rowIndex == nbrLeftSlaves
-                    then
-                        g.height = wa.y + wa.height - g.y - useless_gap - 2*c.border_width
-                    else
-                        g.height = slaveLeftHeight - useless_gap - 2*c.border_width
-                    end
-                else
-                    -- right slave
-                    g.x = wa.x + slaveLwid + mainwid + useless_gap
-                    g.y = wa.y + useless_gap + (rowIndex-1)*slaveRightHeight
-
-                    g.width = slaveRwid - 2*useless_gap - 2*c.border_width
-
-                    -- if last slave in right row use remaining space for that slave
-                    if rowIndex == nbrRightSlaves
-                    then
-                        g.height = wa.y + wa.height - g.y - useless_gap - 2*c.border_width
-                    else
-                        g.height = slaveRightHeight - useless_gap - 2*c.border_width
-                    end
-
-                end
-
-                if g.width < 1 then g.width = 1 end
-                if g.height < 1 then g.height = 1 end
-                c:geometry(g)
-            end
-        end
-    end
-end
-
-return centerworkd
index 6aca99db900ddc15bb675a06ff617d425a7c2e87..33b7ffc7489634761565caab3248c336b494a975 100644 (file)
      Licensed under GNU General Public License v2 
       * (c) 2014,      projektile                 
       * (c) 2013,      Luke Bonham                
+      * (c) 2010,      Nicolas Estibals           
       * (c) 2010-2012, Peter Hofmann              
                                                   
 --]]
 
-local tag       = require("awful.tag")
-local beautiful = require("beautiful")
-local math      = { ceil  = math.ceil,
-                    floor = math.floor,
-                    max   = math.max }
-local tonumber  = tonumber
+local math     = { ceil  = math.ceil,
+                   floor = math.floor,
+                   max   = math.max }
+local screen   = screen
+local tonumber = tonumber
 
 local termfair  = { name = "termfair" }
+termfair.center = { name = "centerfair" }
 
-function termfair.arrange(p)
-    -- Layout with fixed number of vertical columns (read from nmaster).
-    -- New windows align from left to right. When a row is full, a now
-    -- one above it is created. Like this:
-
-    --        (1)                (2)                (3)
-    --   +---+---+---+      +---+---+---+      +---+---+---+
-    --   |   |   |   |      |   |   |   |      |   |   |   |
-    --   | 1 |   |   |  ->  | 2 | 1 |   |  ->  | 3 | 2 | 1 |  ->
-    --   |   |   |   |      |   |   |   |      |   |   |   |
-    --   +---+---+---+      +---+---+---+      +---+---+---+
-
-    --        (4)                (5)                (6)
-    --   +---+---+---+      +---+---+---+      +---+---+---+
-    --   | 4 |   |   |      | 5 | 4 |   |      | 6 | 5 | 4 |
-    --   +---+---+---+  ->  +---+---+---+  ->  +---+---+---+
-    --   | 3 | 2 | 1 |      | 3 | 2 | 1 |      | 3 | 2 | 1 |
-    --   +---+---+---+      +---+---+---+      +---+---+---+
-
-    -- A useless gap (like the dwm patch) can be defined with
-    -- beautiful.useless_gap_width.
-    local useless_gap = tonumber(beautiful.useless_gap_width) or 0
-    if useless_gap < 0 then useless_gap = 0 end
-
-    -- A global border can be defined with
-    -- beautiful.global_border_width
-    local global_border = tonumber(beautiful.global_border_width) or 0
-    if global_border < 0 then global_border = 0 end
-
-    -- Screen.
+local function do_fair(p, orientation)
+    local t = p.tag or screen[p.screen].selected_tag
     local wa = p.workarea
     local cls = p.clients
 
-    -- Borders are factored in.
-    wa.height = wa.height - (global_border * 2)
-    wa.width = wa.width - (global_border * 2)
-    wa.x = wa.x + global_border
-    wa.y = wa.y + global_border
-
-    -- How many vertical columns?
-    local t = tag.selected(p.screen)
-    local num_x = termfair.nmaster or tag.getnmaster(t)
-
-    -- Do at least "desired_y" rows.
-    local desired_y = termfair.ncol or tag.getncol(t)
-
-    if #cls > 0
-    then
-        local num_y = math.max(math.ceil(#cls / num_x), desired_y)
+    if #cls == 0 then return end
+
+    if orientation == "west" then
+        -- Layout with fixed number of vertical columns (read from nmaster).
+        -- New windows align from left to right. When a row is full, a now
+        -- one above it is created. Like this:
+
+        --        (1)                (2)                (3)
+        --   +---+---+---+      +---+---+---+      +---+---+---+
+        --   |   |   |   |      |   |   |   |      |   |   |   |
+        --   | 1 |   |   |  ->  | 2 | 1 |   |  ->  | 3 | 2 | 1 |  ->
+        --   |   |   |   |      |   |   |   |      |   |   |   |
+        --   +---+---+---+      +---+---+---+      +---+---+---+
+
+        --        (4)                (5)                (6)
+        --   +---+---+---+      +---+---+---+      +---+---+---+
+        --   | 4 |   |   |      | 5 | 4 |   |      | 6 | 5 | 4 |
+        --   +---+---+---+  ->  +---+---+---+  ->  +---+---+---+
+        --   | 3 | 2 | 1 |      | 3 | 2 | 1 |      | 3 | 2 | 1 |
+        --   +---+---+---+      +---+---+---+      +---+---+---+
+
+        -- How many vertical columns? Read from nmaster on the tag.
+        local num_x = tonumber(termfair.nmaster) or t.master_count
+        local ncol  = tonumber(termfair.ncol) or t.column_count
+
+        if num_x <= 2 then num_x = 2 end
+        if ncol  <= 1 then ncol  = 1 end
+        local width = math.floor(wa.width/num_x)
+
+        local num_y     = math.max(math.ceil(#cls / num_x), ncol)
+        local height    = math.floor(wa.height/num_y)
         local cur_num_x = num_x
-        local at_x = 0
-        local at_y = 0
+        local at_x      = 0
+        local at_y      = 0
+
         local remaining_clients = #cls
-        local width = math.floor((wa.width - (num_x + 1)*useless_gap) / num_x)
-        local height = math.floor((wa.height - (num_y + 1)*useless_gap) / num_y)
 
         -- We start the first row. Left-align by limiting the number of
         -- available slots.
-        if remaining_clients < num_x
-        then
+        if remaining_clients < num_x then
             cur_num_x = remaining_clients
         end
 
         -- Iterate in reversed order.
-        for i = #cls,1,-1
-        do
+        for i = #cls,1,-1 do
             -- Get x and y position.
             local c = cls[i]
             local this_x = cur_num_x - at_x - 1
             local this_y = num_y - at_y - 1
 
-            -- Calc geometry.
+            -- Calculate geometry.
             local g = {}
-            if this_x == (num_x - 1)
-            then
-                g.width = wa.width - (num_x - 1)*width - (num_x + 1)*useless_gap - 2*c.border_width
+            if this_x == (num_x - 1) then
+                g.width = wa.width - (num_x - 1)*width
             else
-                g.width = width - 2*c.border_width
+                g.width = width
             end
-            if this_y == (num_y - 1)
-            then
-                g.height = wa.height - (num_y - 1)*height - (num_y + 1)*useless_gap - 2*c.border_width
+
+            if this_y == (num_y - 1) then
+                g.height = wa.height - (num_y - 1)*height
             else
-                g.height = height - 2*c.border_width
+                g.height = height
             end
 
             g.x = wa.x + this_x*width
             g.y = wa.y + this_y*height
 
-            if useless_gap > 0
-            then
-                -- All clients tile evenly.
-                g.x = g.x + (this_x + 1)*useless_gap
-                g.y = g.y + (this_y + 1)*useless_gap
-
-            end
-            if g.width < 1 then g.width = 1 end
+            if g.width  < 1 then g.width  = 1 end
             if g.height < 1 then g.height = 1 end
-            c:geometry(g)
+
+            p.geometries[c] = g
+
             remaining_clients = remaining_clients - 1
 
             -- Next grid position.
             at_x = at_x + 1
-            if at_x == num_x
-            then
+            if at_x == num_x then
                 -- Row full, create a new one above it.
                 at_x = 0
                 at_y = at_y + 1
 
                 -- We start a new row. Left-align.
-                if remaining_clients < num_x
-                then
+                if remaining_clients < num_x then
                     cur_num_x = remaining_clients
                 end
             end
         end
+    elseif orientation == "center" then
+        -- Layout with fixed number of vertical columns (read from nmaster).
+        -- Cols are centerded until there is nmaster columns, then windows
+        -- are stacked in the slave columns, with at most ncol clients per
+        -- column if possible.
+
+        -- with nmaster=3 and ncol=1 you'll have
+        --        (1)                (2)                (3)
+        --   +---+---+---+      +-+---+---+-+      +---+---+---+
+        --   |   |   |   |      | |   |   | |      |   |   |   |
+        --   |   | 1 |   |  ->  | | 1 | 2 | | ->   | 1 | 2 | 3 |  ->
+        --   |   |   |   |      | |   |   | |      |   |   |   |
+        --   +---+---+---+      +-+---+---+-+      +---+---+---+
+
+        --        (4)                (5)
+        --   +---+---+---+      +---+---+---+
+        --   |   |   | 3 |      |   | 2 | 4 |
+        --   + 1 + 2 +---+  ->  + 1 +---+---+
+        --   |   |   | 4 |      |   | 3 | 5 |
+        --   +---+---+---+      +---+---+---+
+
+        -- How many vertical columns? Read from nmaster on the tag.
+        local num_x = tonumber(termfair.center.nmaster) or t.master_count
+        local ncol  = tonumber(termfair.center.ncol) or t.column_count
+
+        if num_x <= 2 then num_x = 2 end
+        if ncol  <= 1 then ncol  = 1 end
+
+        local width = math.floor(wa.width / num_x)
+
+        if #cls < num_x then
+            -- Less clients than the number of columns, let's center it!
+            local offset_x = wa.x + (wa.width - #cls*width) / 2
+            for i = 1, #cls do
+                local g = { y = wa.y }
+                g.width  = width
+                g.height = wa.height
+                if g.width < 1 then g.width = 1 end
+                if g.height < 1 then g.height = 1 end
+                g.x = offset_x + (i - 1) * width
+                p.geometries[cls[i]] = g
+            end
+        else
+            -- More clients than the number of columns, let's arrange it!
+            -- Master client deserves a special treatement
+            local g = {}
+            g.width = wa.width - (num_x - 1)*width
+            g.height = wa.height
+            if g.width < 1 then g.width = 1 end
+            if g.height < 1 then g.height = 1 end
+            g.x = wa.x
+            g.y = wa.y
+            p.geometries[cls[1]] = g
+
+            -- Treat the other clients
+
+            -- Compute distribution of clients among columns
+            local num_y = {}
+            local remaining_clients = #cls-1
+            local ncol_min = math.ceil(remaining_clients/(num_x-1))
+
+            if ncol >= ncol_min then
+                for i = (num_x-1), 1, -1 do
+                    if (remaining_clients-i+1) < ncol then
+                        num_y[i] = remaining_clients-i + 1
+                    else
+                        num_y[i] = ncol
+                    end
+                    remaining_clients = remaining_clients - num_y[i]
+                end
+            else
+                local rem = remaining_clients % (num_x-1)
+                if rem == 0 then
+                    for i = 1, num_x-1 do
+                        num_y[i] = ncol_min
+                    end
+                else
+                    for i = 1, num_x-1 do
+                        num_y[i] = ncol_min - 1
+                    end
+                    for i = 0, rem-1 do
+                        num_y[num_x-1-i] = num_y[num_x-1-i] + 1
+                    end
+                end
+            end
+
+            -- Compute geometry of the other clients
+            local nclient = 2 -- we start with the 2nd client
+            local wx = g.x + g.width
+            for i = 1, (num_x-1) do
+                local height = math.floor(wa.height / num_y[i])
+                local wy = wa.y
+                for j = 0, (num_y[i]-2) do
+                    local g = {}
+                    g.x = wx
+                    g.y = wy
+                    g.height = height
+                    g.width = width
+                    if g.width < 1 then g.width = 1 end
+                    if g.height < 1 then g.height = 1 end
+                    p.geometries[cls[nclient]] = g
+                    nclient = nclient + 1
+                    wy = wy + height
+                end
+                local g = {}
+                g.x = wx
+                g.y = wy
+                g.height = wa.height - (num_y[i] - 1)*height
+                g.width = width
+                if g.width < 1 then g.width = 1 end
+                if g.height < 1 then g.height = 1 end
+                p.geometries[cls[nclient]] = g
+                nclient = nclient + 1
+                wx = wx + width
+            end
+        end
     end
 end
 
+function termfair.center.arrange(p)
+    return do_fair(p, "center")
+end
+
+function termfair.arrange(p)
+    return do_fair(p, "west")
+end
+
 return termfair
diff --git a/layout/uselessfair.lua b/layout/uselessfair.lua
deleted file mode 100644 (file)
index e1284e0..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-
---[[
-                                                  
-     Licensed under GNU General Public License v2 
-      * (c) 2014,      projektile, worron         
-      * (c) 2013,      Luke Bonham                
-      * (c) 2012,      Josh Komoroske             
-      * (c) 2010-2012, Peter Hofmann              
-                                                  
---]]
-
-local beautiful = require("beautiful")
-local ipairs    = ipairs
-local math      = { ceil = math.ceil, sqrt = math.sqrt, floor = math.floor, max = math.max }
-local tonumber  = tonumber
-
-local uselessfair = {}
-
--- Transformation functions
-local function swap(geometry)
-    return { x = geometry.y, y = geometry.x, width = geometry.height, height = geometry.width }
-end
-
--- Client geometry correction depending on useless gap and window border
-local function size_correction(c, geometry, useless_gap)
-    geometry.width  = math.max(geometry.width  - 2 * c.border_width - useless_gap, 1)
-    geometry.height = math.max(geometry.height - 2 * c.border_width - useless_gap, 1)
-    geometry.x = geometry.x + useless_gap / 2
-    geometry.y = geometry.y + useless_gap / 2
-end
-
--- Main tiling function
-local function fair(p, orientation)
-
-    -- Theme vars
-    local useless_gap = beautiful.useless_gap_width or 0
-    local global_border = beautiful.global_border_width or 0
-
-    -- Aliases
-    local wa = p.workarea
-    local cls = p.clients
-
-    -- Nothing to tile here
-    if #cls == 0 then return end
-
-    -- Workarea size correction depending on useless gap and global border
-    wa.height = wa.height - 2 * global_border - useless_gap
-    wa.width  = wa.width -  2 * global_border - useless_gap
-    wa.x = wa.x + useless_gap / 2 + global_border
-    wa.y = wa.y + useless_gap / 2 + global_border
-
-    -- Geometry calculation
-    local row, col = 0, 0
-
-    local rows = math.ceil(math.sqrt(#cls))
-    local cols = math.ceil(#cls / rows)
-
-    for i, c in ipairs(cls) do
-        local g = {}
-
-        -- find tile orientation for current client and swap geometry if need
-        local need_swap = (orientation == "east" and #cls <= 2) or (orientation == "south" and #cls > 2)
-        local area = need_swap and swap(wa) or wa
-
-        -- calculate geometry
-        if #cls < (cols * rows) and row == cols - 1 then
-            g.width = area.width / (rows - ((cols * rows) - #cls))
-        else
-            g.width = area.width / rows
-        end
-
-        g.height = area.height / cols
-        g.x = area.x + col * g.width
-        g.y = area.y + row * g.height
-
-        -- turn back to real if geometry was swapped
-        if need_swap then g = swap(g) end
-
-        -- window size correction depending on useless gap and window border
-        size_correction(c, g, useless_gap)
-
-        -- set geometry
-        c:geometry(g)
-
-        -- update tile grid coordinates
-        col = i % rows
-        row = math.floor(i / rows)
-    end
-end
-
--- Layout constructor
-local function construct_layout(name, direction)
-    return {
-        name = name,
-        -- @p screen The screen number to tile
-        arrange = function(p) return fair(p, direction) end
-    }
-end
-
--- Build layouts with different tile direction
-uselessfair.vertical   = construct_layout("uselessfair", "south")
-uselessfair.horizontal = construct_layout("uselessfairh", "east")
-
--- Module aliase
-uselessfair.arrange = uselessfair.vertical.arrange
-uselessfair.name = uselessfair.vertical.name
-
-return uselessfair
diff --git a/layout/uselesspiral.lua b/layout/uselesspiral.lua
deleted file mode 100644 (file)
index c388961..0000000
+++ /dev/null
@@ -1,123 +0,0 @@
-
---[[
-                                                  
-     Licensed under GNU General Public License v2 
-      * (c) 2014, projektile                      
-      * (c) 2013, Luke Bonham                     
-      * (c) 2009, Uli Schlachter                  
-      * (c) 2008, Julien Danjolu                  
-                                                  
---]]
-
-local beautiful = require("beautiful")
-local ipairs    = ipairs
-local tonumber  = tonumber
-local math      = require("math")
-
-local uselesspiral = {}
-
-local function spiral(p, spiral)
-    -- A useless gap (like the dwm patch) can be defined with
-    -- beautiful.useless_gap_width.
-    local useless_gap = tonumber(beautiful.useless_gap_width) or 0
-    if useless_gap < 0 then useless_gap = 0 end
-
-    -- A global border can be defined with
-    -- beautiful.global_border_width
-    local global_border = tonumber(beautiful.global_border_width) or 0
-    if global_border < 0 then global_border = 0 end
-
-    -- Themes border width requires an offset
-    local bw = tonumber(beautiful.border_width) or 0
-
-    -- get our orientation right
-    local wa = p.workarea
-    local cls = p.clients
-    local n = #cls -- number of windows total; k = which window number
-
-    wa.height = wa.height - ((global_border * 2) + (bw * 2))
-    wa.width = wa.width - ((global_border * 2) + (bw * 2))
-
-    local static_wa = wa
-
-    for k, c in ipairs(cls) do
-        if k < n then
-            if k % 2 == 0 then
-                wa.height = (wa.height / 2)
-            else
-                wa.width = (wa.width / 2)
-            end
-        end
-
-        if k % 4 == 0 and spiral then
-            wa.x = wa.x - wa.width
-        elseif k % 2 == 0 or
-            (k % 4 == 3 and k < n and spiral) then
-            wa.x = wa.x + wa.width
-        end
-
-        if k % 4 == 1 and k ~= 1 and spiral then
-            wa.y = wa.y - wa.height
-        elseif k % 2 == 1 and k ~= 1 or
-            (k % 4 == 0 and k < n and spiral) then
-            wa.y = wa.y + wa.height
-        end
-
-            local wa2 = {}
-            wa2.x = wa.x + (useless_gap / 2) + global_border
-            wa2.y = wa.y + (useless_gap / 2) + global_border
-            wa2.height = wa.height - (useless_gap / 2)
-            wa2.width = wa.width - (useless_gap / 2)
-
-        -- Useless gap.
-        if useless_gap > 0
-        then
-            -- Top and left clients are shrinked by two steps and
-            -- get moved away from the border. Other clients just
-            -- get shrinked in one direction.
-
-            top = false
-            left = false
-
-            if wa2.y == static_wa.y then
-               top = true
-            end
-
-            if wa2.x == static_wa.x then
-               left = true
-            end
-
-            if top then
-                wa2.height = wa2.height - useless_gap
-                wa2.y = wa2.y - (useless_gap / 2)
-            else
-                wa2.height = wa2.height - (useless_gap / 2)
-            end
-
-            if left then
-                wa2.width = wa2.width - useless_gap
-                wa2.x = wa2.x - (useless_gap / 2)
-            else
-                wa2.width = wa2.width - (useless_gap / 2)
-            end
-        end
-        -- End of useless gap.
-
-        c:geometry(wa2)
-    end
-end
-
---- Dwindle layout
-uselesspiral.dwindle = {}
-uselesspiral.dwindle.name = "uselessdwindle"
-function uselesspiral.dwindle.arrange(p)
-    return spiral(p, false)
-end
-
---- Spiral layout
-uselesspiral.name = "uselesspiral"
-function uselesspiral.arrange(p)
-    return spiral(p, true)
-end
-
-return uselesspiral
diff --git a/layout/uselesstile.lua b/layout/uselesstile.lua
deleted file mode 100644 (file)
index ee33060..0000000
+++ /dev/null
@@ -1,231 +0,0 @@
-
---[[
-                                                  
-     Licensed under GNU General Public License v2 
-      * (c) 2014, projektile, worron              
-      * (c) 2013, Luke Bonham                     
-      * (c) 2009, Donald Ephraim Curtis           
-      * (c) 2008, Julien Danjolu                  
-                                                  
---]]
-
-local tag       = require("awful.tag")
-local beautiful = require("beautiful")
-local ipairs    = ipairs
-local math      = { floor = math.floor,
-                    ceil  = math.ceil,
-                    max   = math.max,
-                    min   = math.min }
-local tonumber  = tonumber
-
-local uselesstile = {}
-
--- Transformation functions
-local function flip(canvas, geometry)
-    return {
-        -- vertical only
-        x = 2 * canvas.x + canvas.width - geometry.x - geometry.width,
-        y = geometry.y,
-        width = geometry.width,
-        height = geometry.height
-    }
-end
-
-local function swap(geometry)
-    return { x = geometry.y, y = geometry.x, width = geometry.height, height = geometry.width }
-end
-
--- Find geometry for secondary windows column
-local function cut_column(wa, n, index)
-    local width = math.floor(wa.width / n)
-    local area = { x = wa.x + (index - 1) * width, y = wa.y, width = width, height = wa.height }
-
-    return area
-end
-
--- Find geometry for certain window in column
-local function cut_row(wa, factor, index, used)
-    local height = math.floor(wa.height * factor.window[index] / factor.total)
-    local area = { x = wa.x, y = wa.y + used, width = wa.width, height = height }
-
-    return area
-end
-
--- Client geometry correction depending on useless gap and window border
-local function size_correction(c, geometry, useless_gap)
-    geometry.width  = math.max(geometry.width  - 2 * c.border_width - useless_gap, 1)
-    geometry.height = math.max(geometry.height - 2 * c.border_width - useless_gap, 1)
-    geometry.x = geometry.x + useless_gap / 2
-    geometry.y = geometry.y + useless_gap / 2
-end
-
--- Check size factor for group of clients and calculate total
-local function calc_factor(n, winfactors)
-    local factor = { window = winfactors, total = 0, min = 1 }
-
-    for i = 1, n do
-        if not factor.window[i] then
-            factor.window[i] = factor.min
-        else
-            factor.min = math.min(factor.window[i], factor.min)
-            if factor.window[i] < 0.05 then factor.window[i] = 0.05 end
-        end
-        factor.total = factor.total + factor.window[i]
-    end
-
-    return factor
-end
-
--- Tile group of clients in given area
--- @canvas need for proper transformation only
--- @winfactors table with clients size factors
-local function tile_column(canvas, area, list, useless_gap, transformation, winfactors)
-    local used = 0
-    local factor = calc_factor(#list, winfactors)
-
-    for i, c in ipairs(list) do
-        local g = cut_row(area, factor, i, used)
-        if i == #list then g.height = area.height - used end
-        used = used + g.height
-
-        -- swap workarea dimensions
-        if transformation.flip then g = flip(canvas, g) end
-        if transformation.swap then g = swap(g) end
-
-        -- useless gap and border correction
-        size_correction(c, g, useless_gap)
-
-
-        c:geometry(g)
-    end
-end
-
---Main tile function
-local function tile(p, orientation)
-
-    -- Theme vars
-    local useless_gap = beautiful.useless_gap_width or 0
-    local global_border = beautiful.global_border_width or 0
-
-    -- Aliases
-    local wa = p.workarea
-    local cls = p.clients
-    local t = tag.selected(p.screen)
-
-    -- Nothing to tile here
-    if #cls == 0 then return end
-
-    -- Get tag prop
-    local nmaster = math.min(tag.getnmaster(t), #cls)
-    local mwfact = tag.getmwfact(t)
-
-    if nmaster == 0 then
-        mwfact = 0
-    elseif nmaster == #cls then
-        mwfact = 1
-    end
-
-    -- clients size factor
-    local data = tag.getdata(t).windowfact
-
-    if not data then
-        data = {}
-        tag.getdata(t).windowfact = data
-    end
-
-    -- Workarea size correction depending on useless gap and global border
-    wa.height = wa.height - 2 * global_border - useless_gap
-    wa.width  = wa.width -  2 * global_border - useless_gap
-    wa.x = wa.x + useless_gap / 2 + global_border
-    wa.y = wa.y + useless_gap / 2 + global_border
-
-    -- Find which transformation we need for given orientation
-    local transformation = {
-        swap = orientation == 'top' or orientation == 'bottom',
-        flip = orientation == 'left' or orientation == 'top'
-    }
-
-    -- Swap workarea dimensions if orientation vertical
-    if transformation.swap then wa = swap(wa) end
-
-    -- Split master and other windows
-    local cls_master, cls_other = {}, {}
-
-    for i, c in ipairs(cls) do
-        if i <= nmaster then
-            table.insert(cls_master, c)
-        else
-            table.insert(cls_other, c)
-        end
-    end
-
-    -- Tile master windows
-    local master_area = {
-        x = wa.x,
-        y = wa.y,
-        width  = nmaster > 0 and math.floor(wa.width * mwfact) or 0,
-        height = wa.height
-    }
-
-    if not data[0] then data[0] = {} end
-    tile_column(wa, master_area, cls_master, useless_gap, transformation, data[0])
-
-    -- Tile other windows
-    local other_area = {
-        x = wa.x + master_area.width,
-        y = wa.y,
-        width  = wa.width - master_area.width,
-        height = wa.height
-    }
-
-    -- get column number for other windows
-    local ncol = math.min(tag.getncol(t), #cls_other)
-
-    if ncol == 0 then ncol = 1 end
-
-    -- split other windows to column groups
-    local last_small_column = ncol - #cls_other % ncol
-    local rows_min = math.floor(#cls_other / ncol)
-
-    local client_index = 1
-    local used = 0
-    for i = 1, ncol do
-        local position = transformation.flip and ncol - i + 1 or i
-        local rows = i <= last_small_column and rows_min or rows_min + 1
-        local column = {}
-
-        for j = 1, rows do
-            table.insert(column, cls_other[client_index])
-            client_index = client_index + 1
-        end
-
-        -- and tile
-        local column_area = cut_column(other_area, ncol, position)
-        if i == ncol then column_area.width = other_area.width - used end
-        used = used + column_area.width
-
-        if not data[i] then data[i] = {} end
-        tile_column(wa, column_area, column, useless_gap, transformation, data[i])
-    end
-end
-
--- Layout constructor
-local function construct_layout(name, orientation)
-    return {
-        name = name,
-        -- @p screen number to tile
-        arrange = function(p) return tile(p, orientation) end
-    }
-end
-
--- Build layouts with different tile direction
-uselesstile.right  = construct_layout("uselesstile", "right")
-uselesstile.left   = construct_layout("uselesstileleft", "left")
-uselesstile.bottom = construct_layout("uselesstilebottom", "bottom")
-uselesstile.top    = construct_layout("uselesstiletop", "top")
-
--- Module aliase
-uselesstile.arrange = uselesstile.right.arrange
-uselesstile.name = uselesstile.right.name
-
-return uselesstile
index 1de819a94ba2813867b8fb2d19197fafc8e0a871..46974735ff5994e7dd7de4a3cdb24e63436bced9 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env sh
 #
 #   Adapted from Eridan's "fs" (cleanup, enhancements and switch to bash/Linux)
 #   JM,  10/12/2004
diff --git a/scripts/mpdcover b/scripts/mpdcover
deleted file mode 100755 (executable)
index 6f9062c..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-#!/bin/bash
-#
-# A simple cover fetcher script for current playing song on mpd.
-#
-# Original author: Wolfgang Mueller
-#
-# Adapted for Lain internal use.
-# https://github.com/copycat-killer/lain
-#
-# You can use, edit and redistribute this script in any way you like.
-#
-# Dependencies: imagemagick.
-#
-# Usage: mpdcover <music_directory> <song_file> <cover_resize> <default_art>
-
-# Configuration-------------------------------------------------------
-
-# Music directory
-MUSIC_DIR=$1
-
-# Song file
-file=$2
-
-# Regex expression used for image search
-IMG_REG="(Front|front|Cover|cover|Art|art|Folder|folder)\.(jpg|jpeg|png|gif)$"
-
-# Path of temporary resized cover
-TEMP_PATH="/tmp/mpdcover.png"
-
-# Resize cover
-COVER_RESIZE="$3x$3"
-
-if [ $COVER_RESIZE == "x" ]; then
-    COVER_RESIZE="100x100"
-fi
-
-# The default cover to use (optional)
-DEFAULT_ART=$4
-
-# Thumbnail background (transparent)
-COVER_BACKGROUND="none"
-
-#--------------------------------------------------------------------
-
-# check if anything is playing at all
-[[ -z $file ]] && exit 1
-
-# Art directory
-art="$MUSIC_DIR/${file%/*}"
-
-# find every file that matches IMG_REG set the first matching file to be the
-# cover.
-cover="$(find "$art/" -maxdepth 1 -type f | egrep -i -m1 "$IMG_REG")"
-
-# when no cover is found, use DEFAULT_ART as cover
-cover="${cover:=$DEFAULT_ART}"
-
-# check if art is available
-if [[ -n $cover ]]; then
-   if [[ -n $COVER_RESIZE ]]; then
-        convert "$cover" -scale $COVER_RESIZE -gravity "center" -background "$COVER_BACKGROUND" "$TEMP_PATH"
-        cover="$TEMP_PATH"
-   fi
-else
-   rm $TEMP_PATH
-fi
-
-exit 0
index 5fe8213dfae623bc906cefde22eb3ad137810cd9..9c4dc063ddb14a214c6b5521ba846b0bd1263f34 100644 (file)
 --]]
 
 local awful        = require("awful")
-local beautiful    = require("beautiful")
-local math         = { sqrt = math.sqrt }
-local mouse        = mouse
+local sqrt         = math.sqrt
 local pairs        = pairs
-local string       = { gsub = string.gsub }
 local client       = client
-local screen       = screen
 local tonumber     = tonumber
-
 local wrequire     = require("lain.helpers").wrequire
 local setmetatable = setmetatable
 
@@ -29,26 +24,23 @@ local setmetatable = setmetatable
 -- lain.util
 local util = { _NAME = "lain.util" }
 
--- Like awful.menu.clients, but only show clients of currently selected
--- tags.
+-- Like awful.menu.clients, but only show clients of currently selected tags
 function util.menu_clients_current_tags(menu, args)
     -- List of currently selected tags.
-    local cls_tags = awful.tag.selectedlist(mouse.screen)
+    local cls_tags = awful.screen.focused().selected_tags
+
+    if cls_tags == nil then return nil end
 
     -- Final list of menu items.
     local cls_t = {}
 
-    if cls_tags == nil then return nil end
-
     -- For each selected tag get all clients of that tag and add them to
     -- the menu. A click on a menu item will raise that client.
-    for i = 1,#cls_tags
-    do
-        local t = cls_tags[i]
+    for i = 1,#cls_tags do
+        local t   = cls_tags[i]
         local cls = t:clients()
 
-        for k, c in pairs(cls)
-        do
+        for k, c in pairs(cls) do
             cls_t[#cls_t + 1] = { awful.util.escape(c.name) or "",
                                   function ()
                                       c.minimized = false
@@ -68,167 +60,109 @@ function util.menu_clients_current_tags(menu, args)
 
     -- Set the list of items and show the menu.
     menu.items = cls_t
-    local m = awful.menu.new(menu)
+    local m = awful.menu(menu)
     m:show(args)
+
     return m
 end
 
--- Magnify a client: Set it to "float" and resize it.
-local magnified_client = nil
+-- Magnify a client: set it to "float" and resize it.
 function util.magnify_client(c)
-    if c and not awful.client.floating.get(c) then
+    if c and not c.floating then
         util.mc(c)
-        magnified_client = c
+        util.magnified_client = c
     else
-        awful.client.floating.set(c, false)
-        magnified_client = nil
+        c.floating = false
+        util.magnified_client = nil
     end
 end
 
 -- https://github.com/copycat-killer/lain/issues/195
 function util.mc(c)
-    c = c or magnified_client
+    c = c or util.magnified_client
     if not c then return end
-    awful.client.floating.set(c, true)
-    local mg = screen[mouse.screen].geometry
-    local tag = awful.tag.selected(mouse.screen)
-    local mwfact = awful.tag.getmwfact(tag)
-    local g = {}
-    g.width = math.sqrt(mwfact) * mg.width
-    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
-    if c then c:geometry(g) end -- if c is still a valid object
-end
-
--- Read the nice value of pid from /proc.
-local function get_nice_value(pid)
-    local n = first_line('/proc/' .. pid .. '/stat')
-    if not n then return 0 end
-
-    -- Remove pid and tcomm. This is necessary because tcomm may contain
-    -- nasty stuff such as whitespace or additional parentheses...
-    n = string.gsub(n, '.*%) ', '')
 
-    -- Field number 17 now is the nice value.
-    fields = split(n, ' ')
-    return tonumber(fields[17])
-end
-
--- To be used as a signal handler for "focus"
--- This requires beautiful.border_focus{,_highprio,_lowprio}.
-function util.niceborder_focus(c)
-    local n = get_nice_value(c.pid)
-    if n == 0
-    then
-        c.border_color = beautiful.border_focus
-    elseif n < 0
-    then
-        c.border_color = beautiful.border_focus_highprio
-    else
-        c.border_color = beautiful.border_focus_lowprio
-    end
-end
+    c.floating   = true
+    local s      = awful.screen.focused()
+    local mg     = s.geometry
+    local mwfact = s.selected_tag.master_width_factor or 0.5
+    local g      = {}
+    g.width      = sqrt(mwfact) * mg.width
+    g.height     = sqrt(mwfact) * mg.height
+    g.x          = mg.x + (mg.width - g.width) / 2
+    g.y          = mg.y + (mg.height - g.height) / 2
 
--- To be used as a signal handler for "unfocus"
--- This requires beautiful.border_normal{,_highprio,_lowprio}.
-function util.niceborder_unfocus(c)
-    local n = get_nice_value(c.pid)
-    if n == 0
-    then
-        c.border_color = beautiful.border_normal
-    elseif n < 0
-    then
-        c.border_color = beautiful.border_normal_highprio
-    else
-        c.border_color = beautiful.border_normal_lowprio
-    end
+    if c then c:geometry(g) end -- if c is still a valid object
 end
 
 -- Non-empty tag browsing
 -- direction in {-1, 1} <-> {previous, next} non-empty tag
 function util.tag_view_nonempty(direction, sc)
-   local s = sc or mouse.screen or 1
-   local scr = screen[s]
+   local s = sc or awful.screen.focused()
 
-   for i = 1, #awful.tag.gettags(s) do
-       awful.tag.viewidx(direction,s)
-       if #awful.client.visible(s) > 0 then
+   for i = 1, #s.tags do
+       awful.tag.viewidx(direction, s)
+       if #s.clients > 0 then
            return
        end
    end
 end
 
 -- {{{ Dynamic tagging
---
+
 -- Add a new tag
-function util.add_tag(mypromptbox)
-    awful.prompt.run({prompt="New tag name: "}, mypromptbox[mouse.screen].widget,
-    function(text)
-        if text:len() > 0 then
-            props = { selected = true }
-            tag = awful.tag.add(new_name, props)
-            tag.name = text
-            tag:emit_signal("property::name")
+function util.add_tag()
+    awful.prompt.run {
+        prompt       = "New tag name: ",
+        textbox      = awful.screen.focused().mypromptbox.widget,
+        exe_callback = function(name)
+            if not name or #name == 0 then return end
+            awful.tag.add(name, { screen = awful.screen.focused() }):view_only()
         end
-    end)
+    }
 end
 
 -- Rename current tag
--- @author: minism
-function util.rename_tag(mypromptbox)
-    local tag = awful.tag.selected(mouse.screen)
-    awful.prompt.run({prompt="Rename tag: "}, mypromptbox[mouse.screen].widget,
-    function(text)
-        if text:len() > 0 then
-            tag.name = text
-            tag:emit_signal("property::name")
+function util.rename_tag()
+    awful.prompt.run {
+        prompt       = "Rename tag: ",
+        textbox      = awful.screen.focused().mypromptbox.widget,
+        exe_callback = function(new_name)
+            if not new_name or #new_name == 0 then return end
+            local t = awful.screen.focused().selected_tag
+            if t then
+                t.name = new_name
+            end
         end
-    end)
+    }
 end
 
 -- Move current tag
 -- pos in {-1, 1} <-> {previous, next} tag position
 function util.move_tag(pos)
-    local tag = awful.tag.selected(mouse.screen)
-    local idx = awful.tag.getidx(tag)
+    local tag = awful.screen.focused().selected_tag
     if tonumber(pos) <= -1 then
-        awful.tag.move(idx - 1, tag)
+        awful.tag.move(tag.index - 1, tag)
     else
-        awful.tag.move(idx + 1, tag)
+        awful.tag.move(tag.index + 1, tag)
     end
 end
 
--- Remove current tag (if empty)
+-- Delete current tag
 -- Any rule set on the tag shall be broken
-function util.remove_tag()
-    local tag = awful.tag.selected(mouse.screen)
-    local prevtag = awful.tag.gettags(mouse.screen)[awful.tag.getidx(tag) - 1]
-    awful.tag.delete(tag, prevtag)
+function util.delete_tag()
+    local t = awful.screen.focused().selected_tag
+    if not t then return end
+    t:delete()
 end
---
+
 -- }}}
 
 -- On the fly useless gaps change
 function util.useless_gaps_resize(thatmuch)
-    beautiful.useless_gap_width = tonumber(beautiful.useless_gap_width) + thatmuch
-    awful.layout.arrange(mouse.screen)
-end
-
--- On the fly global border change
-function util.global_border_resize(thatmuch)
-    beautiful.global_border_width = tonumber(beautiful.global_border_width) + thatmuch
-    awful.layout.arrange(mouse.screen)
-end
-
--- Check if an element exist on a table
-function util.element_in_table(element, tbl)
-    for _, i in pairs(tbl) do
-        if i == element then
-            return true
-        end
-    end
-    return false
+    local scr = awful.screen.focused()
+    scr.selected_tag.gap = scr.selected_tag.gap + tonumber(thatmuch)
+    awful.layout.arrange(scr)
 end
 
 return setmetatable(util, { __index = wrequire })
index d367bca51c69eac91b8a8073b25ff99726229848..0d3b17a1b1da7b8655a3ceda569871c34e7e8e5e 100644 (file)
@@ -8,58 +8,56 @@
                                  
 --]]
 
-local beautiful    = require("beautiful")
-local tostring     = tostring
+local string       = { format = string.format }
 local setmetatable = setmetatable
 
 -- Lain markup util submodule
 -- lain.util.markup
-local markup = {}
-
-local fg = {}
-local bg = {}
+local markup = { fg = {}, bg = {} }
 
 -- Convenience tags.
-function markup.bold(text)      return '<b>'     .. tostring(text) .. '</b>'     end
-function markup.italic(text)    return '<i>'     .. tostring(text) .. '</i>'     end
-function markup.strike(text)    return '<s>'     .. tostring(text) .. '</s>'     end
-function markup.underline(text) return '<u>'     .. tostring(text) .. '</u>'     end
-function markup.monospace(text) return '<tt>'    .. tostring(text) .. '</tt>'    end
-function markup.big(text)       return '<big>'   .. tostring(text) .. '</big>'   end
-function markup.small(text)     return '<small>' .. tostring(text) .. '</small>' end
+function markup.bold(text)      return '<b>'     .. text .. '</b>'     end
+function markup.italic(text)    return '<i>'     .. text .. '</i>'     end
+function markup.strike(text)    return '<s>'     .. text .. '</s>'     end
+function markup.underline(text) return '<u>'     .. text .. '</u>'     end
+function markup.monospace(text) return '<tt>'    .. text .. '</tt>'    end
+function markup.big(text)       return '<big>'   .. text .. '</big>'   end
+function markup.small(text)     return '<small>' .. text .. '</small>' end
 
 -- Set the font.
 function markup.font(font, text)
-  return '<span font="'  .. tostring(font)  .. '">' .. tostring(text) ..'</span>'
+  return '<span font="'  .. font  .. '">' .. text ..'</span>'
 end
 
 -- Set the foreground.
-function fg.color(color, text)
-  return '<span foreground="' .. tostring(color) .. '">' .. tostring(text) .. '</span>'
+function markup.fg.color(color, text)
+  return '<span foreground="' .. color .. '">' .. text .. '</span>'
 end
 
 -- Set the background.
-function bg.color(color, text)
-  return '<span background="' .. tostring(color) .. '">' .. tostring(text) .. '</span>'
+function markup.bg.color(color, text)
+  return '<span background="' .. color .. '">' .. text .. '</span>'
 end
 
--- Context: focus
-function fg.focus(text) return fg.color(beautiful.fg_focus, text) end
-function bg.focus(text) return bg.color(beautiful.bg_focus, text) end
-function markup.focus(text) return bg.focus(fg.focus(text)) end
+-- Set foreground and background.
+function markup.color(fg, bg, text)
+  return string.format('<span foreground="%s" background="%s">%s</span>', fg, bg, text)
+end
 
--- Context: normal
-function fg.normal(text) return fg.color(beautiful.fg_normal, text) end
-function bg.normal(text) return bg.color(beautiful.bg_normal, text) end
-function markup.normal(text) return bg.normal(fg.normal(text)) end
+-- Set font and foreground.
+function markup.fontfg(font, fg, text)
+  return string.format('<span font="%s" foreground="%s">%s</span>', font, fg, text)
+end
 
--- Context: urgent
-function fg.urgent(text) return fg.color(beautiful.fg_urgent, text) end
-function bg.urgent(text) return bg.color(beautiful.bg_urgent, text) end
-function markup.urgent(text) return bg.urgent(fg.urgent(text)) end
+-- Set font and background.
+function markup.fontbg(font, bg, text)
+  return string.format('<span font="%s" background="%s">%s</span>', font, bg, text)
+end
 
-markup.fg = fg
-markup.bg = bg
+-- Set font, foreground and background.
+function markup.fontcolor(font, fg, bg, text)
+  return string.format('<span font="%s" foreground="%s" background="%s">%s</span>', font, fg, bg, text)
+end
 
 -- link markup.{fg,bg}(...) calls to markup.{fg,bg}.color(...)
 setmetatable(markup.fg, { __call = function(_, ...) return markup.fg.color(...) end })
index 771741e4cc3ea3eca3983c09dd6883bd2ecf33aa..4c428039df23ec9c6d41732280219c5ec1b6fa7d 100644 (file)
@@ -3,22 +3,22 @@
                                                    
      Licensed under GNU General Public License v2  
       * (c) 2016, Luke Bonham                      
+      * (c) 2015, unknown                          
                                                    
 --]]
 
-local awful  = require("awful")
-local capi   = { client = client,
-                 mouse  = mouse,
-                 screen = screen,
-                 timer  = timer }
-local string = string
+local awful        = require("awful")
+local capi         = { client = client }
+
+local math         = { floor  = math.floor }
+local string       = { format = string.format }
 
 local pairs        = pairs
+local screen       = screen
+
 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,
@@ -26,137 +26,144 @@ local quake = {}
 -- 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
+    if self.followtag then self.screen = awful.screen.focused() end
+
+    -- 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
+        cmd = string.format("%s %s %s", self.app,
+              string.format(self.argname, self.name), self.extra)
+        awful.spawn(cmd, { tag = self.screen.selected_tag })
+        return
+    end
+
+    -- Set geometry
+    client.floating = true
+    client.border_width = self.border
+    client.size_hints_honor = false
+    client:geometry(self:compute_size())
+
+    -- Set not sticky and on top
+    client.sticky = false
+    client.ontop = true
+    client.above = true
+    client.skip_taskbar = true
+
+    -- Additional user settings
+    if self.settings then self.settings(client) end
+
+    -- Toggle display
+    if self.visible then
+        client.hidden = false
+        client:raise()
+        self.last_tag = self.screen.selected_tag
+        client:tags({self.screen.selected_tag})
+        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
+        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:compute_size()
+    -- skip if we already have a geometry for this screen
+    if not self.geometry[self.screen] then
+        local geom
+        if not self.overlap then
+            geom = screen[self.screen].workarea
+        else
+            geom = screen[self.screen].geometry
+        end
+        local width, height = self.width, self.height
+        if width  <= 1 then width = math.floor(geom.width * width) - 2 * self.border end
+        if height <= 1 then height = math.floor(geom.height * height) end
+        local x, y
+        if     self.horiz == "left"  then x = geom.x
+        elseif self.horiz == "right" then x = geom.width + geom.x - width
+        else   x = geom.x + (geom.width - width)/2 end
+        if     self.vert == "top"    then y = geom.y
+        elseif self.vert == "bottom" then y = geom.height + geom.y - height
+        else   y = geom.y + (geom.height - height)/2 end
+        self.geometry[self.screen] = { x = x, y = y, width = width, height = height }
+    end
+    return self.geometry[self.screen]
 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
+    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.border     = conf.border    or 1          -- client border width
+    conf.visible    = conf.visible   or false      -- initially not visible
+    conf.followtag  = conf.followtag or false      -- spawn on currently focused screen
+    conf.overlap    = conf.overlap   or false      -- overlap wibox
+    conf.screen     = conf.screen    or awful.screen.focused()
+    conf.settings   = conf.settings
+
+    -- If width or height <= 1 this is a proportion of the workspace
+    conf.height     = conf.height    or 0.25       -- height
+    conf.width      = conf.width     or 1          -- width
+    conf.vert       = conf.vert      or "top"      -- top, bottom or center
+    conf.horiz      = conf.horiz     or "left"     -- left, right or center
+    conf.geometry   = {}                           -- internal use
+
+    local dropdown = setmetatable(conf, { __index = quake })
+
+    capi.client.connect_signal("manage", function(c)
+        if c.instance == dropdown.name and c.screen == dropdown.screen then
+            dropdown:display()
+        end
     end)
+    capi.client.connect_signal("unmanage", function(c)
+        if c.instance == dropdown.name and c.screen == dropdown.screen then
+            dropdown.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
+    return dropdown
 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
+     if self.followtag then self.screen = awful.screen.focused() end
+     local current_tag = self.screen.selected_tag
+     if current_tag and self.last_tag ~= current_tag and self.visible then
+         self:display():move_to_tag(current_tag)
+     else
+         self.visible = not self.visible
+         self:display()
+     end
 end
 
-setmetatable(quake, { __call = function(_, ...) return quake:new(...) end })
-
-return quake
+return setmetatable(quake, { __call = function(_, ...) return quake:new(...) end })
index 6e5ef96f9be785b53e470090fc438b1f0cff5f2e..abf57c4b9c0e75892127147dc2df135e5e7761f7 100644 (file)
@@ -8,15 +8,11 @@
 --]]
 
 local wibox     = require("wibox")
-local beautiful = require("beautiful")
 local gears     = require("gears")
 
 -- Lain Cairo separators util submodule
 -- lain.util.separators
-local separators = {}
-
-local height = beautiful.awful_widget_height or 0
-local width  = beautiful.separators_width or 9
+local separators = { height = 0, width = 9 }
 
 -- [[ Arrow
 
@@ -24,7 +20,9 @@ local width  = beautiful.separators_width or 9
 function separators.arrow_right(col1, col2)
     local widget = wibox.widget.base.make_widget()
 
-    widget.fit = function(m, w, h) return width, height end
+    widget.fit = function(m, w, h)
+        return separators.width, separators.height
+    end
 
     widget.draw = function(mycross, wibox, cr, width, height)
         if col2 ~= "alpha" then
@@ -62,7 +60,9 @@ end
 function separators.arrow_left(col1, col2)
     local widget = wibox.widget.base.make_widget()
 
-    widget.fit = function(m, w, h) return width, height end
+    widget.fit = function(m, w, h)
+        return separators.width, separators.height
+    end
 
     widget.draw = function(mycross, wibox, cr, width, height)
         if col1 ~= "alpha" then
index fb20d768222e974e83f6af9b2360c05a5290eaf5..300cc1b54d6832274fcfe8e87aca04d05843951c 100644 (file)
@@ -6,27 +6,26 @@
                                                   
 --]]
 
-local newtimer     = require("lain.helpers").newtimer
-local async        = require("lain.asyncshell")
-local wibox        = require("wibox")
-
+local helpers      = require("lain.helpers")
+local textbox      = require("wibox.widget.textbox")
 local setmetatable = setmetatable
 
--- Basic template for custom widgets
--- Asynchronous version
+-- Template for custom asynchronous widgets
 -- lain.widgets.abase
 
 local function worker(args)
-    local abase    = {}
-    local args     = args or {}
-    local timeout  = args.timeout or 5
-    local cmd      = args.cmd or ""
-    local settings = args.settings or function() end
+    local abase     = {}
+    local args      = args or {}
+    local timeout   = args.timeout or 5
+    local nostart   = args.nostart or false
+    local stoppable = args.stoppable or false
+    local cmd       = args.cmd
+    local settings  = args.settings or function() widget:set_text(output) end
 
-    abase.widget = wibox.widget.textbox('')
+    abase.widget = args.widget or textbox()
 
     function abase.update()
-        async.request(cmd, function(f)
+        helpers.async(cmd, function(f)
             output = f
             if output ~= abase.prev then
                 widget = abase.widget
@@ -36,9 +35,9 @@ local function worker(args)
         end)
     end
 
-    newtimer(cmd, timeout, abase.update)
+    abase.timer = helpers.newtimer(cmd, timeout, abase.update, nostart, stoppable)
 
-    return setmetatable(abase, { __index = abase.widget })
+    return abase
 end
 
 return setmetatable({}, { __call = function(_, ...) return worker(...) end })
index 7500097b05eac9e0cc3bcc695d9f4b44551e5522..f73194b2a6c9b23dfc35394ed48864fd856e3dad 100644 (file)
@@ -7,21 +7,18 @@
                                                   
 --]]
 
-local newtimer     = require("lain.helpers").newtimer
-local read_pipe    = require("lain.helpers").read_pipe
-
+local helpers      = require("lain.helpers")
+local shell        = require("awful.util").shell
 local wibox        = require("wibox")
-
 local string       = { match  = string.match,
                        format = string.format }
-
 local setmetatable = setmetatable
 
 -- ALSA volume
 -- lain.widgets.alsa
-local alsa = { last_level = "0", last_status = "" }
 
 local function worker(args)
+    local alsa     = { widget = wibox.widget.textbox() }
     local args     = args or {}
     local timeout  = args.timeout or 5
     local settings = args.settings or function() end
@@ -29,31 +26,31 @@ local function worker(args)
     alsa.cmd           = args.cmd or "amixer"
     alsa.channel       = args.channel or "Master"
     alsa.togglechannel = args.togglechannel
-    alsa.widget        = wibox.widget.textbox('')
+
+    local format_cmd = string.format("%s get %s", alsa.cmd, alsa.channel)
+
+    if alsa.togglechannel then
+        format_cmd = { shell, "-c", string.format("%s get %s; %s get %s",
+        alsa.cmd, alsa.channel, alsa.cmd, alsa.togglechannel) }
+    end
+
+    alsa.last = {}
 
     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
-            alsa.last_status = s
-
-            widget = alsa.widget
-            settings()
-        end
+        helpers.async(format_cmd, function(mixer)
+            local l,s = string.match(mixer, "([%d]+)%%.*%[([%l]*)")
+            if alsa.last.level ~= l or alsa.last.status ~= s then
+                volume_now = { level = l, status = s }
+                widget = alsa.widget
+                settings()
+                alsa.last = volume_now
+            end
+        end)
     end
 
-    timer_id = string.format("alsa-%s-%s", alsa.cmd, alsa.channel)
-    newtimer(timer_id, timeout, alsa.update)
+    helpers.newtimer(string.format("alsa-%s-%s", alsa.cmd, alsa.channel), timeout, alsa.update)
 
-    return setmetatable(alsa, { __index = alsa.widget })
+    return alsa
 end
 
-return setmetatable(alsa, { __call = function(_, ...) return worker(...) end })
+return setmetatable({}, { __call = function(_, ...) return worker(...) end })
index b20bc4025b1287712bb1929e182aa7e7ebb2481f..628e8386fd527ccec7729cd9d08943116a7ddac4 100644 (file)
                                                   
 --]]
 
-local newtimer     = require("lain.helpers").newtimer
-local read_pipe    = require("lain.helpers").read_pipe
-
+local helpers      = require("lain.helpers")
 local awful        = require("awful")
-local beautiful    = require("beautiful")
 local naughty      = require("naughty")
-
+local wibox        = require("wibox")
 local math         = { modf   = math.modf }
-local mouse        = mouse
 local string       = { format = string.format,
                        match  = string.match,
                        rep    = string.rep }
+local type         = type
 local tonumber     = tonumber
-
 local setmetatable = setmetatable
 
 -- ALSA volume bar
 -- lain.widgets.alsabar
 local alsabar = {
-    channel = "Master",
-    step    = "1%",
-
     colors = {
-        background = beautiful.bg_normal,
+        background = "#000000",
         mute       = "#EB8F8F",
         unmute     = "#A4CE8A"
     },
 
-    terminal = terminal or "xterm",
-    mixer    = terminal .. " -e alsamixer",
-
-    notifications = {
-        font      = beautiful.font:sub(beautiful.font:find(""), beautiful.font:find(" ")),
-        font_size = "11",
-        color     = beautiful.fg_normal,
-        bar_size  = 18,
-        screen    = 1
-    },
-
     _current_level = 0,
     _muted         = false
 }
 
-function alsabar.notify()
-    alsabar.update()
-
-    local preset = {
-        title   = "",
-        text    = "",
-        timeout = 5,
-        screen  = alsabar.notifications.screen,
-        font    = alsabar.notifications.font .. " " ..
-                  alsabar.notifications.font_size,
-        fg      = alsabar.notifications.color
-    }
-
-    if alsabar._muted
-    then
-        preset.title = alsabar.channel .. " - Muted"
-    else
-        preset.title = alsabar.channel .. " - " .. alsabar._current_level .. "%"
-    end
-
-    int = math.modf((alsabar._current_level / 100) * alsabar.notifications.bar_size)
-    preset.text = "["
-                .. string.rep("|", int)
-                .. string.rep(" ", alsabar.notifications.bar_size - int)
-                .. "]"
-
-    if alsabar.followmouse then
-        preset.screen = mouse.screen
-    end
-
-    if alsabar._notify ~= nil then
-        alsabar._notify = naughty.notify ({
-            replaces_id = alsabar._notify.id,
-            preset      = preset,
-        })
-    else
-        alsabar._notify = naughty.notify ({
-            preset = preset,
-        })
-    end
-end
-
 local function worker(args)
     local args       = args or {}
     local timeout    = args.timeout or 5
     local settings   = args.settings or function() end
     local width      = args.width or 63
-    local height     = args.heigth or 1
+    local height     = args.height or 1
     local ticks      = args.ticks or false
     local ticks_size = args.ticks_size or 7
     local vertical   = args.vertical or false
 
-    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
-    alsabar.followmouse   = args.followmouse or false
+    alsabar.cmd                 = args.cmd or "amixer"
+    alsabar.channel             = args.channel or "Master"
+    alsabar.togglechannel       = args.togglechannel
+    alsabar.colors              = args.colors or alsabar.colors
+    alsabar.followtag           = args.followtag or false
+    alsabar.notification_preset = args.notification_preset
+
+    if not alsabar.notification_preset then
+        alsabar.notification_preset      = {}
+        alsabar.notification_preset.font = "Monospace 10"
+    end
+
+    local format_cmd = string.format("%s get %s", alsabar.cmd, alsabar.channel)
 
-    alsabar.bar = awful.widget.progressbar()
+    if alsabar.togglechannel then
+        format_cmd = { awful.util.shell, "-c", string.format("%s get %s; %s get %s",
+        alsabar.cmd, alsabar.channel, alsabar.cmd, alsabar.togglechannel) }
+    end
+
+    alsabar.bar = wibox.widget {
+        forced_height    = height,
+        forced_width     = width,
+        color            = alsabar.colors.unmute,
+        background_color = alsabar.colors.background,
+        margins          = 1,
+        paddings         = 1,
+        ticks            = ticks,
+        ticks_size       = ticks_size,
+        widget           = wibox.widget.progressbar,
+        layout           = vertical and wibox.container.rotate
+    }
 
-    alsabar.bar:set_background_color(alsabar.colors.background)
-    alsabar.bar:set_color(alsabar.colors.unmute)
     alsabar.tooltip = awful.tooltip({ objects = { alsabar.bar } })
-    alsabar.bar:set_width(width)
-    alsabar.bar:set_height(height)
-    alsabar.bar:set_ticks(ticks)
-    alsabar.bar:set_ticks_size(ticks_size)
-    alsabar.bar:set_vertical(vertical)
-
-    function alsabar.update()
-        -- Get mixer control contents
-        local mixer = read_pipe(string.format("%s get %s", alsabar.cmd, alsabar.channel))
-
-        -- 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) or alsabar._current_level
-            alsabar.bar:set_value(alsabar._current_level / 100)
-            if not mute and tonumber(volu) == 0 or mute == "off"
-            then
-                alsabar._muted = true
-                alsabar.tooltip:set_text (" [Muted] ")
-                alsabar.bar:set_color(alsabar.colors.mute)
+
+    function alsabar.update(callback)
+        helpers.async(format_cmd, function(mixer)
+            local volu,mute = string.match(mixer, "([%d]+)%%.*%[([%l]*)")
+            if (volu and tonumber(volu) ~= alsabar._current_level) or (mute and string.match(mute, "on") ~= alsabar._muted) then
+                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
+                    alsabar._muted = true
+                    alsabar.tooltip:set_text ("[Muted]")
+                    alsabar.bar.color = alsabar.colors.mute
+                else
+                    alsabar._muted = false
+                    alsabar.tooltip:set_text(string.format("%s: %s", alsabar.channel, volu))
+                    alsabar.bar.color = alsabar.colors.unmute
+                end
+
+                volume_now = {}
+                volume_now.level = tonumber(volu)
+                volume_now.status = mute
+
+                settings()
+
+                if type(callback) == "function" then callback() end
+            end
+        end)
+    end
+
+    function alsabar.notify()
+        alsabar.update(function()
+            local preset = alsabar.notification_preset
+
+            if alsabar._muted then
+                preset.title = string.format("%s - Muted", alsabar.channel)
             else
-                alsabar._muted = false
-                alsabar.tooltip:set_text(string.format(" %s:%s ", alsabar.channel, volu))
-                alsabar.bar:set_color(alsabar.colors.unmute)
+                preset.title = string.format("%s - %s%%", alsabar.channel, alsabar._current_level)
             end
 
-            volume_now = {}
-            volume_now.level = tonumber(volu)
-            volume_now.status = mute
-            settings()
-        end
+            int = math.modf((alsabar._current_level / 100) * awful.screen.focused().mywibox.height)
+            preset.text = string.format("[%s%s]", string.rep("|", int),
+                          string.rep(" ", awful.screen.focused().mywibox.height - int))
+
+            if alsabar.followtag then preset.screen = awful.screen.focused() end
+
+            if not alsabar.notification then
+                alsabar.notification = naughty.notify {
+                    preset  = preset,
+                    destroy = function() alsabar.notification = nil end
+                }
+            else
+                naughty.replace_text(alsabar.notification, preset.title, preset.text)
+            end
+        end)
     end
 
-    alsabar.bar:buttons(awful.util.table.join (
-          awful.button({}, 1, function()
-            awful.util.spawn(alsabar.mixer)
-          end),
-          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.util.spawn(string.format("%s set %s %s+", alsabar.cmd, alsabar.channel, alsabar.step))
-            alsabar.update()
-          end),
-          awful.button({}, 5, function()
-            awful.util.spawn(string.format("%s set %s %s-", alsabar.cmd, alsabar.channel, alsabar.step))
-            alsabar.update()
-          end)
-    ))
-
-    timer_id = string.format("alsabar-%s-%s", alsabar.cmd, alsabar.channel)
-
-    newtimer(timer_id, timeout, alsabar.update)
+    helpers.newtimer(string.format("alsabar-%s-%s", alsabar.cmd, alsabar.channel), timeout, alsabar.update)
 
     return alsabar
 end
diff --git a/widgets/base.lua b/widgets/base.lua
deleted file mode 100644 (file)
index cd84e36..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-
---[[
-                                                  
-     Licensed under GNU General Public License v2 
-      * (c) 2014, Luke Bonham                     
-                                                  
---]]
-
-local newtimer     = require("lain.helpers").newtimer
-local read_pipe    = require("lain.helpers").read_pipe
-
-local wibox        = require("wibox")
-
-local setmetatable = setmetatable
-
--- Basic template for custom widgets
--- lain.widgets.base
-
-local function worker(args)
-    local base     = {}
-    local args     = args or {}
-    local timeout  = args.timeout or 5
-    local cmd      = args.cmd or ""
-    local settings = args.settings or function() end
-
-    base.widget = wibox.widget.textbox('')
-
-    function base.update()
-        output = read_pipe(cmd)
-        if output ~= base.prev then
-            widget = base.widget
-            settings()
-            base.prev = output
-        end
-    end
-
-    newtimer(cmd, timeout, base.update)
-
-    return setmetatable(base, { __index = base.widget })
-end
-
-return setmetatable({}, { __call = function(_, ...) return worker(...) end })
index f63b1fa2700644e1ce7e695f7cab2c2bc56daee5..e479fd9b3413a1a38f6fe5d629cfbd2622a58c88 100644 (file)
@@ -7,18 +7,16 @@
                                                                                                                        
 --]]
 
-local newtimer     = require("lain.helpers").newtimer
 local first_line   = require("lain.helpers").first_line
-
+local newtimer     = require("lain.helpers").newtimer
 local naughty      = require("naughty")
 local wibox        = require("wibox")
-
 local math         = { abs    = math.abs,
                        floor  = math.floor,
                        log10  = math.log10,
                        min    = math.min }
 local string       = { format = string.format }
-
+local ipairs       = ipairs
 local type         = type
 local tonumber     = tonumber
 local setmetatable = setmetatable
@@ -27,7 +25,7 @@ local setmetatable = setmetatable
 -- lain.widgets.bat
 
 local function worker(args)
-    local bat       = {}
+    local bat       = { widget = wibox.widget.textbox() }
     local args      = args or {}
     local timeout   = args.timeout or 30
     local batteries = args.batteries or (args.battery and {args.battery}) or {"BAT0"}
@@ -35,8 +33,6 @@ local function worker(args)
     local notify    = args.notify or "on"
     local settings  = args.settings or function() end
 
-    bat.widget = wibox.widget.textbox('')
-
     bat_notification_low_preset = {
         title   = "Battery low",
         text    = "Plug the cable!",
@@ -110,35 +106,48 @@ local function worker(args)
             end
         end
 
+        -- When one of the battery is charging, others' status are either
+        -- "Full", "Unknown" or "Charging". When the laptop is not plugged in,
+        -- one or more of the batteries may be full, but only one battery
+        -- discharging suffices to set global status to "Discharging".
         bat_now.status = bat_now.n_status[1]
+        for _,status in ipairs(bat_now.n_status) do
+            if status == "Discharging" or status == "Charging" then
+                bat_now.status = status
+            end
+        end
         bat_now.ac_status = tonumber(first_line(string.format("%s%s/online", pspath, ac))) or "N/A"
 
         if bat_now.status ~= "N/A" then
+            if 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
+
             -- 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
+            elseif bat_now.status ~= "Full" 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
-
-                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
+                -- Calculate time and watt if rates are greater then 0
+                if (sum_rate_power > 0 or sum_rate_current > 0) then
+                    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
+
+                    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
+                 end
 
                 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"
@@ -165,9 +174,9 @@ local function worker(args)
         end
     end
 
-    newtimer(battery, timeout, bat.update)
+    newtimer("batteries", timeout, bat.update)
 
-    return setmetatable(bat, { __index = bat.widget })
+    return bat
 end
 
 return setmetatable({}, { __call = function(_, ...) return worker(...) end })
diff --git a/widgets/borderbox.lua b/widgets/borderbox.lua
deleted file mode 100644 (file)
index 149b835..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-
---[[
-                                                  
-     Licensed under GNU General Public License v2 
-      * (c) 2013,      Luke Bonham                
-      * (c) 2010-2012, Peter Hofmann              
-                                                  
---]]
-
-local wibox = require("awful.wibox")
-local setmetatable = setmetatable
-
--- Creates a thin wibox at a position relative to another wibox
--- lain.widgets.borderbox
-local borderbox = {}
-
-local function worker(relbox, s, args)
-    local where    = args.position or 'top'
-    local color    = args.color or '#FFFFFF'
-    local size     = args.size or 1
-    local box      = nil
-    local wiboxarg = { position = nil, bg = color }
-
-    if where == 'top'
-    then
-        wiboxarg.width = relbox.width
-        wiboxarg.height = size
-        box = wibox(wiboxarg)
-        box.x = relbox.x
-        box.y = relbox.y - size
-    elseif where == 'bottom'
-    then
-        wiboxarg.width = relbox.width
-        wiboxarg.height = size
-        box = wibox(wiboxarg)
-        box.x = relbox.x
-        box.y = relbox.y + relbox.height
-    elseif where == 'left'
-    then
-        wiboxarg.width = size
-        wiboxarg.height = relbox.height
-        box = wibox(wiboxarg)
-        box.x = relbox.x - size
-        box.y = relbox.y
-    elseif where == 'right'
-    then
-        wiboxarg.width = size
-        wiboxarg.height = relbox.height
-        box = wibox(wiboxarg)
-        box.x = relbox.x + relbox.width
-        box.y = relbox.y
-    end
-
-    box.screen = s
-    return box
-end
-
-return setmetatable(borderbox, { __call = function(_, ...) return worker(...) end })
index 1728dfc2aad3ebd76bf8c0cfc0c6ce124887256d..f351356f7527fee968d41669a7e7f41077514860 100644 (file)
@@ -6,54 +6,43 @@
                                                   
 --]]
 
-local icons_dir    = require("lain.helpers").icons_dir
-
+local helpers      = require("lain.helpers")
+local markup       = require("lain.util.markup")
 local awful        = require("awful")
-local beautiful    = require("beautiful")
 local naughty      = require("naughty")
-
-local io           = { popen = io.popen }
-local os           = { date = os.date }
-local mouse        = mouse
+local os           = { date   = os.date }
 local string       = { format = string.format,
-                       sub    = string.sub,
                        gsub   = string.gsub }
+local ipairs       = ipairs
 local tonumber     = tonumber
-
 local setmetatable = setmetatable
 
 -- Calendar notification
 -- lain.widgets.calendar
-local calendar = {}
-local cal_notification = nil
+local calendar = { offset = 0 }
 
 function calendar.hide()
-    if cal_notification ~= nil then
-        naughty.destroy(cal_notification)
-        cal_notification = nil
-    end
+    if not calendar.notification then return end
+    naughty.destroy(calendar.notification)
+    calendar.notification = nil
 end
 
 function calendar.show(t_out, inc_offset, scr)
-    calendar.hide()
-
-    local f, c_text
-    local offs  = inc_offset or 0
-    local tims  = t_out or 0
-    local today = tonumber(os.date('%d'))
+    local today = os.date("%e")
+    local offs = inc_offset or 0
+    local f
 
     calendar.offset = calendar.offset + offs
 
-    if offs == 0 or calendar.offset == 0
-    then -- current month showing, today highlighted
-        calendar.offset = 0
-        calendar.notify_icon = calendar.icons .. today .. ".png"
+    local current_month = (offs == 0 or calendar.offset == 0)
 
-        -- bg and fg inverted to highlight today
-        f = io.popen(calendar.cal_format(today))
+    if current_month then -- today highlighted
+        calendar.offset = 0
+        calendar.notify_icon = string.format("%s%s.png", calendar.icons, tonumber(today))
+        f = calendar.cal
     else -- no current month showing, no day to highlight
-       local month = tonumber(os.date('%m'))
-       local year = tonumber(os.date('%Y'))
+       local month = tonumber(os.date("%m"))
+       local year  = tonumber(os.date("%Y"))
 
        month = month + calendar.offset
 
@@ -68,55 +57,30 @@ function calendar.show(t_out, inc_offset, scr)
        end
 
        calendar.notify_icon = nil
-       f = io.popen(string.format('%s %s %s', calendar.cal, month, year))
+       f = string.format("%s %s %s", calendar.cal, month, year)
     end
 
-    c_text = "<tt><span font='" .. calendar.font .. " "
-             .. calendar.font_size .. "'><b>"
-             .. f:read() .. "</b>\n\n"
-             .. f:read() .. "\n"
-             .. f:read("*all"):gsub("\n*$", "")
-             .. "</span></tt>"
-    f:close()
-
-    if calendar.followmouse then
-        scrp = mouse.screen
+    if calendar.followtag then
+        calendar.notification_preset.screen = awful.screen.focused()
     else
-        scrp = scr or calendar.scr_pos
+        calendar.notification_preset.screen = src or 1
     end
 
-    cal_notification = naughty.notify({
-        text = c_text,
-        icon = calendar.notify_icon,
-        position = calendar.position,
-        fg = calendar.fg,
-        bg = calendar.bg,
-        timeout = tims,
-        screen = scrp
-    })
+    helpers.async(f, function(ws)
+        fg, bg = calendar.notification_preset.fg, calendar.notification_preset.bg
+        ws = ws:gsub("%c%[%d+[m]?%s?%d+%c%[%d+[m]?", markup.bold(markup.color(bg, fg, today)))
+        calendar.hide()
+        calendar.notification = naughty.notify({
+            preset      = calendar.notification_preset,
+            text        = ws:gsub("\n*$", ""),
+            icon        = calendar.notify_icon,
+            timeout     = t_out or calendar.notification_preset.timeout or 5
+        })
+    end)
 end
 
-function calendar.attach(widget, args)
-    local args = args or {}
-
-    calendar.cal         = args.cal or "/usr/bin/cal"
-    calendar.cal_format  = args.cal_format or function(today)
-        return string.format("%s | sed -r -e 's/_\\x08//g' -e '0,/(^| )%d($| )/ s/(^| )%d($| )/\\1<b><span foreground=\"%s\" background=\"%s\">%d<\\/span><\\/b>\\2/'",
-                             calendar.cal, today, today, calendar.bg, calendar.fg, today)
-    end
-    calendar.icons       = args.icons or icons_dir .. "cal/white/"
-    calendar.font        = args.font or beautiful.font:gsub(" %d.*", "")
-    calendar.font_size   = tonumber(args.font_size) or 11
-    calendar.fg          = args.fg or beautiful.fg_normal or "#FFFFFF"
-    calendar.bg          = args.bg or beautiful.bg_normal or "#000000"
-    calendar.position    = args.position or "top_right"
-    calendar.scr_pos     = args.scr_pos or 1
-    calendar.followmouse = args.followmouse or false
-
-    calendar.offset      = 0
-    calendar.notify_icon = nil
-
-    widget:connect_signal("mouse::enter", function () calendar.show(0, 0, calendar.scr_pos) end)
+function calendar.attach(widget)
+    widget:connect_signal("mouse::enter", function () calendar.show(0) 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),
@@ -128,4 +92,23 @@ function calendar.attach(widget, args)
                                              calendar.show(0, 1, calendar.scr_pos) end)))
 end
 
-return setmetatable(calendar, { __call = function(_, ...) return create(...) end })
+local function worker(args)
+    local args                   = args or {}
+    calendar.cal                 = args.cal or "/usr/bin/cal"
+    calendar.attach_to           = args.attach_to or {}
+    calendar.followtag           = args.followtag or false
+    calendar.icons               = args.icons or helpers.icons_dir .. "cal/white/"
+    calendar.notification_preset = args.notification_preset
+
+    if not calendar.notification_preset then
+        calendar.notification_preset = {
+            font = "Monospace 10",
+            fg   = "#FFFFFF",
+            bg   = "#000000"
+        }
+    end
+
+    for i, widget in ipairs(calendar.attach_to) do calendar.attach(widget) end
+end
+
+return setmetatable(calendar, { __call = function(_, ...) return worker(...) end })
index fa514407f61589762fbc03cae64237b7e4dd6f95..94076c85b6f9e72721f2380a5012f697c0a3afa5 100644 (file)
@@ -6,14 +6,15 @@
                                                      
 --]]
 
-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 helpers      = require("lain.helpers")
+local json         = require("lain.util.dkjson")
+local focused      = require("awful.screen").focused
+local pread        = require("awful.util").pread
+local naughty      = require("naughty")
+local wibox        = require("wibox")
+local next         = next
+local os           = { getenv = os.getenv }
+local table        = table
 local setmetatable = setmetatable
 
 -- Google Play Music Desktop infos
@@ -24,12 +25,12 @@ 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 followtag     = args.followtag 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.widget = wibox.widget.textbox()
 
     gpmdp_notification_preset = {
         title   = "Now playing",
@@ -39,14 +40,13 @@ local function worker(args)
     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 }
+        local filelines = helpers.lines_from(file_location)
+
+        if not next(filelines) then
+            local gpm_now = { running = false, playing = false }
         else
-            dict, pos, err = json.decode(file:read "*a", 1, nil)
-            file:close()
-            gpm_now = {}
+            dict, pos, err = json.decode(table.concat(filelines), 1, nil)
+            local gpm_now = {}
             gpm_now.artist    = dict.song.artist
             gpm_now.album     = dict.song.album
             gpm_now.title     = dict.song.title
@@ -54,7 +54,7 @@ local function worker(args)
             gpm_now.playing   = dict.playing
         end
 
-        if (pread("pidof 'Google Play Music Desktop Player'") ~= '') then
+        if pread("pidof 'Google Play Music Desktop Player'") ~= '' then
             gpm_now.running = true
         else
             gpm_now.running = false
@@ -64,32 +64,29 @@ local function worker(args)
         widget = gpmdp.widget
         settings()
 
-        if gpm_now.playing
-        then
-            if notify == "on" and gpm_now.title ~= helpers.get_map("gpmdp_current")
-            then
+        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
+                if followtag then gpmdp_notification_preset.screen = focused() end
 
-                gpmdp.id = naughty.notify({
-                    preset = gpmdp_notification_preset,
-                    icon = "/tmp/gpmcover.png",
-                    replaces_id = gpmdp.id,
-                }).id
+                helpers.async(string.format("curl %d -o /tmp/gpmcover.png", gpm_now.cover_url),
+                function(f)
+                    gpmdp.id = naughty.notify({
+                        preset = gpmdp_notification_preset,
+                        icon = "/tmp/gpmcover.png",
+                        replaces_id = gpmdp.id
+                    }).id
+                end)
             end
-        elseif not gpm_now.running
-        then
+        elseif not gpm_now.running then
             helpers.set_map("gpmdp_current", nil)
         end
     end
 
-    helpers.newtimer("gpmdp", timeout, gpmdp.update)
+    gpmdp.timer = helpers.newtimer("gpmdp", timeout, gpmdp.update, true, true)
 
-    return setmetatable(gpmdp, { __index = gpmdp.widget })
+    return gpmdp
 end
 
 return setmetatable(gpmdp, { __call = function(_, ...) return worker(...) end })
index 23dc6e3cb975b15e34c7f93883ab045478559de3..08e7bd06a3002f013712a42af88c5e99efa74b2d 100644 (file)
@@ -6,24 +6,21 @@
                                                   
 --]]
 
-local newtimer     = require("lain.helpers").newtimer
-local read_pipe    = require("lain.helpers").read_pipe
-
-local wibox        = require("wibox")
+local helpers      = require("lain.helpers")
 local awful        = require("awful")
-
-local string       = { match = string.match }
-
+local wibox        = require("wibox")
+local string       = { format = string.format,
+                       match  = string.match }
+local execute      = os.execute
 local setmetatable = setmetatable
 
 -- Keyboard layout switcher
 -- lain.widgets.contrib.kblayout
+local kbdlayout = {}
 
 local function worker(args)
-   local kbdlayout    = {}
-   kbdlayout.widget   = wibox.widget.textbox('')
-
-   local layouts          = args.layouts
+   local args             = args or {}
+   local layouts          = args.layouts or {}
    local settings         = args.settings or function () end
    local add_us_secondary = true
    local timeout          = args.timeout or 5
@@ -31,52 +28,53 @@ local function worker(args)
 
    if args.add_us_secondary == false then add_us_secondary = false end
 
-   -- Mouse bindings
-   kbdlayout.widget:buttons(awful.util.table.join(
-                              awful.button({ }, 1, function () kbdlayout.next() end),
-                              awful.button({ }, 3, function () kbdlayout.prev() end)))
+   kbdlayout.widget = wibox.widget.textbox()
 
-   local function run_settings(layout, variant)
+   local function kbd_run_settings(layout, variant)
+      kbdlayout_now = {
+          layout  = string.match(layout, "[^,]+"), -- Make sure to match the primary layout only.
+          variant = 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()
-      local status = read_pipe('setxkbmap -query')
-
-      run_settings(string.match(status, "layout:%s*([^\n]*)"),
-                   string.match(status, "variant:%s*([^\n]*)"))
+      helpers.async("setxkbmap -query", function(status)
+          kbd_run_settings(string.match(status, "layout:%s*([^\n]*)"),
+          string.match(status, "variant:%s*([^\n]*)"))
+      end)
    end
 
    function kbdlayout.set(i)
+      if #layouts == 0 then return end
       idx = ((i - 1) % #layouts) + 1 -- Make sure to wrap around as needed.
-      local to_execute = 'setxkbmap ' .. layouts[idx].layout
+      local to_execute = "setxkbmap " .. layouts[idx].layout
 
       if add_us_secondary and not string.match(layouts[idx].layout, ",?us,?") then
          to_execute = to_execute .. ",us"
       end
 
       if layouts[idx].variant then
-         to_execute = to_execute .. ' ' .. layouts[idx].variant
+         to_execute = to_execute .. " " .. layouts[idx].variant
       end
 
-      if os.execute(to_execute) then
-         run_settings(layouts[idx].layout, layouts[idx].variant)
+      if execute(to_execute) then
+         kbd_run_settings(layouts[idx].layout, layouts[idx].variant)
       end
    end
 
-   function kbdlayout.next()
-      kbdlayout.set(idx + 1)
-   end
+   function kbdlayout.next() kbdlayout.set(idx + 1) end
+   function kbdlayout.prev() kbdlayout.set(idx - 1) end
 
-   function kbdlayout.prev()
-      kbdlayout.set(idx - 1)
-   end
+   -- Mouse bindings
+   kbdlayout.widget:buttons(awful.util.table.join(
+                              awful.button({ }, 1, function () kbdlayout.next() end),
+                              awful.button({ }, 3, function () kbdlayout.prev() end)))
+
+   helpers.newtimer("kbdlayout", timeout, kbdlayout.update)
 
-   newtimer("kbdlayout", timeout, kbdlayout.update)
-   return setmetatable(kbdlayout, { __index = kbdlayout.widget })
+   return kbdlayout
 end
 
 return setmetatable({}, { __call = function (_, ...) return worker(...) end })
index b79dbe2034ae44d071ade51ea9c976c606c4735d..6fe96a15d994ec3368c909cb0a76171885af2385 100644 (file)
@@ -6,19 +6,15 @@
                                                                   
 --]]
 
-local helpers = require("lain.helpers")
-local async   = require("lain.asyncshell")
-
-local escape_f = require("awful.util").escape
-local naughty  = require("naughty")
-local wibox    = require("wibox")
-
-local io     = { popen   = io.popen }
-local os     = { execute = os.execute,
-                 getenv  = os.getenv }
-local string = { format  = string.format,
-                 gmatch  = string.gmatch }
-
+local helpers      = require("lain.helpers")
+local shell        = require("awful.util").shell
+local focused      = require("awful.screen").focused
+local escape_f     = require("awful.util").escape
+local naughty      = require("naughty")
+local wibox        = require("wibox")
+local os           = { getenv = os.getenv }
+local string       = { format = string.format,
+                       gmatch = string.gmatch }
 local setmetatable = setmetatable
 
 -- MOC audio player
@@ -26,31 +22,23 @@ local setmetatable = setmetatable
 local moc = {}
 
 local function worker(args)
-    local args        = args or {}
-    local timeout     = args.timeout or 2
-    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 followmouse = args.followmouse or false
-    local settings    = args.settings or function() end
+    local args          = args or {}
+    local timeout       = args.timeout or 2
+    local music_dir     = args.music_dir or os.getenv("HOME") .. "/Music"
+    local cover_pattern = args.cover_pattern or "*\\.(jpg|jpeg|png|gif)$"
+    local cover_size    = args.cover_size or 100
+    local default_art   = args.default_art or ""
+    local followtag     = args.followtag or false
+    local settings      = args.settings or function() end
 
-    local mpdcover = helpers.scripts_dir .. "mpdcover"
+    moc.widget = wibox.widget.textbox()
 
-    moc.widget = wibox.widget.textbox('')
-
-    moc_notification_preset = {
-        title   = "Now playing",
-        timeout = 6
-    }
+    moc_notification_preset = { title = "Now playing", timeout = 6 }
 
     helpers.set_map("current moc track", nil)
 
     function moc.update()
-        -- mocp -i will produce output like:
-        -- Artist: Travis
-        -- Album: The Man Who
-        -- etc.
-        async.request("mocp -i", function(f)
+        helpers.async("mocp -i", function(f)
             moc_now = {
                 state   = "N/A",
                 file    = "N/A",
@@ -63,13 +51,13 @@ local function worker(args)
 
             for line in string.gmatch(f, "[^\n]+") do
                 for k, v in string.gmatch(line, "([%w]+):[%s](.*)$") do
-                    if k == "State" then moc_now.state = v
-                    elseif k == "File" then moc_now.file = v
-                    elseif k == "Artist" then moc_now.artist = escape_f(v)
-                    elseif k == "SongTitle" then moc_now.title = escape_f(v)
-                    elseif k == "Album" then moc_now.album = escape_f(v)
+                    if     k == "State"       then moc_now.state   = v
+                    elseif k == "File"        then moc_now.file    = v
+                    elseif k == "Artist"      then moc_now.artist  = escape_f(v)
+                    elseif k == "SongTitle"   then moc_now.title   = escape_f(v)
+                    elseif k == "Album"       then moc_now.album   = escape_f(v)
                     elseif k == "CurrentTime" then moc_now.elapsed = escape_f(v)
-                    elseif k == "TotalTime" then moc_now.total = escape_f(v)
+                    elseif k == "TotalTime"   then moc_now.total   = escape_f(v)
                     end
                 end
             end
@@ -82,18 +70,22 @@ local function worker(args)
             if moc_now.state == "PLAY" then
                 if moc_now.title ~= helpers.get_map("current moc track") then
                     helpers.set_map("current moc track", moc_now.title)
-                    os.execute(string.format("%s %q %q %d %q", mpdcover, "",
-                               moc_now.file, cover_size, default_art))
 
-                    if followmouse then
-                        moc_notification_preset.screen = mouse.screen
-                    end
+                    if followtag then moc_notification_preset.screen = focused() end
 
-                    moc.id = naughty.notify({
-                        preset = moc_notification_preset,
-                        icon = "/tmp/mpdcover.png",
+                    local common =  {
+                        preset      = moc_notification_preset,
+                        icon        = default_art,
+                        icon_size   = cover_size,
                         replaces_id = moc.id,
-                    }).id
+                    }
+
+                    local path   = string.format("%s/%s", music_dir, string.match(moc_now.file, ".*/"))
+                    local cover  = string.format("find '%s' -maxdepth 1 -type f | egrep -i -m1 '%s'", path, cover_pattern)
+                    helpers.async({ shell, "-c", cover }, function(current_icon)
+                        common.icon = current_icon:gsub("\n", "")
+                        moc.id = naughty.notify(common).id
+                    end)
                 end
             elseif  moc_now.state ~= "PAUSE" then
                 helpers.set_map("current moc track", nil)
@@ -101,9 +93,9 @@ local function worker(args)
         end)
     end
 
-    helpers.newtimer("moc", timeout, moc.update)
+    moc.timer = helpers.newtimer("moc", timeout, moc.update, true, true)
 
-    return setmetatable(moc, { __index = moc.widget })
+    return moc
 end
 
 return setmetatable(moc, { __call = function(_, ...) return worker(...) end })
index 69247ee7716b2a55038e9630d41fc53db39d99ec..73cc100773197f17089116c2bdd5aac78c7fcefb 100644 (file)
@@ -6,73 +6,49 @@
                                                         
 --]]
 
-local awful        = require("awful")
-local os           = os
-local spawn        = awful.util.spawn_with_shell
-
-local setmetatable = setmetatable
+local async   = require("lain.helpers").async
+local awful   = require("awful")
+local execute = os.execute
+local type    = type
 
 -- Redshift
 -- 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 function init()
-    -- As there is no way to determine if redshift was previously
-    -- toggled off (i.e Awesome on-the-fly restart), kill redshift to make sure
-    os.execute("pkill redshift")
-    -- Remove existing color adjustment
-    spawn("redshift -x")
-    -- (Re)start redshift
-    spawn("redshift")
-    running = true
-    active = true
-end
-
-function redshift:toggle()
-    if running then
-        -- Sending -USR1 toggles redshift (See project website)
-        os.execute("pkill -USR1 redshift")
-        active = not active
-    else
-        init()
+local redshift = { active = false, pid = nil }
+
+function redshift:start()
+    execute("pkill redshift")
+    awful.spawn.with_shell("redshift -x") -- clear adjustments
+    redshift.pid = awful.spawn.with_shell("redshift")
+    redshift.active = true
+    if type(redshift.update_fun) == "function" then
+        redshift.update_fun(redshift.active)
     end
-    update_fnct()
 end
 
-function redshift:off()
-    if running and active then
-        redshift:toggle()
-    end
-end
-
-function redshift:on()
-    if not active then
-        redshift:toggle()
-    end
-end
-
-function redshift:is_active()
-    return active
+function redshift:toggle()
+    async({ awful.util.shell, "-c", string.format("ps -p %d -o pid=", redshift.pid) }, function(f)
+        if f and #f > 0 then -- redshift is running
+            -- Sending -USR1 toggles redshift (See project website)
+            execute("pkill -USR1 redshift")
+            redshift.active = not redshift.active
+        else -- not started or killed, (re)start it
+            redshift:start()
+        end
+        redshift.update_fun(redshift.active)
+    end)
 end
 
 -- Attach to a widget
 -- Provides a button which toggles redshift on/off on click
 -- @param widget:  Widget to attach to.
--- @param fnct:    Function to be run each time redshift is toggled (optional).
+-- @param fun:     Function to be run each time redshift is toggled (optional).
 --                 Use it to update widget text or icons on status change.
-function redshift:attach(widget, fnct)
-    update_fnct  = fnct or function() end
-    if not attached then
-        init()
-        attached = true
-        update_fnct()
+function redshift:attach(widget, fun)
+    redshift.update_fun = fun or function() end
+    if not redshift.pid then redshift:start() end
+    if widget then
+        widget:buttons(awful.util.table.join(awful.button({}, 1, function () redshift:toggle() end)))
     end
-    widget:buttons(awful.util.table.join( awful.button({}, 1, function () redshift:toggle() end) ))
 end
 
-return setmetatable(redshift, { _call = function(_, ...) return create(...) end })
+return redshift
index fc438d9bfce40fc7534a91765473391657e77197..4a9b5b6fc6f023e9a71e43eaf3072ab0056a7a76 100644 (file)
                                                   
 --]]
 
-local icons_dir    = require("lain.helpers").icons_dir
-
-local awful        = require("awful")
-local beautiful    = require("beautiful")
-local naughty      = require("naughty")
-
-local mouse        = mouse
-local io           = io
-local string       = { len = string.len }
-local tonumber     = tonumber
-
-local setmetatable = setmetatable
+local helpers = require("lain.helpers")
+local markup  = require("lain.util").markup
+local awful   = require("awful")
+local naughty = require("naughty")
+local string  = { format = string.format, gsub = string.gsub }
 
 -- Taskwarrior notification
 -- lain.widgets.contrib.task
 local task = {}
 
-local task_notification = nil
-
-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
+    if not task.notification then return end
+    naughty.destroy(task.notification)
+    task.notification = nil
 end
 
-function task.show(scr_pos)
+function task.show(scr)
     task.hide()
 
-    local f, c_text, scrp
-
-    if task.followmouse then
-        scrp = mouse.screen
-    else
-        scrp = scr_pos or task.scr_pos
+    if task.followtag then
+        task.notification_preset.screen = awful.screen.focused()
+    elseif scr then
+        task.notification_preset.screen = scr
     end
 
-    f = io.popen('task ' .. task.cmdline)
-    c_text = "<span font='"
-             .. task.font .. " "
-             .. task.font_size .. "'>"
-             .. awful.util.escape(f:read("*all"):gsub("\n*$", ""))
-             .. "</span>"
-    f:close()
-
-    task_notification = naughty.notify({ title = "[task next]",
-                                         text = c_text,
-                                         icon = task.notify_icon,
-                                         position = task.position,
-                                         fg = task.fg,
-                                         bg = task.bg,
-                                         timeout = task.timeout,
-                                         screen = scrp
-                                     })
+    helpers.async(task.show_cmd, function(f)
+        task.notification = naughty.notify({
+            preset = task.notification_preset,
+            title  = task.show_cmd,
+            text   = markup.font(task.notification_preset.font,
+                     awful.util.escape(f:gsub("\n*$", "")))
+        })
+    end)
 end
 
-function task.prompt_add()
-  awful.prompt.run({ prompt = "Add task: " },
-      mypromptbox[mouse.screen].widget,
-      function (...)
-          local f = io.popen("task add " .. ...)
-          c_text = "\n<span font='"
-                   .. task.font .. " "
-                   .. task.font_size .. "'>"
-                   .. awful.util.escape(f:read("*all"))
-                   .. "</span>"
-          f:close()
-
-          naughty.notify({
-              text     = c_text,
-              icon     = task.notify_icon,
-              position = task.position,
-              fg       = task.fg,
-              bg       = task.bg,
-              timeout  = task.timeout,
-          })
-      end,
-      nil,
-      awful.util.getdir("cache") .. "/history_task_add")
-end
-
-function task.prompt_search()
-  awful.prompt.run({ prompt = "Search task: " },
-      mypromptbox[mouse.screen].widget,
-      function (...)
-          local f = io.popen("task " .. ...)
-          c_text = f:read("*all"):gsub(" \n*$", "")
-          f:close()
-
-          if string.len(c_text) == 0
-          then
-              c_text = "No results found."
-          else
-              c_text = "<span font='"
-                       .. task.font .. " "
-                       .. task.font_size .. "'>"
-                       .. awful.util.escape(c_text)
-                       .. "</span>"
-          end
-
-          naughty.notify({
-              title    = "[task next " .. ... .. "]",
-              text     = c_text,
-              icon     = task.notify_icon,
-              position = task.position,
-              fg       = task.fg,
-              bg       = task.bg,
-              timeout  = task.timeout,
-              screen   = mouse.screen
-          })
-      end,
-      nil,
-      awful.util.getdir("cache") .. "/history_task")
+function task.prompt()
+    awful.prompt.run {
+        prompt       = task.prompt_text,
+        textbox      = awful.screen.focused().mypromptbox.widget,
+        exe_callback = function(t)
+            helpers.async(t, function(f)
+                naughty.notify {
+                    preset = task.notification_preset,
+                    title    = t,
+                    text     = markup.font(task.notification_preset.font,
+                               awful.util.escape(f:gsub("\n*$", "")))
+                }
+            end)
+        end,
+        history_path = awful.util.getdir("cache") .. "/history_task"
+    }
 end
 
 function task.attach(widget, args)
-    local args       = args or {}
-
-    task.font_size   = tonumber(args.font_size) or 12
-    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"
+    local args               = args or {}
+    task.show_cmd            = args.show_cmd or "task next"
+    task.prompt_text         = args.prompt_text or "Enter task command: "
+    task.followtag           = args.followtag or false
+    task.notification_preset = args.notification_preset
+
+    if not task.notification_preset then
+        task.notification_preset = {
+            font = "Monospace 10",
+            icon = helpers.icons_dir .. "/taskwarrior.png"
+        }
+    end
 
-    widget:connect_signal("mouse::enter", function () task.show(task.scr_pos) end)
-    widget:connect_signal("mouse::leave", function () task.hide() end)
+    if widget then
+        widget:connect_signal("mouse::enter", function () task.show() end)
+        widget:connect_signal("mouse::leave", function () task.hide() end)
+    end
 end
 
-return setmetatable(task, { __call = function(_, ...) return create(...) end })
+return task
index 0a22526fedffb3e6937d86218861ff2d088bbfa1..20a10f4140e6dbf61e250449e050a13c353d9411 100644 (file)
 local debug        = { getinfo = debug.getinfo }
 local newtimer     = require("lain.helpers").newtimer
 local first_line   = require("lain.helpers").first_line
-local beautiful    = require("beautiful")
 local naughty      = require("naughty")
 local wibox        = require("wibox")
-
 local string       = { format = string.format }
 local math         = { floor = math.floor }
 local tostring     = tostring
 local setmetatable = setmetatable
-
 package.path       = debug.getinfo(1,"S").source:match[[^@?(.*[\/])[^\/]-$]] .. "?.lua;" .. package.path
 local smapi        = require("smapi")
 
 -- ThinkPad SMAPI-enabled battery info widget
 -- lain.widgets.contrib.tpbat
-local tpbat = { }
-local tpbat_notification = nil
+local tpbat = {}
 
-function tpbat:hide()
-    if tpbat_notification ~= nil
-    then
-        naughty.destroy(tpbat_notification)
-        tpbat_notification = nil
-    end
+function tpbat.hide()
+    if not tpbat.notification then return end
+    naughty.destroy(tpbat.notification)
+    tpbat.notification = nil
 end
 
-function tpbat:show(t_out)
-    tpbat:hide()
+function tpbat.show(t_out)
+    tpbat.hide()
 
-    local bat   = self.bat
-    local t_out = t_out or 0
+    local bat = tpbat.bat
 
     if bat == nil or not bat:installed() then return end
 
+    local t_out = t_out or 0
     local mfgr   = bat:get('manufacturer') or "no_mfgr"
     local model  = bat:get('model') or "no_model"
     local chem   = bat:get('chemistry') or "no_chem"
@@ -57,10 +51,8 @@ function tpbat:show(t_out)
     local time   = bat:remaining_time()
     local msg    = "\t"
 
-    if status ~= "idle" and status ~= "nil"
-    then
-        if time == "N/A"
-        then
+    if status ~= "idle" and status ~= "nil" then
+        if time == "N/A" then
             msg = "...Calculating time remaining..."
         else
             msg = time .. (status == "charging" and " until charged" or " remaining")
@@ -72,11 +64,10 @@ function tpbat:show(t_out)
     local str = string.format("%s : %s %s (%s)\n", bat.name, mfgr, model, chem)
                 .. string.format("\n%s \t\t\t %s", status:upper(), msg)
 
-    tpbat_notification = naughty.notify({
-        preset = { fg = beautiful.fg_normal },
-        text = str,
+    tpbat.notification = naughty.notify({
+        text    = str,
         timeout = t_out,
-        screen = client.focus and client.focus.screen or 1
+        screen  = client.focus and client.focus.screen or 1
     })
 end
 
@@ -89,7 +80,7 @@ function tpbat.register(args)
     tpbat.bat = smapi:battery(battery) -- Create a new battery
     local bat = tpbat.bat
 
-    tpbat.widget = wibox.widget.textbox('')
+    tpbat.widget = wibox.widget.textbox()
 
     bat_notification_low_preset = {
         title = "Battery low",
@@ -117,7 +108,7 @@ function tpbat.register(args)
         })
     end
 
-    function update()
+    function tpbat.update()
         bat_now = {
             status = "Not present",
             perc   = "N/A",
@@ -156,15 +147,16 @@ function tpbat.register(args)
         end
 
         widget = tpbat.widget
+
         settings()
     end
 
-    newtimer("tpbat-" .. bat.name, timeout, update)
+    newtimer("tpbat-" .. bat.name, timeout, tpbat.update)
 
-    widget:connect_signal('mouse::enter', function () tpbat:show() end)
-    widget:connect_signal('mouse::leave', function () tpbat:hide() end)
+    widget:connect_signal('mouse::enter', function () tpbat.show() end)
+    widget:connect_signal('mouse::leave', function () tpbat.hide() end)
 
-    return tpbat.widget
+    return tpbat
 end
 
 return setmetatable(tpbat, { __call = function(_, ...) return tpbat.register(...) end })
index 271d9c2d4c721223ddaef53729140708ab2808c5..9002585d69e55a9169ce3e2f6c4f9c70c1232218 100644 (file)
@@ -81,13 +81,11 @@ function smapi:battery(name)
         local time_val = bat_now.status == 'discharging' and 'remaining_running_time' or 'remaining_charging_time'
         local mins_left = self:get(time_val)
 
-        if mins_left:find("^%d+") == nil
-        then
-            return "N/A"
-        end
+        if not mins_left:find("^%d+") then return "N/A" end
 
         local hrs = math.floor(mins_left / 60)
         local min = mins_left % 60
+
         return string.format("%02d:%02d", hrs, min)
     end
 
index 737b6fa454c2cfefe01fb44fd83d65b60d8ef071..076b692c80279aac98b6035e8f5b74537d7f708d 100644 (file)
@@ -7,16 +7,12 @@
                                                   
 --]]
 
-local lines_match  = require("lain.helpers").lines_match
-local newtimer     = require("lain.helpers").newtimer
-
+local helpers      = require("lain.helpers")
 local wibox        = require("wibox")
-
 local math         = { ceil   = math.ceil }
 local string       = { format = string.format,
                        gmatch = string.gmatch }
 local tostring     = tostring
-
 local setmetatable = setmetatable
 
 -- CPU usage
@@ -25,28 +21,26 @@ local cpu = { core = {} }
 
 local function worker(args)
     local args     = args or {}
-         local timeout  = args.timeout or 2
-         local settings = args.settings or function() end
+    local timeout  = args.timeout or 2
+    local settings = args.settings or function() end
 
-         cpu.widget = wibox.widget.textbox('')
+    cpu.widget = wibox.widget.textbox()
 
-         function update()
+    function cpu.update()
         -- Read the amount of time the CPUs have spent performing
-                   -- different kinds of work. Read the first line of /proc/stat
-                   -- which is the sum of all CPUs.
-        local times = lines_match("cpu","/proc/stat")
+        -- different kinds of work. Read the first line of /proc/stat
+        -- which is the sum of all CPUs.
+        local times = helpers.lines_match("cpu","/proc/stat")
 
-        for index,time in pairs(times)
-        do
+        for index,time in pairs(times) do
             local coreid = index - 1
-            local core = cpu.core[coreid] or
-                  { last_active = 0 , last_total = 0, usage = 0 }
-            local at = 1
-            local idle = 0
-            local total = 0
-
-            for field in string.gmatch(time, "[%s]+([^%s]+)")
-            do
+            local core   = cpu.core[coreid] or
+                           { last_active = 0 , last_total = 0, usage = 0 }
+            local at     = 1
+            local idle   = 0
+            local total  = 0
+
+            for field in string.gmatch(time, "[%s]+([^%s]+)") do
                 -- 4 = idle, 5 = ioWait. Essentially, the CPUs have done
                 -- nothing during these times.
                 if at == 4 or at == 5 then
@@ -62,27 +56,27 @@ local function worker(args)
                 -- Read current data and calculate relative values.
                 local dactive = active - core.last_active
                 local dtotal  = total - core.last_total
-
-                local usage = math.ceil((dactive / dtotal) * 100)
+                local usage   = math.ceil((dactive / dtotal) * 100)
 
                 core.last_active = active
                 core.last_total  = total
                 core.usage       = usage
 
                 -- Save current data for the next run.
-                cpu.core[coreid] = core;
+                cpu.core[coreid] = core
             end
         end
 
-        widget = cpu.widget
         cpu_now = cpu.core
         cpu_now.usage = cpu_now[0].usage
+        widget = cpu.widget
 
         settings()
     end
 
-    newtimer("cpu", timeout, update)
-    return cpu.widget
+    helpers.newtimer("cpu", timeout, cpu.update)
+
+    return cpu
 end
 
 return setmetatable(cpu, { __call = function(_, ...) return worker(...) end })
index 6038746cc0d797c48c18d00841ebfe3dbae0de4c..ec87191766fbf79315944d92154a11d2ac2f277e 100644 (file)
@@ -1,62 +1,50 @@
 
 --[[
-                                                      
-     Licensed under GNU General Public License v2     
-      * (c) 2013, Luke Bonham                         
-      * (c) 2010, Adrian C.      <anrxc@sysphere.org> 
-      * (c) 2009, Lucas de Vries <lucas@glacicle.com> 
-                                                      
+                                                  
+     Licensed under GNU General Public License v2 
+      * (c) 2013, Luke Bonham                     
+                                                  
 --]]
 
 local helpers      = require("lain.helpers")
 
-local beautiful    = require("beautiful")
+local shell        = require("awful.util").shell
+local focused      = require("awful.screen").focused
 local wibox        = require("wibox")
 local naughty      = require("naughty")
 
-local io           = { popen  = io.popen }
-local pairs        = pairs
-local mouse        = mouse
-local string       = { match  = string.match,
-                       format = string.format }
+local string       = string
 local tonumber     = tonumber
 
 local setmetatable = setmetatable
 
 -- File system disk space usage
 -- lain.widgets.fs
-local fs = {}
-local fs_notification  = nil
+local fs = { unit  = { ["mb"] = 1024, ["gb"] = 1024^2 } }
 
 function fs.hide()
-    if fs_notification ~= nil then
-        naughty.destroy(fs_notification)
-        fs_notification = nil
-    end
+    if not fs.notification then return end
+    naughty.destroy(fs.notification)
+    fs.notification = nil
 end
 
-function fs.show(seconds, options, scr)
-    fs.hide()
+function fs.show(seconds, scr)
+    fs.update()
 
-    local cmd = (options and string.format("dfs %s", options)) or "dfs"
-    local ws = helpers.read_pipe(helpers.scripts_dir .. cmd):gsub("\n*$", "")
+    fs.hide()
 
-    if fs.followmouse then
-        fs.notification_preset.screen = mouse.screen
+    if fs.followtag then
+        fs.notification_preset.screen = focused()
     elseif scr then
-                         fs.notification_preset.screen = scr
+        fs.notification_preset.screen = scr or 1
     end
 
-    fs_notification = naughty.notify({
-        preset  = fs.notification_preset,
-        text    = ws,
-        timeout = seconds or 5
+    fs.notification = naughty.notify({
+        preset      = fs.notification_preset,
+        timeout     = seconds or 5
     })
 end
 
--- Unit definitions
-local unit = { ["mb"] = 1024, ["gb"] = 1024^2 }
-
 local function worker(args)
     local args             = args or {}
     local timeout          = args.timeout or 600
@@ -65,65 +53,78 @@ local function worker(args)
     local notify           = args.notify or "on"
     local settings         = args.settings or function() end
 
-    fs.followmouse         = args.followmouse or false
-    fs.notification_preset = args.notification_preset or { fg = beautiful.fg_normal }
+    fs.options             = args.options
+    fs.followtag           = args.followtag or false
+    fs.notification_preset = args.notification_preset
 
-    fs.widget = wibox.widget.textbox('')
+    if not fs.notification_preset then
+        fs.notification_preset = {
+            font = "Monospace 10",
+            fg   = "#FFFFFF",
+            bg   = "#000000"
+        }
+    end
+
+    fs.widget = wibox.widget.textbox()
 
     helpers.set_map(partition, false)
 
-    function update()
-        fs_info = {}
-        fs_now  = {}
-        local f = assert(io.popen("LC_ALL=C df -kP"))
-
-        for line in f:lines() do -- Match: (size) (used)(avail)(use%) (mount)
-            local s     = string.match(line, "^.-[%s]([%d]+)")
-            local u,a,p = string.match(line, "([%d]+)[%D]+([%d]+)[%D]+([%d]+)%%")
-            local m     = string.match(line, "%%[%s]([%p%w]+)")
-
-            if u and m then -- Handle 1st line and broken regexp
-                fs_info[m .. " size_mb"]  = string.format("%.1f", tonumber(s) / unit["mb"])
-                fs_info[m .. " size_gb"]  = string.format("%.1f", tonumber(s) / unit["gb"])
-                fs_info[m .. " used_p"]   = tonumber(p)
-                fs_info[m .. " avail_p"]  = 100 - tonumber(p)
+    function fs.update()
+        fs_info, fs_now  = {}, {}
+        helpers.async({ shell, "-c", "/usr/bin/env LC_ALL=C df -k --output=target,size,used,avail,pcent" }, function(f)
+            for line in string.gmatch(f, "\n[^\n]+") do
+                local m,s,u,a,p = string.match(line, "(/.-%s).-(%d+).-(%d+).-(%d+).-([%d]+)%%")
+                m = m:gsub(" ", "") -- clean target from any whitespace
+
+                fs_info[m .. " size_mb"]  = string.format("%.1f", tonumber(s) / fs.unit["mb"])
+                fs_info[m .. " size_gb"]  = string.format("%.1f", tonumber(s) / fs.unit["gb"])
+                fs_info[m .. " used_mb"]  = string.format("%.1f", tonumber(u) / fs.unit["mb"])
+                fs_info[m .. " used_gb"]  = string.format("%.1f", tonumber(u) / fs.unit["gb"])
+                fs_info[m .. " used_p"]   = p
+                fs_info[m .. " avail_mb"] = string.format("%.1f", tonumber(a) / fs.unit["mb"])
+                fs_info[m .. " avail_gb"] = string.format("%.1f", tonumber(a) / fs.unit["gb"])
+                fs_info[m .. " avail_p"]  = string.format("%d", 100 - tonumber(p))
+            end
+
+            fs_now.size_mb      = fs_info[partition .. " size_mb"]  or "N/A"
+            fs_now.size_gb      = fs_info[partition .. " size_gb"]  or "N/A"
+            fs_now.used         = fs_info[partition .. " used_p"]   or "N/A"
+            fs_now.used_mb      = fs_info[partition .. " used_mb"]  or "N/A"
+            fs_now.used_gb      = fs_info[partition .. " used_gb"]  or "N/A"
+            fs_now.available    = fs_info[partition .. " avail_p"]  or "N/A"
+            fs_now.available_mb = fs_info[partition .. " avail_mb"] or "N/A"
+            fs_now.available_gb = fs_info[partition .. " avail_gb"] or "N/A"
+
+            notification_preset = fs.notification_preset
+            widget = fs.widget
+            settings()
+
+            if notify == "on" and #fs_now.used > 0 and tonumber(fs_now.used) >= 99 and not helpers.get_map(partition) then
+                naughty.notify({
+                    preset = naughty.config.presets.critical,
+                    title  = "Warning",
+                    text   = partition .. " is empty",
+                })
+                helpers.set_map(partition, true)
+            else
+                helpers.set_map(partition, false)
             end
-        end
-
-        f:close()
-
-        fs_now.used      = tonumber(fs_info[partition .. " used_p"])  or 0
-        fs_now.available = tonumber(fs_info[partition .. " avail_p"]) or 0
-        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 notify == "on" and fs_now.used >= 99 and not helpers.get_map(partition)
-        then
-            naughty.notify({
-                title = "warning",
-                text = partition .. " ran out!\nmake some room",
-                timeout = 8,
-                fg = "#000000",
-                bg = "#FFFFFF",
-            })
-            helpers.set_map(partition, true)
-        else
-            helpers.set_map(partition, false)
-        end
+        end)
+
+        local notifycmd = (fs.options and string.format("dfs %s", fs.options)) or "dfs"
+        helpers.async(helpers.scripts_dir .. notifycmd, function(ws)
+            fs.notification_preset.text = ws:gsub("\n*$", "")
+        end)
     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)
+       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)
+    helpers.newtimer(partition, timeout, fs.update)
 
-    return setmetatable(fs, { __index = fs.widget })
+    return fs
 end
 
 return setmetatable(fs, { __call = function(_, ...) return worker(...) end })
index de2f7b454d5493191aca1c3c7d00e6425b9c328d..95d35fd0401617348b2ceb2b0a18e7e5516bcaa9 100644 (file)
@@ -7,77 +7,71 @@
 --]]
 
 local helpers      = require("lain.helpers")
-local async        = require("lain.asyncshell")
-
 local naughty      = require("naughty")
 local wibox        = require("wibox")
-
-local mouse        = mouse
 local string       = { format = string.format,
                        gsub   = string.gsub }
+local type         = type
 local tonumber     = tonumber
-
 local setmetatable = setmetatable
 
 -- Mail IMAP check
 -- lain.widgets.imap
 
 local function worker(args)
-    local imap        = {}
-    local args        = args or {}
-
-    local server      = args.server
-    local mail        = args.mail
-    local password    = args.password
-
-    local port        = args.port or 993
-    local timeout     = args.timeout or 60
-    local is_plain    = args.is_plain or false
-    local followmouse = args.followmouse or false
-    local settings    = args.settings or function() end
-
-    local head_command  = "curl --connect-timeout 3 -fsm 3"
+    local imap      = { widget = wibox.widget.textbox() }
+    local args      = args or {}
+    local server    = args.server
+    local mail      = args.mail
+    local password  = args.password
+    local port      = args.port or 993
+    local timeout   = args.timeout or 60
+    local is_plain  = args.is_plain or false
+    local followtag = args.followtag or false
+    local settings  = args.settings or function() end
+
+    local head_command = "curl --connect-timeout 3 -fsm 3"
     local request = "-X 'SEARCH (UNSEEN)'"
 
+    if not server or not mail or not password then return end
+
     helpers.set_map(mail, 0)
 
     if not is_plain then
-        password = helpers.read_pipe(password):gsub("\n", "")
+        if type(password) == "string" or type(password) == "table" then
+            helpers.async(password, function(f) password = f:gsub("\n", "") end)
+        elseif type(password) == "function" then
+            local p = password()
+        end
     end
 
-    imap.widget = wibox.widget.textbox('')
-
     function update()
         mail_notification_preset = {
             icon     = helpers.icons_dir .. "mail.png",
             position = "top_left"
         }
 
-        if followmouse then
-            mail_notification_preset.screen = mouse.screen
+        if followtag then
+            mail_notification_preset.screen = awful.screen.focused()
         end
 
         curl = string.format("%s --url imaps://%s:%s/INBOX -u %s:%q %s -k",
                head_command, server, port, mail, password, request)
 
-        async.request(curl, function(f)
+        helpers.async(curl, function(f)
             _, mailcount = string.gsub(f, "%d+", "")
             _ = nil
 
             widget = imap.widget
             settings()
 
-            if mailcount >= 1 and mailcount > helpers.get_map(mail)
-            then
+            if mailcount >= 1 and mailcount > helpers.get_map(mail) then
                 if mailcount == 1 then
                     nt = mail .. " has one new message"
                 else
                     nt = mail .. " has <b>" .. mailcount .. "</b> new messages"
                 end
-                naughty.notify({
-                    preset = mail_notification_preset,
-                    text = nt
-                })
+                naughty.notify({ preset = mail_notification_preset, text = nt })
             end
 
             helpers.set_map(mail, mailcount)
@@ -85,9 +79,9 @@ local function worker(args)
 
     end
 
-    helpers.newtimer(mail, timeout, update, true)
+    imap.timer = helpers.newtimer(mail, timeout, update, true, true)
 
-    return setmetatable(imap, { __index = imap.widget })
+    return imap
 end
 
 return setmetatable({}, { __call = function(_, ...) return worker(...) end })
diff --git a/widgets/maildir.lua b/widgets/maildir.lua
deleted file mode 100644 (file)
index e963fc6..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-
---[[
-                                                  
-     Licensed under GNU General Public License v2 
-      * (c) 2013,      Luke Bonham                
-      * (c) 2010-2012, Peter Hofmann              
-                                                  
---]]
-
-local newtimer        = require("lain.helpers").newtimer
-local read_pipe       = require("lain.helpers").read_pipe
-local spairs          = require("lain.helpers").spairs
-
-local wibox           = require("wibox")
-
-local awful           = require("awful")
-local util            = require("lain.util")
-
-local io              = { popen  = io.popen }
-local os              = { getenv = os.getenv }
-local pairs           = pairs
-local string          = { len    = string.len,
-                          match  = string.match }
-
-local setmetatable    = setmetatable
-
--- Maildir check
--- lain.widgets.maildir
-local maildir = {}
-
-local function worker(args)
-    local args         = args or {}
-    local timeout      = args.timeout or 60
-    local mailpath     = args.mailpath or os.getenv("HOME") .. "/Mail"
-    local ignore_boxes = args.ignore_boxes or {}
-    local settings     = args.settings or function() end
-    local ext_mail_cmd = args.external_mail_cmd
-
-    maildir.widget = wibox.widget.textbox('')
-
-    function update()
-        if ext_mail_cmd then
-            awful.util.spawn(ext_mail_cmd)
-        end
-
-        -- Find pathes to mailboxes.
-        local p = io.popen("find " .. mailpath ..
-                           " -mindepth 1 -maxdepth 2 -type d" ..
-                           " -not -name .git")
-        local boxes = {}
-        repeat
-            line = p:read("*l")
-            if line ~= nil
-            then
-                -- Find all files in the "new" subdirectory. For each
-                -- file, print a single character (no newline). Don't
-                -- match files that begin with a dot.
-                -- Afterwards the length of this string is the number of
-                -- new mails in that box.
-                local mailstring = read_pipe("find " .. line ..
-                                    "/new -mindepth 1 -type f " ..
-                                    "-not -name '.*' -printf a")
-
-                -- Strip off leading mailpath.
-                local box = string.match(line, mailpath .. "/(.*)")
-                local nummails = string.len(mailstring)
-                if nummails > 0
-                then
-                    boxes[box] = nummails
-                end
-            end
-        until line == nil
-
-        p:close()
-
-        newmail = "no mail"
-
-        -- Count the total number of mails irrespective of where it was found
-        total = 0
-
-        for box, number in spairs(boxes)
-        do
-            -- Add this box only if it's not to be ignored.
-            if not util.element_in_table(box, ignore_boxes)
-            then
-                total = total + number
-                if newmail == "no mail"
-                then
-                    newmail = box .. "(" .. number .. ")"
-                else
-                    newmail = newmail .. ", " ..
-                              box .. "(" .. number .. ")"
-                end
-            end
-        end
-
-        widget = maildir.widget
-        settings()
-    end
-
-    newtimer(mailpath, timeout, update, true)
-    return maildir.widget
-end
-
-return setmetatable(maildir, { __call = function(_, ...) return worker(...) end })
index aede803c0c9a50fe8aef6f051efcc72e19a767ad..7b717ef7aca05e7e016b9fd5c3170017f10db9e2 100644 (file)
@@ -7,14 +7,11 @@
                                                   
 --]]
 
-local newtimer     = require("lain.helpers").newtimer
-
+local helpers      = require("lain.helpers")
 local wibox        = require("wibox")
-
-local io           = { lines  = io.lines }
-local math         = { floor  = math.floor }
-local string       = { gmatch = string.gmatch }
-
+local gmatch       = string.gmatch
+local lines        = io.lines
+local floor        = math.floor
 local setmetatable = setmetatable
 
 -- Memory usage (ignoring caches)
@@ -26,25 +23,24 @@ local function worker(args)
     local timeout  = args.timeout or 2
     local settings = args.settings or function() end
 
-    mem.widget = wibox.widget.textbox('')
+    mem.widget = wibox.widget.textbox()
 
-    function update()
+    function mem.update()
         mem_now = {}
-        for line in io.lines("/proc/meminfo")
-        do
-            for k, v in string.gmatch(line, "([%a]+):[%s]+([%d]+).+")
-            do
-                if     k == "MemTotal"  then mem_now.total = math.floor(v / 1024)
-                elseif k == "MemFree"   then mem_now.free  = math.floor(v / 1024)
-                elseif k == "Buffers"   then mem_now.buf   = math.floor(v / 1024)
-                elseif k == "Cached"    then mem_now.cache = math.floor(v / 1024)
-                elseif k == "SwapTotal" then mem_now.swap  = math.floor(v / 1024)
-                elseif k == "SwapFree"  then mem_now.swapf = math.floor(v / 1024)
+        for line in lines("/proc/meminfo") do
+            for k, v in gmatch(line, "([%a]+):[%s]+([%d]+).+") do
+                if     k == "MemTotal"     then mem_now.total = floor(v / 1024 + 0.5)
+                elseif k == "MemFree"      then mem_now.free  = floor(v / 1024 + 0.5)
+                elseif k == "Buffers"      then mem_now.buf   = floor(v / 1024 + 0.5)
+                elseif k == "Cached"       then mem_now.cache = floor(v / 1024 + 0.5)
+                elseif k == "SwapTotal"    then mem_now.swap  = floor(v / 1024 + 0.5)
+                elseif k == "SwapFree"     then mem_now.swapf = floor(v / 1024 + 0.5)
+                elseif k == "SReclaimable" then mem_now.srec  = floor(v / 1024 + 0.5)
                 end
             end
         end
 
-        mem_now.used = mem_now.total - (mem_now.free + mem_now.buf + mem_now.cache)
+        mem_now.used = mem_now.total - mem_now.free - mem_now.buf - mem_now.cache - mem_now.srec
         mem_now.swapused = mem_now.swap - mem_now.swapf
         mem_now.perc = math.floor(mem_now.used / mem_now.total * 100)
 
@@ -52,9 +48,9 @@ local function worker(args)
         settings()
     end
 
-    newtimer("mem", timeout, update)
+    helpers.newtimer("mem", timeout, mem.update)
 
-    return mem.widget
+    return mem
 end
 
 return setmetatable(mem, { __call = function(_, ...) return worker(...) end })
index 8568764aeeaa69089f15cf1c5656783e1c08c493..ddedf243a9f3176f47d0620668528c9e3c01e17d 100644 (file)
@@ -8,20 +8,15 @@
 --]]
 
 local helpers      = require("lain.helpers")
-local async        = require("lain.asyncshell")
-
+local shell        = require("awful.util").shell
 local escape_f     = require("awful.util").escape
+local focused      = require("awful.screen").focused
 local naughty      = require("naughty")
 local wibox        = require("wibox")
-
-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 os           = { getenv = os.getenv }
+local string       = { format = string.format,
+                       gmatch = string.gmatch,
+                       match  = string.match }
 local setmetatable = setmetatable
 
 -- MPD infos
@@ -29,34 +24,31 @@ local setmetatable = setmetatable
 local mpd = {}
 
 local function worker(args)
-    local args        = args or {}
-    local timeout     = args.timeout or 2
-    local password    = args.password or ""
-    local host        = args.host or "127.0.0.1"
-    local port        = args.port or "6600"
-    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
-
-    local mpdcover = helpers.scripts_dir .. "mpdcover"
-    local mpdh = "telnet://" .. host .. ":" .. port
-    local echo = echo_cmd .. " 'password " .. password .. "\nstatus\ncurrentsong\nclose'"
-
-    mpd.widget = wibox.widget.textbox('')
-
-    mpd_notification_preset = {
-        title   = "Now playing",
-        timeout = 6
-    }
+    local args          = args or {}
+    local timeout       = args.timeout or 2
+    local password      = (args.password and #args.password > 0 and string.format("password %s\\n", args.password)) or ""
+    local host          = args.host or "127.0.0.1"
+    local port          = args.port or "6600"
+    local music_dir     = args.music_dir or os.getenv("HOME") .. "/Music"
+    local cover_pattern = args.cover_pattern or "*\\.(jpg|jpeg|png|gif)$"
+    local cover_size    = args.cover_size or 100
+    local default_art   = args.default_art
+    local notify        = args.notify or "on"
+    local followtag     = args.followtag or false
+    local settings      = args.settings or function() end
+
+    local mpdh = string.format("telnet://%s:%s", host, port)
+    local echo = string.format("printf \"%sstatus\\ncurrentsong\\nclose\\n\"", password)
+    local cmd  = string.format("%s | curl --connect-timeout 1 -fsm 3 %s", echo, mpdh)
+
+    mpd.widget = wibox.widget.textbox()
+
+    mpd_notification_preset = { title = "Now playing", timeout = 6 }
 
     helpers.set_map("current mpd track", nil)
 
     function mpd.update()
-        async.request(echo .. " | curl --connect-timeout 1 -fsm 3 " .. mpdh, function (f)
+        helpers.async({ shell, "-c", cmd }, function(f)
             mpd_now = {
                 random_mode  = false,
                 single_mode  = false,
@@ -70,6 +62,8 @@ local function worker(args)
                 artist       = "N/A",
                 title        = "N/A",
                 album        = "N/A",
+                genre        = "N/A",
+                track        = "N/A",
                 date         = "N/A",
                 time         = "N/A",
                 elapsed      = "N/A"
@@ -83,6 +77,8 @@ local function worker(args)
                     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 == "Genre"          then mpd_now.genre        = escape_f(v)
+                    elseif k == "Track"          then mpd_now.track        = 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+")
@@ -101,41 +97,41 @@ local function worker(args)
             widget = mpd.widget
             settings()
 
-            if mpd_now.state == "play"
-            then
-                if notify == "on" and mpd_now.title ~= helpers.get_map("current mpd track")
-                then
+            if mpd_now.state == "play" then
+                if notify == "on" and mpd_now.title ~= helpers.get_map("current mpd track") then
                     helpers.set_map("current mpd track", mpd_now.title)
 
-                    if string.match(mpd_now.file, "http.*://") == nil
-                    then -- local file
-                        os.execute(string.format("%s %q %q %d %q", mpdcover, music_dir,
-                                   mpd_now.file, cover_size, default_art))
-                        current_icon = "/tmp/mpdcover.png"
-                    else -- http stream
-                        current_icon = default_art
-                    end
-
-                    if followmouse then
-                        mpd_notification_preset.screen = mouse.screen
+                    if followtag then mpd_notification_preset.screen = focused() end
+
+                    local common =  {
+                        preset      = mpd_notification_preset,
+                        icon        = default_art,
+                        icon_size   = cover_size,
+                        replaces_id = mpd.id
+                    }
+
+                    if not string.match(mpd_now.file, "http.*://") then -- local file instead of http stream
+                        local path   = string.format("%s/%s", music_dir, string.match(mpd_now.file, ".*/"))
+                        local cover  = string.format("find '%s' -maxdepth 1 -type f | egrep -i -m1 '%s'", path:gsub("'", "'\\''"), cover_pattern)
+                        helpers.async({ shell, "-c", cover }, function(current_icon)
+                            common.icon = current_icon:gsub("\n", "")
+                            if #common.icon == 0 then common.icon = nil end
+                            mpd.id = naughty.notify(common).id
+                        end)
+                    else
+                        mpd.id = naughty.notify(common).id
                     end
 
-                    mpd.id = naughty.notify({
-                        preset = mpd_notification_preset,
-                        icon = current_icon,
-                        replaces_id = mpd.id,
-                    }).id
                 end
-            elseif mpd_now.state ~= "pause"
-            then
+            elseif mpd_now.state ~= "pause" then
                 helpers.set_map("current mpd track", nil)
             end
         end)
     end
 
-    helpers.newtimer("mpd", timeout, mpd.update)
+    mpd.timer = helpers.newtimer("mpd", timeout, mpd.update, true, true)
 
-    return setmetatable(mpd, { __index = mpd.widget })
+    return mpd
 end
 
 return setmetatable(mpd, { __call = function(_, ...) return worker(...) end })
index 18831685d1a11c73aab9f6ab757e7c54e2e7fd35..223f3aff15f409e8518727c54a7d4475ce5d9a65 100644 (file)
 local helpers      = require("lain.helpers")
 local naughty      = require("naughty")
 local wibox        = require("wibox")
-
 local string       = { format = string.format,
-                       gsub   = string.gsub,
                        match  = string.match }
-
 local setmetatable = setmetatable
 
 -- Network infos
 -- lain.widgets.net
 
 local function worker(args)
-    local net = { last_t = 0, last_r = 0, devices = {} }
-
-    function net.get_first_device()
-        local ws = helpers.read_pipe("ip link show | cut -d' ' -f2,9")
-        ws = ws:match("%w+: UP") or ws:match("ppp%w+: UNKNOWN")
-        if ws then return { ws:match("(%w+):") }
-        else return {} end
-    end
-
-    local args     = args or {}
-    local timeout  = args.timeout or 2
-    local units    = args.units or 1024 --kb
-    local notify   = args.notify or "on"
-    local screen   = args.screen or 1
-    local settings = args.settings or function() end
-    local iface    = args.iface or net.get_first_device()
-
-    net.widget = wibox.widget.textbox('')
+    local net = { widget = wibox.widget.textbox() }
+    net.last_t = 0
+    net.last_r = 0
+    net.devices = {}
+
+    local args       = args or {}
+    local timeout    = args.timeout or 2
+    local units      = args.units or 1024 --kb
+    local notify     = args.notify or "on"
+    local screen     = args.screen or 1
+    local settings   = args.settings or function() end
 
     -- Compatibility with old API where iface was a string corresponding to 1 interface
-    if type(iface) == "string" then
-        iftable = {iface}
-    else
-        iftable = iface
+    net.iface = (args.iface and (type(args.iface) == "string" and {args.iface}) or
+                (type(args.iface) == "table" and args.iface)) or {}
+
+    function net.get_device()
+        helpers.async(string.format("ip link show", device_cmd), function(ws)
+            ws = ws:match("(%w+): <BROADCAST,MULTICAST,.-UP,LOWER_UP>")
+            net.iface = ws and { ws } or {}
+        end)
     end
 
-    -- Mark all devices as initially online/active
-    for i, dev in ipairs(iftable) do
-        helpers.set_map(dev, true)
-    end
+    if #net.iface == 0 then net.get_device() end
 
     function update()
-        -- This check is required to ensure we keep looking for one device if
-        -- none is found by net.get_first_device() at startup (i.e. iftable = {})
-        if next(iftable) == nil then
-            iftable = net.get_first_device()
-        end
-
         -- These are the totals over all specified interfaces
         net_now = {
             -- New api - Current state of requested devices
@@ -72,15 +57,14 @@ local function worker(args)
         local total_t = 0
         local total_r = 0
 
-        for i, dev in ipairs(iftable) do
-            local dev_now = {}
+        for i, dev in ipairs(net.iface) do
+            local dev_now    = {}
             local dev_before = net.devices[dev] or { last_t = 0, last_r = 0 }
+            local now_t      = tonumber(helpers.first_line(string.format("/sys/class/net/%s/statistics/tx_bytes", dev)) or 0)
+            local now_r      = tonumber(helpers.first_line(string.format("/sys/class/net/%s/statistics/rx_bytes", dev)) or 0)
 
-            dev_now.carrier  = helpers.first_line(string.format('/sys/class/net/%s/carrier', dev)) or '0'
-            dev_now.state    = helpers.first_line(string.format('/sys/class/net/%s/operstate', dev)) or 'down'
-
-            local now_t      = tonumber(helpers.first_line(string.format('/sys/class/net/%s/statistics/tx_bytes', dev)) or 0)
-            local now_r      = tonumber(helpers.first_line(string.format('/sys/class/net/%s/statistics/rx_bytes', dev)) or 0)
+            dev_now.carrier  = helpers.first_line(string.format("/sys/class/net/%s/carrier", dev)) or "0"
+            dev_now.state    = helpers.first_line(string.format("/sys/class/net/%s/operstate", dev)) or "down"
 
             dev_now.sent     = (now_t - dev_before.last_t) / timeout / units
             dev_now.received = (now_r - dev_before.last_r) / timeout / units
@@ -88,8 +72,8 @@ local function worker(args)
             net_now.sent     = net_now.sent     + dev_now.sent
             net_now.received = net_now.received + dev_now.received
 
-            dev_now.sent     = string.gsub(string.format('%.1f', dev_now.sent), ',', '.')
-            dev_now.received = string.gsub(string.format('%.1f', dev_now.received), ',', '.')
+            dev_now.sent     = string.format('%.1f', dev_now.sent)
+            dev_now.received = string.format('%.1f', dev_now.received)
 
             dev_now.last_t   = now_t
             dev_now.last_r   = now_r
@@ -113,32 +97,27 @@ local function worker(args)
                 helpers.set_map(dev, true)
             end
 
-            -- Old api compatibility
             net_now.carrier      = dev_now.carrier
             net_now.state        = dev_now.state
-            -- And new api
             net_now.devices[dev] = dev_now
-            -- With the new api new_now.sent and net_now.received will be the
+            -- new_now.sent and net_now.received will be the
             -- totals across all specified devices
-
         end
 
         if total_t ~= net.last_t or total_r ~= net.last_r then
-            -- Convert to a string to round the digits after the float point
-            net_now.sent     = string.gsub(string.format('%.1f', net_now.sent), ',', '.')
-            net_now.received = string.gsub(string.format('%.1f', net_now.received), ',', '.')
-
-            net.last_t = total_t
-            net.last_r = total_r
+            net_now.sent     = string.format('%.1f', net_now.sent)
+            net_now.received = string.format('%.1f', net_now.received)
+            net.last_t       = total_t
+            net.last_r       = total_r
         end
 
         widget = net.widget
         settings()
     end
 
-    helpers.newtimer(iface, timeout, update)
+    helpers.newtimer("network", timeout, update)
 
-    return setmetatable(net, { __index = net.widget })
+    return net
 end
 
 return setmetatable({}, { __call = function(_, ...) return worker(...) end })
index 15bea8b3bddfc08b3baa6142b5de775a1afa97d1..590bd0f883890d130f4adffd3a2e6e3924903f33 100644 (file)
@@ -6,55 +6,57 @@
                                                   
 --]]
 
-local read_pipe    = require("lain.helpers").read_pipe
-local newtimer     = require("lain.helpers").newtimer
+local helpers      = require("lain.helpers")
+local shell        = require("awful.util").shell
 local wibox        = require("wibox")
-
 local string       = { gmatch = string.gmatch,
                        match  = string.match,
                        format = string.format }
-
 local setmetatable = setmetatable
 
 -- PulseAudio volume
 -- lain.widgets.pulseaudio
-local pulseaudio = {}
 
 local function worker(args)
-   local args        = args or {}
-   local timeout     = args.timeout or 5
-   local settings    = args.settings or function() end
-   local scallback   = args.scallback
+    local pulseaudio = { wibox.widget.textbox() }
+    local args        = args or {}
+    local devicetype  = args.devicetype or "sink"
+    local timeout     = args.timeout or 5
+    local settings    = args.settings or function() end
+    local scallback   = args.scallback
+
+    pulseaudio.cmd    = args.cmd or "pacmd list-" .. devicetype .. "s | sed -n -e '0,/*/d' -e '/base volume/d' -e '/volume:/p' -e '/muted:/p' -e '/device\\.string/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()
+        if scallback then pulseaudio.cmd = scallback() end
 
-   function pulseaudio.update()
-      if scallback then pulseaudio.cmd = scallback() end
-      local s = read_pipe(pulseaudio.cmd)
+        helpers.async({ shell, "-c", pulseaudio.cmd }, function(s)
+            volume_now = {
+                index = string.match(s, "index: (%S+)") or "N/A",
+                device = string.match(s, "device.string = \"(%S+)\"") or "N/A",
+                sink   = device, -- legacy API
+                muted  = string.match(s, "muted: (%S+)") or "N/A"
+            }
 
-      volume_now = {}
-      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
 
-      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"
 
-      volume_now.left  = volume_now.channel[1] or "N/A"
-      volume_now.right = volume_now.channel[2] or "N/A"
+            widget = pulseaudio.widget
 
-      widget = pulseaudio.widget
-      settings()
-   end
+            settings()
+        end)
+    end
 
-   newtimer(string.format("pulseaudio-%s", timeout), timeout, pulseaudio.update)
+    helpers.newtimer("pulseaudio", timeout, pulseaudio.update)
 
-   return setmetatable(pulseaudio, { __index = pulseaudio.widget })
+    return pulseaudio
 end
 
-return setmetatable(pulseaudio, { __call = function(_, ...) return worker(...) end })
+return setmetatable({}, { __call = function(_, ...) return worker(...) end })
index 7f226420d3b43c18eff4f9a3edb2b106f47d0f32..c75ad4a4901a9e99c447e6836938d6662a07f357 100644 (file)
@@ -7,90 +7,32 @@
                                                   
 --]]
 
-local newtimer     = require("lain.helpers").newtimer
-local read_pipe    = require("lain.helpers").read_pipe
-
+local helpers      = require("lain.helpers")
 local awful        = require("awful")
-local beautiful    = require("beautiful")
 local naughty      = require("naughty")
-
+local wibox        = require("wibox")
 local math         = { modf   = math.modf }
-local mouse        = mouse
 local string       = { format = string.format,
+                       gmatch = string.gmatch,
                        match  = string.match,
                        rep    = string.rep }
+local type         = type
 local tonumber     = tonumber
-
 local setmetatable = setmetatable
 
--- ALSA volume bar
+-- Pulseaudio volume bar
 -- lain.widgets.pulsebar
 local pulsebar = {
-    sink = 0,
-    step = "1%",
-
     colors = {
-        background = beautiful.bg_normal,
+        background = "#000000",
         mute       = "#EB8F8F",
         unmute     = "#A4CE8A"
     },
 
-    mixer = "pavucontrol",
-
-    notifications = {
-        font      = beautiful.font:sub(beautiful.font:find(""), beautiful.font:find(" ")),
-        font_size = "11",
-        color     = beautiful.fg_normal,
-        bar_size  = 18,
-        screen    = 1
-    },
-
     _current_level = 0,
     _muted         = false
 }
 
-function pulsebar.notify()
-    pulsebar.update()
-
-    local preset = {
-        title   = "",
-        text    = "",
-        timeout = 5,
-        screen  = pulsebar.notifications.screen,
-        font    = pulsebar.notifications.font .. " " ..
-                  pulsebar.notifications.font_size,
-        fg      = pulsebar.notifications.color
-    }
-
-    if pulsebar._muted
-    then
-        preset.title = "Sink " .. pulsebar.sink .. " - Muted"
-    else
-        preset.title = "Sink " .. pulsebar.sink .. " - " .. pulsebar._current_level .. "%"
-    end
-
-    int = math.modf((pulsebar._current_level / 100) * pulsebar.notifications.bar_size)
-    preset.text = "["
-                .. string.rep("|", int)
-                .. string.rep(" ", pulsebar.notifications.bar_size - int)
-                .. "]"
-
-    if pulsebar.followmouse then
-        preset.screen = mouse.screen
-    end
-
-    if pulsebar._notify ~= nil then
-        pulsebar._notify = naughty.notify ({
-            replaces_id = pulsebar._notify.id,
-            preset      = preset,
-        })
-    else
-        pulsebar._notify = naughty.notify ({
-            preset = preset,
-        })
-    end
-end
-
 local function worker(args)
     local args       = args or {}
     local timeout    = args.timeout or 5
@@ -102,79 +44,103 @@ local function worker(args)
     local vertical   = args.vertical or false
     local scallback  = args.scallback
 
-    pulsebar.cmd           = args.cmd or string.format("pacmd list-sinks | sed -n -e '0,/*/d' -e '/base volume/d' -e '/volume:/p' -e '/muted:/p'")
-    pulsebar.colors        = args.colors or pulsebar.colors
-    pulsebar.notifications = args.notifications or pulsebar.notifications
+    pulsebar.cmd           = args.cmd or "pacmd list-sinks | sed -n -e '0,/*/d' -e '/base volume/d' -e '/volume:/p' -e '/muted:/p' -e '/device\\.string/p'"
     pulsebar.sink          = args.sink or 0
-    pulsebar.step          = args.step or pulsebar.step
-    pulsebar.followmouse   = args.followmouse or false
+    pulsebar.colors        = args.colors or pulsebar.colors
+    pulsebar.followtag     = args.followtag or false
+    pulsebar.notifications = args.notification_preset
 
-    pulsebar.bar = awful.widget.progressbar()
+    if not pulsebar.notification_preset then
+        pulsebar.notification_preset      = {}
+        pulsebar.notification_preset.font = "Monospace 10"
+    end
+
+    pulsebar.bar = wibox.widget {
+        forced_height    = height,
+        forced_width     = width,
+        color            = pulsebar.colors.unmute,
+        background_color = pulsebar.colors.background,
+        margins          = 1,
+        paddings         = 1,
+        ticks            = ticks,
+        ticks_size       = ticks_size,
+        widget           = wibox.widget.progressbar,
+        layout           = vertical and wibox.container.rotate
+    }
 
-    pulsebar.bar:set_background_color(pulsebar.colors.background)
-    pulsebar.bar:set_color(pulsebar.colors.unmute)
     pulsebar.tooltip = awful.tooltip({ objects = { pulsebar.bar } })
-    pulsebar.bar:set_width(width)
-    pulsebar.bar:set_height(height)
-    pulsebar.bar:set_ticks(ticks)
-    pulsebar.bar:set_ticks_size(ticks_size)
-    pulsebar.bar:set_vertical(vertical)
 
-    function pulsebar.update()
+    function pulsebar.update(callback)
         if scallback then pulseaudio.cmd = scallback() end
-        local s = read_pipe(pulsebar.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+)")
-
-        local volu = volume_now.left
-        local mute = volume_now.muted
-
-        if (volu and volu ~= pulsebar._current_level) or (mute and mute ~= pulsebar._muted)
-        then
-            pulsebar._current_level = volu
-            pulsebar.bar:set_value(pulsebar._current_level / 100)
-            if not mute and volu == 0 or mute == "yes"
-            then
-                pulsebar._muted = true
-                pulsebar.tooltip:set_text (" [Muted] ")
-                pulsebar.bar:set_color(pulsebar.colors.mute)
+
+        helpers.async({ awful.util.shell, "-c", pulsebar.cmd }, function(s)
+            volume_now = {
+                index = string.match(s, "index: (%S+)") or "N/A",
+                sink  = string.match(s, "device.string = \"(%S+)\"") or "N/A",
+                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"
+
+            local volu = volume_now.left
+            local mute = volume_now.muted
+
+            if (volu and volu ~= pulsebar._current_level) or (mute and mute ~= pulsebar._muted) then
+                pulsebar._current_level = volu
+                pulsebar.bar:set_value(pulsebar._current_level / 100)
+                if (not mute and volu == 0) or mute == "yes" then
+                    pulsebar._muted = true
+                    pulsebar.tooltip:set_text ("[Muted]")
+                    pulsebar.bar.color = pulsebar.colors.mute
+                else
+                    pulsebar._muted = false
+                    pulsebar.tooltip:set_text(string.format("%s: %s", pulsebar.sink, volu))
+                    pulsebar.bar.color = pulsebar.colors.unmute
+                end
+
+                settings()
+
+                if type(callback) == "function" then callback() end
+            end
+        end)
+    end
+
+    function pulsebar.notify()
+        pulsebar.update(function()
+            local preset = pulsebar.notification_preset
+
+            if pulsebar._muted then
+                preset.title = string.format("Sink %s - Muted", pulsebar.sink)
+            else
+                preset.title = string.format("%s - %s%%", pulsebar.sink, pulsebar._current_level)
+            end
+
+            int = math.modf((pulsebar._current_level / 100) * awful.screen.focused().mywibox.height)
+            preset.text = string.format("[%s%s]", string.rep("|", int),
+                          string.rep(" ", awful.screen.focused().mywibox.height - int))
+
+            if pulsebar.followtag then preset.screen = awful.screen.focused() end
+
+            if not pulsebar.notification then
+                pulsebar.notification = naughty.notify {
+                    preset  = preset,
+                    destroy = function() pulsebar.notification = nil end
+                }
             else
-                pulsebar._muted = false
-                pulsebar.tooltip:set_text(string.format(" %s:%s ", pulsebar.sink, volu))
-                pulsebar.bar:set_color(pulsebar.colors.unmute)
+                naughty.replace_text(pulsebar.notification, preset.title, preset.text)
             end
-            settings()
-        end
+        end)
     end
 
-    pulsebar.bar:buttons(awful.util.table.join (
-          awful.button({}, 1, function()
-            awful.util.spawn(pulsebar.mixer)
-          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)
-
-    newtimer(timer_id, timeout, pulsebar.update)
+    helpers.newtimer(string.format("pulsebar-%s", pulsebar.sink), timeout, pulsebar.update)
 
     return pulsebar
 end
index d8e4713a77371088c2ac14693b1183b97ef4517a..df9dbea06bfeb265febeab5ba015cce3c15833c9 100644 (file)
@@ -7,13 +7,10 @@
                                                   
 --]]
 
-local newtimer     = require("lain.helpers").newtimer
-
+local helpers      = require("lain.helpers")
 local wibox        = require("wibox")
-
 local io           = { open = io.open }
 local string       = { match  = string.match }
-
 local setmetatable = setmetatable
 
 -- System load
@@ -21,13 +18,13 @@ local setmetatable = setmetatable
 local sysload = {}
 
 local function worker(args)
-    local args = args or {}
-    local timeout = args.timeout or 2
+    local args     = args or {}
+    local timeout  = args.timeout or 2
     local settings = args.settings or function() end
 
-    sysload.widget = wibox.widget.textbox('')
+    sysload.widget = wibox.widget.textbox()
 
-    function update()
+    function sysload.update()
         local f = io.open("/proc/loadavg")
         local ret = f:read("*all")
         f:close()
@@ -38,8 +35,9 @@ local function worker(args)
         settings()
     end
 
-    newtimer("sysload", timeout, update)
-    return sysload.widget
+    helpers.newtimer("sysload", timeout, sysload.update)
+
+    return sysload
 end
 
 return setmetatable(sysload, { __call = function(_, ...) return worker(...) end })
index a4ada5276de7d667d8ad8fb5c155935651f099c3..1c7e86c92de8e3074eece379457d427ebb1093e0 100644 (file)
@@ -6,13 +6,10 @@
                                                   
 --]]
 
-local newtimer     = require("lain.helpers").newtimer
-
+local helpers      = require("lain.helpers")
 local wibox        = require("wibox")
-
 local io           = { open = io.open }
 local tonumber     = tonumber
-
 local setmetatable = setmetatable
 
 -- coretemp
@@ -25,9 +22,9 @@ local function worker(args)
     local tempfile = args.tempfile or "/sys/class/thermal/thermal_zone0/temp"
     local settings = args.settings or function() end
 
-    temp.widget = wibox.widget.textbox('')
+    temp.widget = wibox.widget.textbox()
 
-    function update()
+    function temp.update()
         local f = io.open(tempfile)
         if f then
             coretemp_now = tonumber(f:read("*all")) / 1000
@@ -40,9 +37,9 @@ local function worker(args)
         settings()
     end
 
-    newtimer("coretemp", timeout, update)
+    helpers.newtimer("coretemp", timeout, temp.update)
 
-    return temp.widget
+    return temp
 end
 
 return setmetatable(temp, { __call = function(_, ...) return worker(...) end })
index 545e6d1a3c6ca6e7a6867150689f45c4e62100f9..1e398cc065f8acaaeb51545a429bd2a9c301056e 100644 (file)
@@ -6,24 +6,17 @@
                                                   
 --]]
 
-local newtimer     = require("lain.helpers").newtimer
-local read_pipe    = require("lain.helpers").read_pipe
-
-local async        = require("lain.asyncshell")
+local helpers      = require("lain.helpers")
 local json         = require("lain.util").dkjson
-local lain_icons   = require("lain.helpers").icons_dir
-
+local focused      = require("awful.screen").focused
 local naughty      = require("naughty")
 local wibox        = require("wibox")
-
 local math         = { floor    = math.floor }
 local os           = { time     = os.time,
                        date     = os.date,
                        difftime = os.difftime }
 local string       = { format   = string.format,
                        gsub     = string.gsub }
-
-local mouse        = mouse
 local tonumber     = tonumber
 local setmetatable = setmetatable
 
@@ -32,7 +25,7 @@ local setmetatable = setmetatable
 -- lain.widgets.weather
 
 local function worker(args)
-    local weather               = {}
+    local weather               = { widget = wibox.widget.textbox() }
     local args                  = args or {}
     local APPID                 = args.APPID or "3e321f9414eaedbfab34983bda77a66e" -- lain default
     local timeout               = args.timeout or 900   -- 15 min
@@ -49,32 +42,33 @@ local function worker(args)
     local lang                  = args.lang or "en"
     local cnt                   = args.cnt or 5
     local date_cmd              = args.date_cmd or "date -u -d @%d +'%%a %%d'"
-    local icons_path            = args.icons_path or lain_icons .. "openweathermap/"
+    local icons_path            = args.icons_path or helpers.icons_dir .. "openweathermap/"
     local notification_preset   = args.notification_preset or {}
     local notification_text_fun = args.notification_text_fun or
                                   function (wn)
-                                      local day = string.gsub(read_pipe(string.format(date_cmd, wn["dt"])), "\n", "")
+                                      local day = os.date("%a %d", wn["dt"])
                                       local tmin = math.floor(wn["temp"]["min"])
                                       local tmax = math.floor(wn["temp"]["max"])
                                       local desc = wn["weather"][1]["description"]
                                       return string.format("<b>%s</b>: %s, %d - %d ", day, desc, tmin, tmax)
                                   end
     local weather_na_markup     = args.weather_na_markup or " N/A "
-    local followmouse           = args.followmouse or false
+    local followtag             = args.followtag or false
     local settings              = args.settings or function() end
 
-    weather.widget    = wibox.widget.textbox(weather_na_markup)
+    weather.widget:set_markup(weather_na_markup)
     weather.icon_path = icons_path .. "na.png"
-    weather.icon      = wibox.widget.imagebox(weather.icon_path)
+    weather.icon = wibox.widget.imagebox(weather.icon_path)
 
     function weather.show(t_out)
         weather.hide()
 
-        if followmouse then
-            notification_preset.screen = mouse.screen
+        if followtag then
+            notification_preset.screen = focused()
         end
 
         if not weather.notification_text then
+            weather.update()
             weather.forecast_update()
         end
 
@@ -104,7 +98,7 @@ local function worker(args)
 
     function weather.forecast_update()
         local cmd = string.format(forecast_call, city_id, units, lang, cnt, APPID)
-        async.request(cmd, function(f)
+        helpers.async(cmd, function(f)
             local pos, err
             weather_now, pos, err = json.decode(f, 1, nil)
 
@@ -124,7 +118,7 @@ local function worker(args)
 
     function weather.update()
         local cmd = string.format(current_call, city_id, units, lang, APPID)
-        async.request(cmd, function(f)
+        helpers.async(cmd, function(f)
             local pos, err, icon
             weather_now, pos, err = json.decode(f, 1, nil)
 
@@ -134,12 +128,9 @@ local function worker(args)
                 local sunrise = tonumber(weather_now["sys"]["sunrise"])
                 local sunset  = tonumber(weather_now["sys"]["sunset"])
                 local icon    = weather_now["weather"][1]["icon"]
-                local utc_m   = string.gsub(read_pipe(string.format("date -u -d 'today 00:00:00' +'%%s'")), "\n", "")
-                local loc_m   = string.gsub(read_pipe(string.format("date -d 'today 00:00:00' +'%%s'")), "\n", "")
-
-                loc_m  = tonumber(loc_m)
-                utc_m  = tonumber(utc_m)
-                offset = utc_offset()
+                local loc_m   = os.time { year = os.date("%Y"), month = os.date("%m"), day = os.date("%d"), hour = 0 }
+                local offset  = utc_offset()
+                local utc_m   = loc_m + offset
 
                 -- if we are 1 day after the GMT, return 1 day back, and viceversa
                 if offset > 0 and loc_m >= utc_m then
@@ -168,10 +159,10 @@ local function worker(args)
 
     weather.attach(weather.widget)
 
-    newtimer("weather-" .. city_id, timeout, weather.update)
-    newtimer("weather_forecast-" .. city_id, timeout, weather.forecast_update)
+    weather.timer = helpers.newtimer("weather-" .. city_id, timeout, weather.update, false, true)
+    weather.timer_forecast = helpers.newtimer("weather_forecast-" .. city_id, timeout, weather.forecast_update, false, true)
 
-    return setmetatable(weather, { __index = weather.widget })
+    return weather
 end
 
 return setmetatable({}, { __call = function(_, ...) return worker(...) end })
diff --git a/wiki b/wiki
index d0df450d05655c5d8f724c42dc6b5d18b3676a60..af671ad9bb1ce9c7bb74a75f489a3b5d0a934558 160000 (submodule)
--- a/wiki
+++ b/wiki
@@ -1 +1 @@
-Subproject commit d0df450d05655c5d8f724c42dc6b5d18b3676a60
+Subproject commit af671ad9bb1ce9c7bb74a75f489a3b5d0a934558