Added settings.lua
This commit is contained in:
		@@ -5,28 +5,27 @@
 | 
			
		||||
 | 
			
		||||
--]]
 | 
			
		||||
 | 
			
		||||
local spawn  = require("awful.spawn")
 | 
			
		||||
local timer  = require("gears.timer")
 | 
			
		||||
local debug  = require("debug")
 | 
			
		||||
local io     = { lines = io.lines,
 | 
			
		||||
                 open  = io.open }
 | 
			
		||||
local pairs  = pairs
 | 
			
		||||
local spawn = require('awful.spawn')
 | 
			
		||||
local timer = require('gears.timer')
 | 
			
		||||
local debug = require('debug')
 | 
			
		||||
local io = { lines = io.lines, open = io.open }
 | 
			
		||||
local pairs = pairs
 | 
			
		||||
local rawget = rawget
 | 
			
		||||
local tsort  = table.sort
 | 
			
		||||
local tsort = table.sort
 | 
			
		||||
local unpack = unpack or table.unpack -- lua 5.1 retro-compatibility
 | 
			
		||||
 | 
			
		||||
-- Lain helper functions for internal use
 | 
			
		||||
-- lain.helpers
 | 
			
		||||
local helpers = {}
 | 
			
		||||
 | 
			
		||||
helpers.lain_dir    = debug.getinfo(1, 'S').source:match[[^@(.*/).*$]]
 | 
			
		||||
helpers.icons_dir   = helpers.lain_dir .. 'icons/'
 | 
			
		||||
helpers.lain_dir = debug.getinfo(1, 'S').source:match([[^@(.*/).*$]])
 | 
			
		||||
helpers.icons_dir = helpers.lain_dir .. 'icons/'
 | 
			
		||||
helpers.scripts_dir = helpers.lain_dir .. 'scripts/'
 | 
			
		||||
 | 
			
		||||
-- {{{ Modules loader
 | 
			
		||||
 | 
			
		||||
function helpers.wrequire(t, k)
 | 
			
		||||
    return rawget(t, k) or require(t._NAME .. '.' .. k)
 | 
			
		||||
	return rawget(t, k) or require(t._NAME .. '.' .. k)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- }}}
 | 
			
		||||
@@ -35,47 +34,51 @@ end
 | 
			
		||||
 | 
			
		||||
-- check if the file exists and is readable
 | 
			
		||||
function helpers.file_exists(path)
 | 
			
		||||
    local file = io.open(path, "rb")
 | 
			
		||||
    if file then file:close() end
 | 
			
		||||
    return file ~= nil
 | 
			
		||||
	local file = io.open(path, 'rb')
 | 
			
		||||
	if file then
 | 
			
		||||
		file:close()
 | 
			
		||||
	end
 | 
			
		||||
	return file ~= nil
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- get a table with all lines from a file
 | 
			
		||||
function helpers.lines_from(path)
 | 
			
		||||
    local lines = {}
 | 
			
		||||
    for line in io.lines(path) do
 | 
			
		||||
        lines[#lines + 1] = line
 | 
			
		||||
    end
 | 
			
		||||
    return lines
 | 
			
		||||
	local lines = {}
 | 
			
		||||
	for line in io.lines(path) do
 | 
			
		||||
		lines[#lines + 1] = line
 | 
			
		||||
	end
 | 
			
		||||
	return lines
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- get a table with all lines from a file matching regexp
 | 
			
		||||
function helpers.lines_match(regexp, path)
 | 
			
		||||
    local lines = {}
 | 
			
		||||
    for line in io.lines(path) do
 | 
			
		||||
        if string.match(line, regexp) then
 | 
			
		||||
            lines[#lines + 1] = line
 | 
			
		||||
        end
 | 
			
		||||
    end
 | 
			
		||||
    return lines
 | 
			
		||||
	local lines = {}
 | 
			
		||||
	for line in io.lines(path) do
 | 
			
		||||
		if string.match(line, regexp) then
 | 
			
		||||
			lines[#lines + 1] = line
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
	return lines
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- get first line of a file
 | 
			
		||||
function helpers.first_line(path)
 | 
			
		||||
    local file, first = io.open(path, "rb"), nil
 | 
			
		||||
    if file then
 | 
			
		||||
        first = file:read("*l")
 | 
			
		||||
        file:close()
 | 
			
		||||
    end
 | 
			
		||||
    return first
 | 
			
		||||
	local file, first = io.open(path, 'rb'), nil
 | 
			
		||||
	if file then
 | 
			
		||||
		first = file:read('*l')
 | 
			
		||||
		file:close()
 | 
			
		||||
	end
 | 
			
		||||
	return first
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- get first non empty line from a file
 | 
			
		||||
function helpers.first_nonempty_line(path)
 | 
			
		||||
    for line in io.lines(path) do
 | 
			
		||||
        if #line then return line end
 | 
			
		||||
    end
 | 
			
		||||
    return nil
 | 
			
		||||
	for line in io.lines(path) do
 | 
			
		||||
		if #line then
 | 
			
		||||
			return line
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
	return nil
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- }}}
 | 
			
		||||
@@ -85,17 +88,19 @@ end
 | 
			
		||||
helpers.timer_table = {}
 | 
			
		||||
 | 
			
		||||
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] = 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]
 | 
			
		||||
	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] = 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
 | 
			
		||||
 | 
			
		||||
-- }}}
 | 
			
		||||
@@ -107,27 +112,25 @@ end
 | 
			
		||||
-- @param callback function to execute on cmd output
 | 
			
		||||
-- @return cmd PID
 | 
			
		||||
function helpers.async(cmd, callback)
 | 
			
		||||
    return spawn.easy_async(cmd,
 | 
			
		||||
    function (stdout, _, _, exit_code)
 | 
			
		||||
        callback(stdout, exit_code)
 | 
			
		||||
    end)
 | 
			
		||||
	return spawn.easy_async(cmd, function(stdout, _, _, exit_code)
 | 
			
		||||
		callback(stdout, exit_code)
 | 
			
		||||
	end)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- like above, but call spawn.easy_async with a shell
 | 
			
		||||
function helpers.async_with_shell(cmd, callback)
 | 
			
		||||
    return spawn.easy_async_with_shell(cmd,
 | 
			
		||||
    function (stdout, _, _, exit_code)
 | 
			
		||||
        callback(stdout, exit_code)
 | 
			
		||||
    end)
 | 
			
		||||
	return spawn.easy_async_with_shell(cmd, function(stdout, _, _, exit_code)
 | 
			
		||||
		callback(stdout, exit_code)
 | 
			
		||||
	end)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- run a command and execute a function on its output line by line
 | 
			
		||||
function helpers.line_callback(cmd, callback)
 | 
			
		||||
    return spawn.with_line_callback(cmd, {
 | 
			
		||||
        stdout = function (line)
 | 
			
		||||
            callback(line)
 | 
			
		||||
        end,
 | 
			
		||||
    })
 | 
			
		||||
	return spawn.with_line_callback(cmd, {
 | 
			
		||||
		stdout = function(line)
 | 
			
		||||
			callback(line)
 | 
			
		||||
		end,
 | 
			
		||||
	})
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- }}}
 | 
			
		||||
@@ -137,11 +140,11 @@ end
 | 
			
		||||
helpers.map_table = {}
 | 
			
		||||
 | 
			
		||||
function helpers.set_map(element, value)
 | 
			
		||||
    helpers.map_table[element] = value
 | 
			
		||||
	helpers.map_table[element] = value
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function helpers.get_map(element)
 | 
			
		||||
    return helpers.map_table[element]
 | 
			
		||||
	return helpers.map_table[element]
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- }}}
 | 
			
		||||
@@ -150,52 +153,56 @@ end
 | 
			
		||||
 | 
			
		||||
-- 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
 | 
			
		||||
	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 = {}
 | 
			
		||||
    for k in pairs(t) do keys[#keys+1] = k end
 | 
			
		||||
	-- collect the keys
 | 
			
		||||
	local keys = {}
 | 
			
		||||
	for k in pairs(t) do
 | 
			
		||||
		keys[#keys + 1] = k
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    tsort(keys)
 | 
			
		||||
	tsort(keys)
 | 
			
		||||
 | 
			
		||||
    -- return the iterator function
 | 
			
		||||
    local i = 0
 | 
			
		||||
    return function()
 | 
			
		||||
        i = i + 1
 | 
			
		||||
        if keys[i] then
 | 
			
		||||
            return keys[i], t[keys[i]]
 | 
			
		||||
        end
 | 
			
		||||
    end
 | 
			
		||||
	-- return the iterator function
 | 
			
		||||
	local i = 0
 | 
			
		||||
	return function()
 | 
			
		||||
		i = i + 1
 | 
			
		||||
		if keys[i] then
 | 
			
		||||
			return keys[i], t[keys[i]]
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- create the partition of singletons of a given set
 | 
			
		||||
-- example: the trivial partition set of {a, b, c}, is {{a}, {b}, {c}}
 | 
			
		||||
function helpers.trivial_partition_set(set)
 | 
			
		||||
    local ss = {}
 | 
			
		||||
    for _,e in pairs(set) do
 | 
			
		||||
        ss[#ss+1] = {e}
 | 
			
		||||
    end
 | 
			
		||||
    return ss
 | 
			
		||||
	local ss = {}
 | 
			
		||||
	for _, e in pairs(set) do
 | 
			
		||||
		ss[#ss + 1] = { e }
 | 
			
		||||
	end
 | 
			
		||||
	return ss
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- create the powerset of a given set
 | 
			
		||||
function helpers.powerset(s)
 | 
			
		||||
    if not s then return {} end
 | 
			
		||||
    local t = {{}}
 | 
			
		||||
    for i = 1, #s do
 | 
			
		||||
        for j = 1, #t do
 | 
			
		||||
            t[#t+1] = {s[i],unpack(t[j])}
 | 
			
		||||
        end
 | 
			
		||||
    end
 | 
			
		||||
    return t
 | 
			
		||||
	if not s then
 | 
			
		||||
		return {}
 | 
			
		||||
	end
 | 
			
		||||
	local t = { {} }
 | 
			
		||||
	for i = 1, #s do
 | 
			
		||||
		for j = 1, #t do
 | 
			
		||||
			t[#t + 1] = { s[i], unpack(t[j]) }
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
	return t
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- }}}
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@
 | 
			
		||||
--]]
 | 
			
		||||
 | 
			
		||||
return {
 | 
			
		||||
    layout = require("lain.layout"),
 | 
			
		||||
    util   = require("lain.util"),
 | 
			
		||||
    widget = require("lain.widget")
 | 
			
		||||
	layout = require('lain.layout'),
 | 
			
		||||
	util = require('lain.util'),
 | 
			
		||||
	widget = require('lain.widget'),
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -7,166 +7,184 @@
 | 
			
		||||
 | 
			
		||||
--]]
 | 
			
		||||
 | 
			
		||||
local floor  = math.floor
 | 
			
		||||
local floor = math.floor
 | 
			
		||||
local screen = screen
 | 
			
		||||
 | 
			
		||||
local cascade = {
 | 
			
		||||
    name     = "cascade",
 | 
			
		||||
    nmaster  = 0,
 | 
			
		||||
    offset_x = 32,
 | 
			
		||||
    offset_y = 8,
 | 
			
		||||
    tile     = {
 | 
			
		||||
        name          = "cascadetile",
 | 
			
		||||
        nmaster       = 0,
 | 
			
		||||
        ncol          = 0,
 | 
			
		||||
        mwfact        = 0,
 | 
			
		||||
        offset_x      = 5,
 | 
			
		||||
        offset_y      = 32,
 | 
			
		||||
        extra_padding = 0
 | 
			
		||||
    }
 | 
			
		||||
	name = 'cascade',
 | 
			
		||||
	nmaster = 0,
 | 
			
		||||
	offset_x = 32,
 | 
			
		||||
	offset_y = 8,
 | 
			
		||||
	tile = {
 | 
			
		||||
		name = 'cascadetile',
 | 
			
		||||
		nmaster = 0,
 | 
			
		||||
		ncol = 0,
 | 
			
		||||
		mwfact = 0,
 | 
			
		||||
		offset_x = 5,
 | 
			
		||||
		offset_y = 32,
 | 
			
		||||
		extra_padding = 0,
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
local function do_cascade(p, tiling)
 | 
			
		||||
    local t = p.tag or screen[p.screen].selected_tag
 | 
			
		||||
    local wa = p.workarea
 | 
			
		||||
    local cls = p.clients
 | 
			
		||||
	local t = p.tag or screen[p.screen].selected_tag
 | 
			
		||||
	local wa = p.workarea
 | 
			
		||||
	local cls = p.clients
 | 
			
		||||
 | 
			
		||||
    if #cls == 0 then return end
 | 
			
		||||
	if #cls == 0 then
 | 
			
		||||
		return
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    if not tiling then
 | 
			
		||||
        -- Cascade windows.
 | 
			
		||||
	if not tiling then
 | 
			
		||||
		-- Cascade windows.
 | 
			
		||||
 | 
			
		||||
        local num_c
 | 
			
		||||
        if cascade.nmaster > 0 then
 | 
			
		||||
            num_c = cascade.nmaster
 | 
			
		||||
        else
 | 
			
		||||
            num_c = t.master_count
 | 
			
		||||
        end
 | 
			
		||||
		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
 | 
			
		||||
		-- 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)
 | 
			
		||||
		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 = {}
 | 
			
		||||
		-- Iterate.
 | 
			
		||||
		for i = 1, #cls, 1 do
 | 
			
		||||
			local c = cls[i]
 | 
			
		||||
			local g = {}
 | 
			
		||||
 | 
			
		||||
            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
 | 
			
		||||
			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
 | 
			
		||||
			if g.width < 1 then
 | 
			
		||||
				g.width = 1
 | 
			
		||||
			end
 | 
			
		||||
			if g.height < 1 then
 | 
			
		||||
				g.height = 1
 | 
			
		||||
			end
 | 
			
		||||
 | 
			
		||||
            p.geometries[c] = g
 | 
			
		||||
        end
 | 
			
		||||
    else
 | 
			
		||||
        -- 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.
 | 
			
		||||
			p.geometries[c] = g
 | 
			
		||||
		end
 | 
			
		||||
	else
 | 
			
		||||
		-- 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.
 | 
			
		||||
 | 
			
		||||
        --         (1)                 (2)                 (3)                 (4)
 | 
			
		||||
        --   +----------+---+    +----------+---+    +----------+---+    +----------+---+
 | 
			
		||||
        --   |          |   |    |          | 3 |    |          | 4 |    |         +---+|
 | 
			
		||||
        --   |          |   | -> |          |   | -> |         +---++ -> |        +---+|+
 | 
			
		||||
        --   |  1       | 2 |    |  1      +---++    |  1      | 3 ||    |  1    +---+|+|
 | 
			
		||||
        --   |          |   |    |         | 2 ||    |        +---++|    |      +---+|+ |
 | 
			
		||||
        --   |          |   |    |         |   ||    |        | 2 | |    |      | 2 |+  |
 | 
			
		||||
        --   +----------+---+    +---------+---++    +--------+---+-+    +------+---+---+
 | 
			
		||||
		--         (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
 | 
			
		||||
		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
 | 
			
		||||
		-- 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
 | 
			
		||||
		-- 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 how_many = (#cls - 1 >= num_c and (#cls - 1)) or num_c
 | 
			
		||||
		local how_many = (#cls - 1 >= num_c and (#cls - 1)) or num_c
 | 
			
		||||
 | 
			
		||||
        local current_offset_x = cascade.tile.offset_x * (how_many - 1)
 | 
			
		||||
        local current_offset_y = cascade.tile.offset_y * (how_many - 1)
 | 
			
		||||
		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
 | 
			
		||||
		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
 | 
			
		||||
		-- 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
 | 
			
		||||
		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
 | 
			
		||||
			-- 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.height = wa.height
 | 
			
		||||
        g.x = wa.x
 | 
			
		||||
        g.y = wa.y
 | 
			
		||||
		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
 | 
			
		||||
		if g.width < 1 then
 | 
			
		||||
			g.width = 1
 | 
			
		||||
		end
 | 
			
		||||
		if g.height < 1 then
 | 
			
		||||
			g.height = 1
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
        p.geometries[c] = 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 = {}
 | 
			
		||||
		-- 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.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
 | 
			
		||||
			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
 | 
			
		||||
			if g.width < 1 then
 | 
			
		||||
				g.width = 1
 | 
			
		||||
			end
 | 
			
		||||
			if g.height < 1 then
 | 
			
		||||
				g.height = 1
 | 
			
		||||
			end
 | 
			
		||||
 | 
			
		||||
            p.geometries[c] = g
 | 
			
		||||
        end
 | 
			
		||||
    end
 | 
			
		||||
			p.geometries[c] = g
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function cascade.tile.arrange(p)
 | 
			
		||||
    return do_cascade(p, true)
 | 
			
		||||
	return do_cascade(p, true)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function cascade.arrange(p)
 | 
			
		||||
    return do_cascade(p, false)
 | 
			
		||||
	return do_cascade(p, false)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
return cascade
 | 
			
		||||
 
 | 
			
		||||
@@ -17,260 +17,274 @@ local mousegrabber = mousegrabber
 | 
			
		||||
local screen = screen
 | 
			
		||||
 | 
			
		||||
local centerwork = {
 | 
			
		||||
    name       = "centerwork",
 | 
			
		||||
    horizontal = { name = "centerworkh" }
 | 
			
		||||
	name = 'centerwork',
 | 
			
		||||
	horizontal = { name = 'centerworkh' },
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
local function arrange(p, layout)
 | 
			
		||||
    local t   = p.tag or screen[p.screen].selected_tag
 | 
			
		||||
    local wa  = p.workarea
 | 
			
		||||
    local cls = p.clients
 | 
			
		||||
	local t = p.tag or screen[p.screen].selected_tag
 | 
			
		||||
	local wa = p.workarea
 | 
			
		||||
	local cls = p.clients
 | 
			
		||||
 | 
			
		||||
    if #cls == 0 then return end
 | 
			
		||||
	if #cls == 0 then
 | 
			
		||||
		return
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    local g = {}
 | 
			
		||||
	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)
 | 
			
		||||
	-- 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
 | 
			
		||||
	local slaveFirstDim, slaveSecondDim = 0, 0
 | 
			
		||||
 | 
			
		||||
    if layout.name == "centerwork" then -- vertical
 | 
			
		||||
        if nbrFirstSlaves  > 0 then slaveFirstDim  = floor(wa.height / nbrFirstSlaves) end
 | 
			
		||||
        if nbrSecondSlaves > 0 then slaveSecondDim = floor(wa.height / nbrSecondSlaves) end
 | 
			
		||||
	if layout.name == 'centerwork' then -- vertical
 | 
			
		||||
		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
 | 
			
		||||
		g.height = wa.height
 | 
			
		||||
		g.width = mainwid
 | 
			
		||||
 | 
			
		||||
        g.x = wa.x + slaveLwid
 | 
			
		||||
        g.y = wa.y
 | 
			
		||||
    else -- horizontal
 | 
			
		||||
        if nbrFirstSlaves  > 0 then slaveFirstDim  = floor(wa.width / nbrFirstSlaves) end
 | 
			
		||||
        if nbrSecondSlaves > 0 then slaveSecondDim = floor(wa.width / nbrSecondSlaves) end
 | 
			
		||||
		g.x = wa.x + slaveLwid
 | 
			
		||||
		g.y = wa.y
 | 
			
		||||
	else -- horizontal
 | 
			
		||||
		if nbrFirstSlaves > 0 then
 | 
			
		||||
			slaveFirstDim = floor(wa.width / nbrFirstSlaves)
 | 
			
		||||
		end
 | 
			
		||||
		if nbrSecondSlaves > 0 then
 | 
			
		||||
			slaveSecondDim = floor(wa.width / nbrSecondSlaves)
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
        g.height  = mainhei
 | 
			
		||||
        g.width = wa.width
 | 
			
		||||
		g.height = mainhei
 | 
			
		||||
		g.width = wa.width
 | 
			
		||||
 | 
			
		||||
        g.x = wa.x
 | 
			
		||||
        g.y = wa.y + slaveThei
 | 
			
		||||
    end
 | 
			
		||||
		g.x = wa.x
 | 
			
		||||
		g.y = wa.y + slaveThei
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    g.width  = max(g.width, 1)
 | 
			
		||||
    g.height = max(g.height, 1)
 | 
			
		||||
	g.width = max(g.width, 1)
 | 
			
		||||
	g.height = max(g.height, 1)
 | 
			
		||||
 | 
			
		||||
    p.geometries[cls[1]] = g
 | 
			
		||||
	p.geometries[cls[1]] = g
 | 
			
		||||
 | 
			
		||||
    -- Auxiliary clients
 | 
			
		||||
    if #cls <= 1 then return end
 | 
			
		||||
    for i = 2, #cls do
 | 
			
		||||
        g = {}
 | 
			
		||||
        local idxChecker, dimToAssign
 | 
			
		||||
	-- Auxiliary clients
 | 
			
		||||
	if #cls <= 1 then
 | 
			
		||||
		return
 | 
			
		||||
	end
 | 
			
		||||
	for i = 2, #cls do
 | 
			
		||||
		g = {}
 | 
			
		||||
		local idxChecker, dimToAssign
 | 
			
		||||
 | 
			
		||||
        local rowIndex = floor(i/2)
 | 
			
		||||
		local rowIndex = floor(i / 2)
 | 
			
		||||
 | 
			
		||||
        if layout.name == "centerwork" then
 | 
			
		||||
            if i % 2 == 0 then -- left slave
 | 
			
		||||
                g.x     = wa.x
 | 
			
		||||
                g.y     = wa.y + (rowIndex - 1) * slaveFirstDim
 | 
			
		||||
                g.width = slaveLwid
 | 
			
		||||
		if layout.name == 'centerwork' then
 | 
			
		||||
			if i % 2 == 0 then -- left slave
 | 
			
		||||
				g.x = wa.x
 | 
			
		||||
				g.y = wa.y + (rowIndex - 1) * slaveFirstDim
 | 
			
		||||
				g.width = slaveLwid
 | 
			
		||||
 | 
			
		||||
                idxChecker, dimToAssign = nbrFirstSlaves, slaveFirstDim
 | 
			
		||||
            else -- right slave
 | 
			
		||||
                g.x     = wa.x + slaveLwid + mainwid
 | 
			
		||||
                g.y     = wa.y + (rowIndex - 1) * slaveSecondDim
 | 
			
		||||
                g.width = slaveRwid
 | 
			
		||||
				idxChecker, dimToAssign = nbrFirstSlaves, slaveFirstDim
 | 
			
		||||
			else -- right slave
 | 
			
		||||
				g.x = wa.x + slaveLwid + mainwid
 | 
			
		||||
				g.y = wa.y + (rowIndex - 1) * slaveSecondDim
 | 
			
		||||
				g.width = slaveRwid
 | 
			
		||||
 | 
			
		||||
                idxChecker, dimToAssign = nbrSecondSlaves, slaveSecondDim
 | 
			
		||||
            end
 | 
			
		||||
				idxChecker, dimToAssign = nbrSecondSlaves, slaveSecondDim
 | 
			
		||||
			end
 | 
			
		||||
 | 
			
		||||
            -- if last slave in row, use remaining space for it
 | 
			
		||||
            if rowIndex == idxChecker then
 | 
			
		||||
                g.height = wa.y + wa.height - g.y
 | 
			
		||||
            else
 | 
			
		||||
                g.height = dimToAssign
 | 
			
		||||
            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 row, use remaining space for it
 | 
			
		||||
			if rowIndex == idxChecker then
 | 
			
		||||
				g.height = wa.y + wa.height - g.y
 | 
			
		||||
			else
 | 
			
		||||
				g.height = dimToAssign
 | 
			
		||||
			end
 | 
			
		||||
		else
 | 
			
		||||
			if i % 2 == 0 then -- top slave
 | 
			
		||||
				g.x = wa.x + (rowIndex - 1) * slaveFirstDim
 | 
			
		||||
				g.y = wa.y
 | 
			
		||||
				g.height = slaveThei
 | 
			
		||||
 | 
			
		||||
                idxChecker, dimToAssign = nbrFirstSlaves, slaveFirstDim
 | 
			
		||||
            else -- bottom slave
 | 
			
		||||
                g.x      = wa.x + (rowIndex - 1) * slaveSecondDim
 | 
			
		||||
                g.y      = wa.y + slaveThei + mainhei
 | 
			
		||||
                g.height = slaveBhei
 | 
			
		||||
				idxChecker, dimToAssign = nbrFirstSlaves, slaveFirstDim
 | 
			
		||||
			else -- bottom slave
 | 
			
		||||
				g.x = wa.x + (rowIndex - 1) * slaveSecondDim
 | 
			
		||||
				g.y = wa.y + slaveThei + mainhei
 | 
			
		||||
				g.height = slaveBhei
 | 
			
		||||
 | 
			
		||||
                idxChecker, dimToAssign = nbrSecondSlaves, slaveSecondDim
 | 
			
		||||
            end
 | 
			
		||||
				idxChecker, dimToAssign = nbrSecondSlaves, slaveSecondDim
 | 
			
		||||
			end
 | 
			
		||||
 | 
			
		||||
            -- if last slave in row, use remaining space for it
 | 
			
		||||
            if rowIndex == idxChecker then
 | 
			
		||||
                g.width = wa.x + wa.width - g.x
 | 
			
		||||
            else
 | 
			
		||||
                g.width = dimToAssign
 | 
			
		||||
            end
 | 
			
		||||
        end
 | 
			
		||||
			-- if last slave in row, use remaining space for it
 | 
			
		||||
			if rowIndex == idxChecker then
 | 
			
		||||
				g.width = wa.x + wa.width - g.x
 | 
			
		||||
			else
 | 
			
		||||
				g.width = dimToAssign
 | 
			
		||||
			end
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
        g.width  = max(g.width, 1)
 | 
			
		||||
        g.height = max(g.height, 1)
 | 
			
		||||
		g.width = max(g.width, 1)
 | 
			
		||||
		g.height = max(g.height, 1)
 | 
			
		||||
 | 
			
		||||
        p.geometries[cls[i]] = g
 | 
			
		||||
    end
 | 
			
		||||
		p.geometries[cls[i]] = g
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function mouse_resize_handler(c, _, _, _, orientation)
 | 
			
		||||
    local wa     = c.screen.workarea
 | 
			
		||||
    local mwfact = c.screen.selected_tag.master_width_factor
 | 
			
		||||
    local g      = c:geometry()
 | 
			
		||||
    local offset = 0
 | 
			
		||||
    local cursor = "cross"
 | 
			
		||||
	local wa = c.screen.workarea
 | 
			
		||||
	local mwfact = c.screen.selected_tag.master_width_factor
 | 
			
		||||
	local g = c:geometry()
 | 
			
		||||
	local offset = 0
 | 
			
		||||
	local cursor = 'cross'
 | 
			
		||||
 | 
			
		||||
    local corner_coords
 | 
			
		||||
	local corner_coords
 | 
			
		||||
 | 
			
		||||
    if orientation == 'vertical' then
 | 
			
		||||
        if g.height + 15 >= wa.height then
 | 
			
		||||
            offset = g.height * .5
 | 
			
		||||
            cursor = "sb_h_double_arrow"
 | 
			
		||||
        elseif g.y + g.height + 15 <= wa.y + wa.height then
 | 
			
		||||
            offset = g.height
 | 
			
		||||
        end
 | 
			
		||||
        corner_coords = { x = wa.x + wa.width * (1 - mwfact) / 2, y = g.y + offset }
 | 
			
		||||
    else
 | 
			
		||||
        if g.width + 15 >= wa.width then
 | 
			
		||||
            offset = g.width * .5
 | 
			
		||||
            cursor = "sb_v_double_arrow"
 | 
			
		||||
        elseif g.x + g.width + 15 <= wa.x + wa.width then
 | 
			
		||||
            offset = g.width
 | 
			
		||||
        end
 | 
			
		||||
        corner_coords = { y = wa.y + wa.height * (1 - mwfact) / 2, x = g.x + offset }
 | 
			
		||||
    end
 | 
			
		||||
	if orientation == 'vertical' then
 | 
			
		||||
		if g.height + 15 >= wa.height then
 | 
			
		||||
			offset = g.height * 0.5
 | 
			
		||||
			cursor = 'sb_h_double_arrow'
 | 
			
		||||
		elseif g.y + g.height + 15 <= wa.y + wa.height then
 | 
			
		||||
			offset = g.height
 | 
			
		||||
		end
 | 
			
		||||
		corner_coords = { x = wa.x + wa.width * (1 - mwfact) / 2, y = g.y + offset }
 | 
			
		||||
	else
 | 
			
		||||
		if g.width + 15 >= wa.width then
 | 
			
		||||
			offset = g.width * 0.5
 | 
			
		||||
			cursor = 'sb_v_double_arrow'
 | 
			
		||||
		elseif g.x + g.width + 15 <= wa.x + wa.width then
 | 
			
		||||
			offset = g.width
 | 
			
		||||
		end
 | 
			
		||||
		corner_coords = { y = wa.y + wa.height * (1 - mwfact) / 2, x = g.x + offset }
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    mouse.coords(corner_coords)
 | 
			
		||||
	mouse.coords(corner_coords)
 | 
			
		||||
 | 
			
		||||
    local prev_coords = {}
 | 
			
		||||
	local prev_coords = {}
 | 
			
		||||
 | 
			
		||||
    mousegrabber.run(function(m)
 | 
			
		||||
        if not c.valid then return false end
 | 
			
		||||
        for _, v in ipairs(m.buttons) do
 | 
			
		||||
            if v then
 | 
			
		||||
                prev_coords = { x = m.x, y = m.y }
 | 
			
		||||
                local new_mwfact
 | 
			
		||||
                if orientation == 'vertical' then
 | 
			
		||||
                    new_mwfact = 1 - (m.x - wa.x) / wa.width * 2
 | 
			
		||||
                else
 | 
			
		||||
                    new_mwfact = 1 - (m.y - wa.y) / wa.height * 2
 | 
			
		||||
                end
 | 
			
		||||
                c.screen.selected_tag.master_width_factor = math.min(math.max(new_mwfact, 0.01), 0.99)
 | 
			
		||||
                return true
 | 
			
		||||
            end
 | 
			
		||||
        end
 | 
			
		||||
        return prev_coords.x == m.x and prev_coords.y == m.y
 | 
			
		||||
    end, cursor)
 | 
			
		||||
	mousegrabber.run(function(m)
 | 
			
		||||
		if not c.valid then
 | 
			
		||||
			return false
 | 
			
		||||
		end
 | 
			
		||||
		for _, v in ipairs(m.buttons) do
 | 
			
		||||
			if v then
 | 
			
		||||
				prev_coords = { x = m.x, y = m.y }
 | 
			
		||||
				local new_mwfact
 | 
			
		||||
				if orientation == 'vertical' then
 | 
			
		||||
					new_mwfact = 1 - (m.x - wa.x) / wa.width * 2
 | 
			
		||||
				else
 | 
			
		||||
					new_mwfact = 1 - (m.y - wa.y) / wa.height * 2
 | 
			
		||||
				end
 | 
			
		||||
				c.screen.selected_tag.master_width_factor = math.min(math.max(new_mwfact, 0.01), 0.99)
 | 
			
		||||
				return true
 | 
			
		||||
			end
 | 
			
		||||
		end
 | 
			
		||||
		return prev_coords.x == m.x and prev_coords.y == m.y
 | 
			
		||||
	end, cursor)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function centerwork.arrange(p)
 | 
			
		||||
    return arrange(p, centerwork)
 | 
			
		||||
	return arrange(p, centerwork)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function centerwork.horizontal.arrange(p)
 | 
			
		||||
    return arrange(p, centerwork.horizontal)
 | 
			
		||||
	return arrange(p, centerwork.horizontal)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function centerwork.mouse_resize_handler(c, corner, x, y)
 | 
			
		||||
    return mouse_resize_handler(c, corner, x, y, 'vertical')
 | 
			
		||||
	return mouse_resize_handler(c, corner, x, y, 'vertical')
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function centerwork.horizontal.mouse_resize_handler(c, corner, x, y)
 | 
			
		||||
    return mouse_resize_handler(c, corner, x, y, 'horizontal')
 | 
			
		||||
	return mouse_resize_handler(c, corner, x, y, 'horizontal')
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--[[
 | 
			
		||||
Make focus.byidx and swap.byidx behave more consistently with other layouts.
 | 
			
		||||
--]]
 | 
			
		||||
 | 
			
		||||
local awful = require("awful")
 | 
			
		||||
local gears = require("gears")
 | 
			
		||||
local awful = require('awful')
 | 
			
		||||
local gears = require('gears')
 | 
			
		||||
local client = client
 | 
			
		||||
 | 
			
		||||
local function compare_position(a, b)
 | 
			
		||||
    if a.x == b.x then
 | 
			
		||||
        return a.y < b.y
 | 
			
		||||
    else
 | 
			
		||||
        return a.x < b.x
 | 
			
		||||
    end
 | 
			
		||||
	if a.x == b.x then
 | 
			
		||||
		return a.y < b.y
 | 
			
		||||
	else
 | 
			
		||||
		return a.x < b.x
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function clients_by_position()
 | 
			
		||||
    local this = client.focus
 | 
			
		||||
    if this then
 | 
			
		||||
        local sorted = {}
 | 
			
		||||
        for _, c in ipairs(client.focus.first_tag:clients()) do
 | 
			
		||||
            if not c.minimized then sorted[#sorted+1] = c end
 | 
			
		||||
        end
 | 
			
		||||
        table.sort(sorted, compare_position)
 | 
			
		||||
	local this = client.focus
 | 
			
		||||
	if this then
 | 
			
		||||
		local sorted = {}
 | 
			
		||||
		for _, c in ipairs(client.focus.first_tag:clients()) do
 | 
			
		||||
			if not c.minimized then
 | 
			
		||||
				sorted[#sorted + 1] = c
 | 
			
		||||
			end
 | 
			
		||||
		end
 | 
			
		||||
		table.sort(sorted, compare_position)
 | 
			
		||||
 | 
			
		||||
        local idx = 0
 | 
			
		||||
        for i, that in ipairs(sorted) do
 | 
			
		||||
            if this.window == that.window then
 | 
			
		||||
                idx = i
 | 
			
		||||
            end
 | 
			
		||||
        end
 | 
			
		||||
		local idx = 0
 | 
			
		||||
		for i, that in ipairs(sorted) do
 | 
			
		||||
			if this.window == that.window then
 | 
			
		||||
				idx = i
 | 
			
		||||
			end
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
        if idx > 0 then
 | 
			
		||||
            return { sorted = sorted, idx = idx }
 | 
			
		||||
        end
 | 
			
		||||
    end
 | 
			
		||||
    return {}
 | 
			
		||||
		if idx > 0 then
 | 
			
		||||
			return { sorted = sorted, idx = idx }
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
	return {}
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local function in_centerwork()
 | 
			
		||||
    return client.focus and client.focus.first_tag.layout.name == "centerwork"
 | 
			
		||||
	return client.focus and client.focus.first_tag.layout.name == 'centerwork'
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
centerwork.focus = {}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
--[[
 | 
			
		||||
Drop in replacements for awful.client.focus.byidx and awful.client.swap.byidx
 | 
			
		||||
that behaves consistently with other layouts.
 | 
			
		||||
--]]
 | 
			
		||||
 | 
			
		||||
function centerwork.focus.byidx(i)
 | 
			
		||||
    if in_centerwork() then
 | 
			
		||||
        local cls = clients_by_position()
 | 
			
		||||
        if cls.idx then
 | 
			
		||||
            local target = cls.sorted[gears.math.cycle(#cls.sorted, cls.idx + i)]
 | 
			
		||||
            awful.client.focus.byidx(0, target)
 | 
			
		||||
        end
 | 
			
		||||
    else
 | 
			
		||||
        awful.client.focus.byidx(i)
 | 
			
		||||
    end
 | 
			
		||||
	if in_centerwork() then
 | 
			
		||||
		local cls = clients_by_position()
 | 
			
		||||
		if cls.idx then
 | 
			
		||||
			local target = cls.sorted[gears.math.cycle(#cls.sorted, cls.idx + i)]
 | 
			
		||||
			awful.client.focus.byidx(0, target)
 | 
			
		||||
		end
 | 
			
		||||
	else
 | 
			
		||||
		awful.client.focus.byidx(i)
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
centerwork.swap = {}
 | 
			
		||||
 | 
			
		||||
function centerwork.swap.byidx(i)
 | 
			
		||||
    if in_centerwork() then
 | 
			
		||||
        local cls = clients_by_position()
 | 
			
		||||
        if cls.idx then
 | 
			
		||||
            local target = cls.sorted[gears.math.cycle(#cls.sorted, cls.idx + i)]
 | 
			
		||||
            client.focus:swap(target)
 | 
			
		||||
        end
 | 
			
		||||
    else
 | 
			
		||||
        awful.client.swap.byidx(i)
 | 
			
		||||
    end
 | 
			
		||||
	if in_centerwork() then
 | 
			
		||||
		local cls = clients_by_position()
 | 
			
		||||
		if cls.idx then
 | 
			
		||||
			local target = cls.sorted[gears.math.cycle(#cls.sorted, cls.idx + i)]
 | 
			
		||||
			client.focus:swap(target)
 | 
			
		||||
		end
 | 
			
		||||
	else
 | 
			
		||||
		awful.client.swap.byidx(i)
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
return centerwork
 | 
			
		||||
 
 | 
			
		||||
@@ -11,9 +11,9 @@
 | 
			
		||||
 | 
			
		||||
--]]
 | 
			
		||||
 | 
			
		||||
local wrequire     = require("lain.helpers").wrequire
 | 
			
		||||
local wrequire = require('lain.helpers').wrequire
 | 
			
		||||
local setmetatable = setmetatable
 | 
			
		||||
 | 
			
		||||
local layout       = { _NAME = "lain.layout" }
 | 
			
		||||
local layout = { _NAME = 'lain.layout' }
 | 
			
		||||
 | 
			
		||||
return setmetatable(layout, { __index = wrequire })
 | 
			
		||||
 
 | 
			
		||||
@@ -8,275 +8,305 @@
 | 
			
		||||
 | 
			
		||||
--]]
 | 
			
		||||
 | 
			
		||||
local math     = math
 | 
			
		||||
local screen   = screen
 | 
			
		||||
local math = math
 | 
			
		||||
local screen = screen
 | 
			
		||||
local tonumber = tonumber
 | 
			
		||||
 | 
			
		||||
local termfair  = { name = "termfair" }
 | 
			
		||||
termfair.center = { name = "centerfair" }
 | 
			
		||||
termfair.stable = { name = "stablefair" }
 | 
			
		||||
local termfair = { name = 'termfair' }
 | 
			
		||||
termfair.center = { name = 'centerfair' }
 | 
			
		||||
termfair.stable = { name = 'stablefair' }
 | 
			
		||||
 | 
			
		||||
local function do_fair(p, orientation)
 | 
			
		||||
    local t = p.tag or screen[p.screen].selected_tag
 | 
			
		||||
    local wa = p.workarea
 | 
			
		||||
    local cls = p.clients
 | 
			
		||||
	local t = p.tag or screen[p.screen].selected_tag
 | 
			
		||||
	local wa = p.workarea
 | 
			
		||||
	local cls = p.clients
 | 
			
		||||
 | 
			
		||||
    if #cls == 0 then return end
 | 
			
		||||
	if #cls == 0 then
 | 
			
		||||
		return
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    -- 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)
 | 
			
		||||
	-- 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)
 | 
			
		||||
 | 
			
		||||
    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 new
 | 
			
		||||
        -- one above it is created. Like this:
 | 
			
		||||
	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 new
 | 
			
		||||
		-- one above it is created. Like this:
 | 
			
		||||
 | 
			
		||||
        --        (1)                (2)                (3)
 | 
			
		||||
        --   +---+---+---+      +---+---+---+      +---+---+---+
 | 
			
		||||
        --   |   |   |   |      |   |   |   |      |   |   |   |
 | 
			
		||||
        --   | 1 |   |   |  ->  | 1 | 2 |   |  ->  | 1 | 2 | 3 |  ->
 | 
			
		||||
        --   |   |   |   |      |   |   |   |      |   |   |   |
 | 
			
		||||
        --   +---+---+---+      +---+---+---+      +---+---+---+
 | 
			
		||||
		--        (1)                (2)                (3)
 | 
			
		||||
		--   +---+---+---+      +---+---+---+      +---+---+---+
 | 
			
		||||
		--   |   |   |   |      |   |   |   |      |   |   |   |
 | 
			
		||||
		--   | 1 |   |   |  ->  | 1 | 2 |   |  ->  | 1 | 2 | 3 |  ->
 | 
			
		||||
		--   |   |   |   |      |   |   |   |      |   |   |   |
 | 
			
		||||
		--   +---+---+---+      +---+---+---+      +---+---+---+
 | 
			
		||||
 | 
			
		||||
        --        (4)                (5)                (6)
 | 
			
		||||
        --   +---+---+---+      +---+---+---+      +---+---+---+
 | 
			
		||||
        --   | 1 |   |   |      | 1 | 2 |   |      | 1 | 2 | 3 |
 | 
			
		||||
        --   +---+---+---+  ->  +---+---+---+  ->  +---+---+---+
 | 
			
		||||
        --   | 2 | 3 | 4 |      | 3 | 4 | 5 |      | 4 | 5 | 6 |
 | 
			
		||||
        --   +---+---+---+      +---+---+---+      +---+---+---+
 | 
			
		||||
		--        (4)                (5)                (6)
 | 
			
		||||
		--   +---+---+---+      +---+---+---+      +---+---+---+
 | 
			
		||||
		--   | 1 |   |   |      | 1 | 2 |   |      | 1 | 2 | 3 |
 | 
			
		||||
		--   +---+---+---+  ->  +---+---+---+  ->  +---+---+---+
 | 
			
		||||
		--   | 2 | 3 | 4 |      | 3 | 4 | 5 |      | 4 | 5 | 6 |
 | 
			
		||||
		--   +---+---+---+      +---+---+---+      +---+---+---+
 | 
			
		||||
 | 
			
		||||
        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 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 remaining_clients = #cls
 | 
			
		||||
		local remaining_clients = #cls
 | 
			
		||||
 | 
			
		||||
        -- We start the first row. Left-align by limiting the number of
 | 
			
		||||
        -- available slots.
 | 
			
		||||
        if remaining_clients < num_x then
 | 
			
		||||
            cur_num_x = remaining_clients
 | 
			
		||||
        end
 | 
			
		||||
		-- We start the first row. Left-align by limiting the number of
 | 
			
		||||
		-- available slots.
 | 
			
		||||
		if remaining_clients < num_x then
 | 
			
		||||
			cur_num_x = remaining_clients
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
        -- Iterate in reversed order.
 | 
			
		||||
        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
 | 
			
		||||
		-- Iterate in reversed order.
 | 
			
		||||
		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
 | 
			
		||||
 | 
			
		||||
            -- Calculate geometry.
 | 
			
		||||
            local g = {}
 | 
			
		||||
            if this_x == (num_x - 1) then
 | 
			
		||||
                g.width = wa.width - (num_x - 1)*width
 | 
			
		||||
            else
 | 
			
		||||
                g.width = width
 | 
			
		||||
            end
 | 
			
		||||
			-- Calculate geometry.
 | 
			
		||||
			local g = {}
 | 
			
		||||
			if this_x == (num_x - 1) then
 | 
			
		||||
				g.width = wa.width - (num_x - 1) * width
 | 
			
		||||
			else
 | 
			
		||||
				g.width = width
 | 
			
		||||
			end
 | 
			
		||||
 | 
			
		||||
            if this_y == (num_y - 1) then
 | 
			
		||||
                g.height = wa.height - (num_y - 1)*height
 | 
			
		||||
            else
 | 
			
		||||
                g.height = height
 | 
			
		||||
            end
 | 
			
		||||
			if this_y == (num_y - 1) then
 | 
			
		||||
				g.height = wa.height - (num_y - 1) * height
 | 
			
		||||
			else
 | 
			
		||||
				g.height = height
 | 
			
		||||
			end
 | 
			
		||||
 | 
			
		||||
            g.x = wa.x + this_x*width
 | 
			
		||||
            g.y = wa.y + this_y*height
 | 
			
		||||
			g.x = wa.x + this_x * width
 | 
			
		||||
			g.y = wa.y + this_y * height
 | 
			
		||||
 | 
			
		||||
            if g.width  < 1 then g.width  = 1 end
 | 
			
		||||
            if g.height < 1 then g.height = 1 end
 | 
			
		||||
			if g.width < 1 then
 | 
			
		||||
				g.width = 1
 | 
			
		||||
			end
 | 
			
		||||
			if g.height < 1 then
 | 
			
		||||
				g.height = 1
 | 
			
		||||
			end
 | 
			
		||||
 | 
			
		||||
            p.geometries[c] = g
 | 
			
		||||
			p.geometries[c] = g
 | 
			
		||||
 | 
			
		||||
            remaining_clients = remaining_clients - 1
 | 
			
		||||
			remaining_clients = remaining_clients - 1
 | 
			
		||||
 | 
			
		||||
            -- Next grid position.
 | 
			
		||||
            at_x = at_x + 1
 | 
			
		||||
            if at_x == num_x then
 | 
			
		||||
                -- Row full, create a new one above it.
 | 
			
		||||
                at_x = 0
 | 
			
		||||
                at_y = at_y + 1
 | 
			
		||||
			-- Next grid position.
 | 
			
		||||
			at_x = at_x + 1
 | 
			
		||||
			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
 | 
			
		||||
                    cur_num_x = remaining_clients
 | 
			
		||||
                end
 | 
			
		||||
            end
 | 
			
		||||
        end
 | 
			
		||||
    elseif orientation == "stable" then
 | 
			
		||||
        -- Layout with fixed number of vertical columns (read from nmaster).
 | 
			
		||||
        -- New windows align from left to right. When a row is full, a new
 | 
			
		||||
        -- one below it is created. Like this:
 | 
			
		||||
				-- We start a new row. Left-align.
 | 
			
		||||
				if remaining_clients < num_x then
 | 
			
		||||
					cur_num_x = remaining_clients
 | 
			
		||||
				end
 | 
			
		||||
			end
 | 
			
		||||
		end
 | 
			
		||||
	elseif orientation == 'stable' then
 | 
			
		||||
		-- Layout with fixed number of vertical columns (read from nmaster).
 | 
			
		||||
		-- New windows align from left to right. When a row is full, a new
 | 
			
		||||
		-- one below it is created. Like this:
 | 
			
		||||
 | 
			
		||||
        --        (1)                (2)                (3)
 | 
			
		||||
        --   +---+---+---+      +---+---+---+      +---+---+---+
 | 
			
		||||
        --   |   |   |   |      |   |   |   |      |   |   |   |
 | 
			
		||||
        --   | 1 |   |   |  ->  | 1 | 2 |   |  ->  | 1 | 2 | 3 |  ->
 | 
			
		||||
        --   |   |   |   |      |   |   |   |      |   |   |   |
 | 
			
		||||
        --   +---+---+---+      +---+---+---+      +---+---+---+
 | 
			
		||||
		--        (1)                (2)                (3)
 | 
			
		||||
		--   +---+---+---+      +---+---+---+      +---+---+---+
 | 
			
		||||
		--   |   |   |   |      |   |   |   |      |   |   |   |
 | 
			
		||||
		--   | 1 |   |   |  ->  | 1 | 2 |   |  ->  | 1 | 2 | 3 |  ->
 | 
			
		||||
		--   |   |   |   |      |   |   |   |      |   |   |   |
 | 
			
		||||
		--   +---+---+---+      +---+---+---+      +---+---+---+
 | 
			
		||||
 | 
			
		||||
        --        (4)                (5)                (6)
 | 
			
		||||
        --   +---+---+---+      +---+---+---+      +---+---+---+
 | 
			
		||||
        --   | 1 | 2 | 3 |      | 1 | 2 | 3 |      | 1 | 2 | 3 |
 | 
			
		||||
        --   +---+---+---+      +---+---+---+      +---+---+---+
 | 
			
		||||
        --   | 4 |   |   |      | 4 | 5 |   |      | 4 | 5 | 6 |
 | 
			
		||||
        --   +---+---+---+  ->  +---+---+---+  ->  +---+---+---+
 | 
			
		||||
		--        (4)                (5)                (6)
 | 
			
		||||
		--   +---+---+---+      +---+---+---+      +---+---+---+
 | 
			
		||||
		--   | 1 | 2 | 3 |      | 1 | 2 | 3 |      | 1 | 2 | 3 |
 | 
			
		||||
		--   +---+---+---+      +---+---+---+      +---+---+---+
 | 
			
		||||
		--   | 4 |   |   |      | 4 | 5 |   |      | 4 | 5 | 6 |
 | 
			
		||||
		--   +---+---+---+  ->  +---+---+---+  ->  +---+---+---+
 | 
			
		||||
 | 
			
		||||
        local num_y     = math.max(math.ceil(#cls / num_x), ncol)
 | 
			
		||||
        local height    = math.floor(wa.height/num_y)
 | 
			
		||||
		local num_y = math.max(math.ceil(#cls / num_x), ncol)
 | 
			
		||||
		local height = math.floor(wa.height / num_y)
 | 
			
		||||
 | 
			
		||||
        for i = #cls,1,-1 do
 | 
			
		||||
            -- Get x and y position.
 | 
			
		||||
            local c = cls[i]
 | 
			
		||||
            local this_x = (i - 1) % num_x
 | 
			
		||||
            local this_y = math.floor((i - this_x - 1) / num_x)
 | 
			
		||||
		for i = #cls, 1, -1 do
 | 
			
		||||
			-- Get x and y position.
 | 
			
		||||
			local c = cls[i]
 | 
			
		||||
			local this_x = (i - 1) % num_x
 | 
			
		||||
			local this_y = math.floor((i - this_x - 1) / num_x)
 | 
			
		||||
 | 
			
		||||
            -- Calculate geometry.
 | 
			
		||||
            local g = {}
 | 
			
		||||
            if this_x == (num_x - 1) then
 | 
			
		||||
                g.width = wa.width - (num_x - 1)*width
 | 
			
		||||
            else
 | 
			
		||||
                g.width = width
 | 
			
		||||
            end
 | 
			
		||||
			-- Calculate geometry.
 | 
			
		||||
			local g = {}
 | 
			
		||||
			if this_x == (num_x - 1) then
 | 
			
		||||
				g.width = wa.width - (num_x - 1) * width
 | 
			
		||||
			else
 | 
			
		||||
				g.width = width
 | 
			
		||||
			end
 | 
			
		||||
 | 
			
		||||
            if this_y == (num_y - 1) then
 | 
			
		||||
                g.height = wa.height - (num_y - 1)*height
 | 
			
		||||
            else
 | 
			
		||||
                g.height = height
 | 
			
		||||
            end
 | 
			
		||||
			if this_y == (num_y - 1) then
 | 
			
		||||
				g.height = wa.height - (num_y - 1) * height
 | 
			
		||||
			else
 | 
			
		||||
				g.height = height
 | 
			
		||||
			end
 | 
			
		||||
 | 
			
		||||
            g.x = wa.x + this_x*width
 | 
			
		||||
            g.y = wa.y + this_y*height
 | 
			
		||||
			g.x = wa.x + this_x * width
 | 
			
		||||
			g.y = wa.y + this_y * height
 | 
			
		||||
 | 
			
		||||
            if g.width  < 1 then g.width  = 1 end
 | 
			
		||||
            if g.height < 1 then g.height = 1 end
 | 
			
		||||
			if g.width < 1 then
 | 
			
		||||
				g.width = 1
 | 
			
		||||
			end
 | 
			
		||||
			if g.height < 1 then
 | 
			
		||||
				g.height = 1
 | 
			
		||||
			end
 | 
			
		||||
 | 
			
		||||
            p.geometries[c] = g
 | 
			
		||||
        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.
 | 
			
		||||
			p.geometries[c] = g
 | 
			
		||||
		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 |  ->
 | 
			
		||||
        --   |   |   |   |      | |   |   | |      |   |   |   |
 | 
			
		||||
        --   +---+---+---+      +-+---+---+-+      +---+---+---+
 | 
			
		||||
		-- 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 |
 | 
			
		||||
        --   +---+---+---+      +---+---+---+
 | 
			
		||||
		--        (4)                (5)
 | 
			
		||||
		--   +---+---+---+      +---+---+---+
 | 
			
		||||
		--   |   |   | 3 |      |   | 2 | 4 |
 | 
			
		||||
		--   + 1 + 2 +---+  ->  + 1 +---+---+
 | 
			
		||||
		--   |   |   | 4 |      |   | 3 | 5 |
 | 
			
		||||
		--   +---+---+---+      +---+---+---+
 | 
			
		||||
 | 
			
		||||
        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
 | 
			
		||||
		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
 | 
			
		||||
			-- 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))
 | 
			
		||||
			-- 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
 | 
			
		||||
			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 _ = 0, (num_y[i]-2) do
 | 
			
		||||
                    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
 | 
			
		||||
                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
 | 
			
		||||
			-- 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 _ = 0, (num_y[i] - 2) do
 | 
			
		||||
					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
 | 
			
		||||
				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")
 | 
			
		||||
	return do_fair(p, 'center')
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function termfair.stable.arrange(p)
 | 
			
		||||
    return do_fair(p, "stable")
 | 
			
		||||
	return do_fair(p, 'stable')
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function termfair.arrange(p)
 | 
			
		||||
    return do_fair(p, "west")
 | 
			
		||||
	return do_fair(p, 'west')
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
return termfair
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -11,180 +11,200 @@
 | 
			
		||||
 | 
			
		||||
--]]
 | 
			
		||||
 | 
			
		||||
local awful        = require("awful")
 | 
			
		||||
local sqrt         = math.sqrt
 | 
			
		||||
local pairs        = pairs
 | 
			
		||||
local client       = client
 | 
			
		||||
local tonumber     = tonumber
 | 
			
		||||
local wrequire     = require("lain.helpers").wrequire
 | 
			
		||||
local awful = require('awful')
 | 
			
		||||
local sqrt = math.sqrt
 | 
			
		||||
local pairs = pairs
 | 
			
		||||
local client = client
 | 
			
		||||
local tonumber = tonumber
 | 
			
		||||
local wrequire = require('lain.helpers').wrequire
 | 
			
		||||
local setmetatable = setmetatable
 | 
			
		||||
 | 
			
		||||
-- Lain utilities submodule
 | 
			
		||||
-- lain.util
 | 
			
		||||
local util = { _NAME = "lain.util" }
 | 
			
		||||
local util = { _NAME = 'lain.util' }
 | 
			
		||||
 | 
			
		||||
-- 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.screen.focused().selected_tags
 | 
			
		||||
	-- List of currently selected tags.
 | 
			
		||||
	local cls_tags = awful.screen.focused().selected_tags
 | 
			
		||||
 | 
			
		||||
    if cls_tags == nil then return nil end
 | 
			
		||||
	if cls_tags == nil then
 | 
			
		||||
		return nil
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    -- Final list of menu items.
 | 
			
		||||
    local cls_t = {}
 | 
			
		||||
	-- Final list of menu items.
 | 
			
		||||
	local cls_t = {}
 | 
			
		||||
 | 
			
		||||
    -- 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]
 | 
			
		||||
        local cls = t:clients()
 | 
			
		||||
	-- 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]
 | 
			
		||||
		local cls = t:clients()
 | 
			
		||||
 | 
			
		||||
        for _, c in pairs(cls) do
 | 
			
		||||
            cls_t[#cls_t + 1] = { awful.util.escape(c.name) or "",
 | 
			
		||||
                                  function ()
 | 
			
		||||
                                      c.minimized = false
 | 
			
		||||
                                      client.focus = c
 | 
			
		||||
                                      c:raise()
 | 
			
		||||
                                  end,
 | 
			
		||||
                                  c.icon }
 | 
			
		||||
        end
 | 
			
		||||
    end
 | 
			
		||||
		for _, c in pairs(cls) do
 | 
			
		||||
			cls_t[#cls_t + 1] = {
 | 
			
		||||
				awful.util.escape(c.name) or '',
 | 
			
		||||
				function()
 | 
			
		||||
					c.minimized = false
 | 
			
		||||
					client.focus = c
 | 
			
		||||
					c:raise()
 | 
			
		||||
				end,
 | 
			
		||||
				c.icon,
 | 
			
		||||
			}
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    -- No clients? Then quit.
 | 
			
		||||
    if #cls_t <= 0 then return nil end
 | 
			
		||||
	-- No clients? Then quit.
 | 
			
		||||
	if #cls_t <= 0 then
 | 
			
		||||
		return nil
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    -- menu may contain some predefined values, otherwise start with a
 | 
			
		||||
    -- fresh menu.
 | 
			
		||||
    if not menu then menu = {} end
 | 
			
		||||
	-- menu may contain some predefined values, otherwise start with a
 | 
			
		||||
	-- fresh menu.
 | 
			
		||||
	if not menu then
 | 
			
		||||
		menu = {}
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    -- Set the list of items and show the menu.
 | 
			
		||||
    menu.items = cls_t
 | 
			
		||||
    local m = awful.menu(menu)
 | 
			
		||||
    m:show(args)
 | 
			
		||||
	-- Set the list of items and show the menu.
 | 
			
		||||
	menu.items = cls_t
 | 
			
		||||
	local m = awful.menu(menu)
 | 
			
		||||
	m:show(args)
 | 
			
		||||
 | 
			
		||||
    return m
 | 
			
		||||
	return m
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Magnify a client: set it to "float" and resize it.
 | 
			
		||||
function util.magnify_client(c, width_f, height_f)
 | 
			
		||||
    if c and not c.floating then
 | 
			
		||||
        util.magnified_client = c
 | 
			
		||||
        util.mc(c, width_f, height_f)
 | 
			
		||||
    else
 | 
			
		||||
        util.magnified_client = nil
 | 
			
		||||
        c.floating = false
 | 
			
		||||
    end
 | 
			
		||||
	if c and not c.floating then
 | 
			
		||||
		util.magnified_client = c
 | 
			
		||||
		util.mc(c, width_f, height_f)
 | 
			
		||||
	else
 | 
			
		||||
		util.magnified_client = nil
 | 
			
		||||
		c.floating = false
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- https://github.com/lcpz/lain/issues/195
 | 
			
		||||
function util.mc(c, width_f, height_f)
 | 
			
		||||
    c = c or util.magnified_client
 | 
			
		||||
    if not c then return end
 | 
			
		||||
	c = c or util.magnified_client
 | 
			
		||||
	if not c then
 | 
			
		||||
		return
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    c.floating   = true
 | 
			
		||||
    local s      = awful.screen.focused()
 | 
			
		||||
    local mg     = s.workarea
 | 
			
		||||
    local g      = {}
 | 
			
		||||
    local mwfact = width_f or s.selected_tag.master_width_factor or 0.5
 | 
			
		||||
    g.width      = sqrt(mwfact) * mg.width
 | 
			
		||||
    g.height     = sqrt(height_f or mwfact) * mg.height
 | 
			
		||||
    g.x          = mg.x + (mg.width - g.width) / 2
 | 
			
		||||
    g.y          = mg.y + (mg.height - g.height) / 2
 | 
			
		||||
	c.floating = true
 | 
			
		||||
	local s = awful.screen.focused()
 | 
			
		||||
	local mg = s.workarea
 | 
			
		||||
	local g = {}
 | 
			
		||||
	local mwfact = width_f or s.selected_tag.master_width_factor or 0.5
 | 
			
		||||
	g.width = sqrt(mwfact) * mg.width
 | 
			
		||||
	g.height = sqrt(height_f or 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
 | 
			
		||||
	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)
 | 
			
		||||
    direction  = direction or 1
 | 
			
		||||
    local s    = sc or awful.screen.focused()
 | 
			
		||||
    local tags = s.tags
 | 
			
		||||
    local sel  = s.selected_tag
 | 
			
		||||
function util.tag_view_nonempty(direction, sc)
 | 
			
		||||
	direction = direction or 1
 | 
			
		||||
	local s = sc or awful.screen.focused()
 | 
			
		||||
	local tags = s.tags
 | 
			
		||||
	local sel = s.selected_tag
 | 
			
		||||
 | 
			
		||||
    local i = sel.index
 | 
			
		||||
    repeat
 | 
			
		||||
        i = i + direction
 | 
			
		||||
	local i = sel.index
 | 
			
		||||
	repeat
 | 
			
		||||
		i = i + direction
 | 
			
		||||
 | 
			
		||||
        -- Wrap around when we reach one of the bounds
 | 
			
		||||
        if i > #tags then
 | 
			
		||||
            i = i - #tags
 | 
			
		||||
        end
 | 
			
		||||
        if i < 1 then
 | 
			
		||||
            i = i + #tags
 | 
			
		||||
        end
 | 
			
		||||
		-- Wrap around when we reach one of the bounds
 | 
			
		||||
		if i > #tags then
 | 
			
		||||
			i = i - #tags
 | 
			
		||||
		end
 | 
			
		||||
		if i < 1 then
 | 
			
		||||
			i = i + #tags
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
        local t = tags[i]
 | 
			
		||||
		local t = tags[i]
 | 
			
		||||
 | 
			
		||||
        -- Stop when we get back to where we started
 | 
			
		||||
        if t == sel then
 | 
			
		||||
            break
 | 
			
		||||
        end
 | 
			
		||||
		-- Stop when we get back to where we started
 | 
			
		||||
		if t == sel then
 | 
			
		||||
			break
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
        -- If it's The One, view it.
 | 
			
		||||
        if #t:clients() > 0 then
 | 
			
		||||
            t:view_only()
 | 
			
		||||
            return
 | 
			
		||||
        end
 | 
			
		||||
    until false
 | 
			
		||||
		-- If it's The One, view it.
 | 
			
		||||
		if #t:clients() > 0 then
 | 
			
		||||
			t:view_only()
 | 
			
		||||
			return
 | 
			
		||||
		end
 | 
			
		||||
	until false
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- {{{ Dynamic tagging
 | 
			
		||||
 | 
			
		||||
-- Add a new tag
 | 
			
		||||
function util.add_tag(layout)
 | 
			
		||||
    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(), layout = layout or awful.layout.suit.tile }):view_only()
 | 
			
		||||
        end
 | 
			
		||||
    }
 | 
			
		||||
	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(), layout = layout or awful.layout.suit.tile })
 | 
			
		||||
				:view_only()
 | 
			
		||||
		end,
 | 
			
		||||
	})
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Rename current tag
 | 
			
		||||
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
 | 
			
		||||
    }
 | 
			
		||||
	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
 | 
			
		||||
 | 
			
		||||
-- Move current tag
 | 
			
		||||
-- pos in {-1, 1} <-> {previous, next} tag position
 | 
			
		||||
function util.move_tag(pos)
 | 
			
		||||
    local tag = awful.screen.focused().selected_tag
 | 
			
		||||
    if tonumber(pos) <= -1 then
 | 
			
		||||
        awful.tag.move(tag.index - 1, tag)
 | 
			
		||||
    else
 | 
			
		||||
        awful.tag.move(tag.index + 1, tag)
 | 
			
		||||
    end
 | 
			
		||||
	local tag = awful.screen.focused().selected_tag
 | 
			
		||||
	if tonumber(pos) <= -1 then
 | 
			
		||||
		awful.tag.move(tag.index - 1, tag)
 | 
			
		||||
	else
 | 
			
		||||
		awful.tag.move(tag.index + 1, tag)
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Delete current tag
 | 
			
		||||
-- Any rule set on the tag shall be broken
 | 
			
		||||
function util.delete_tag()
 | 
			
		||||
    local t = awful.screen.focused().selected_tag
 | 
			
		||||
    if not t then return end
 | 
			
		||||
    t:delete()
 | 
			
		||||
	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, s, t)
 | 
			
		||||
    local scr = s or awful.screen.focused()
 | 
			
		||||
    local tag = t or scr.selected_tag
 | 
			
		||||
    tag.gap = tag.gap + tonumber(thatmuch)
 | 
			
		||||
    awful.layout.arrange(scr)
 | 
			
		||||
	local scr = s or awful.screen.focused()
 | 
			
		||||
	local tag = t or scr.selected_tag
 | 
			
		||||
	tag.gap = tag.gap + tonumber(thatmuch)
 | 
			
		||||
	awful.layout.arrange(scr)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
return setmetatable(util, { __index = wrequire })
 | 
			
		||||
 
 | 
			
		||||
@@ -15,52 +15,78 @@ local setmetatable = setmetatable
 | 
			
		||||
local markup = { fg = {}, bg = {} }
 | 
			
		||||
 | 
			
		||||
-- Convenience tags
 | 
			
		||||
function markup.bold(text)      return format("<b>%s</b>",         text) end
 | 
			
		||||
function markup.italic(text)    return format("<i>%s</i>",         text) end
 | 
			
		||||
function markup.strike(text)    return format("<s>%s</s>",         text) end
 | 
			
		||||
function markup.underline(text) return format("<u>%s</u>",         text) end
 | 
			
		||||
function markup.monospace(text) return format("<tt>%s</tt>",       text) end
 | 
			
		||||
function markup.big(text)       return format("<big>%s</big>",     text) end
 | 
			
		||||
function markup.small(text)     return format("<small>%s</small>", text) end
 | 
			
		||||
function markup.bold(text)
 | 
			
		||||
	return format('<b>%s</b>', text)
 | 
			
		||||
end
 | 
			
		||||
function markup.italic(text)
 | 
			
		||||
	return format('<i>%s</i>', text)
 | 
			
		||||
end
 | 
			
		||||
function markup.strike(text)
 | 
			
		||||
	return format('<s>%s</s>', text)
 | 
			
		||||
end
 | 
			
		||||
function markup.underline(text)
 | 
			
		||||
	return format('<u>%s</u>', text)
 | 
			
		||||
end
 | 
			
		||||
function markup.monospace(text)
 | 
			
		||||
	return format('<tt>%s</tt>', text)
 | 
			
		||||
end
 | 
			
		||||
function markup.big(text)
 | 
			
		||||
	return format('<big>%s</big>', text)
 | 
			
		||||
end
 | 
			
		||||
function markup.small(text)
 | 
			
		||||
	return format('<small>%s</small>', text)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Set the font
 | 
			
		||||
function markup.font(font, text)
 | 
			
		||||
    return format("<span font='%s'>%s</span>", font, text)
 | 
			
		||||
	return format("<span font='%s'>%s</span>", font, text)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Set the foreground
 | 
			
		||||
function markup.fg.color(color, text)
 | 
			
		||||
    return format("<span foreground='%s'>%s</span>", color, text)
 | 
			
		||||
	return format("<span foreground='%s'>%s</span>", color, text)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Set the background
 | 
			
		||||
function markup.bg.color(color, text)
 | 
			
		||||
    return format("<span background='%s'>%s</span>", color, text)
 | 
			
		||||
	return format("<span background='%s'>%s</span>", color, text)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Set foreground and background
 | 
			
		||||
function markup.color(fg, bg, text)
 | 
			
		||||
    return format("<span foreground='%s' background='%s'>%s</span>", fg, bg, text)
 | 
			
		||||
	return format("<span foreground='%s' background='%s'>%s</span>", fg, bg, text)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Set font and foreground
 | 
			
		||||
function markup.fontfg(font, fg, text)
 | 
			
		||||
    return format("<span font='%s' foreground='%s'>%s</span>", font, fg, text)
 | 
			
		||||
	return format("<span font='%s' foreground='%s'>%s</span>", font, fg, text)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Set font and background
 | 
			
		||||
function markup.fontbg(font, bg, text)
 | 
			
		||||
    return format("<span font='%s' background='%s'>%s</span>", font, bg, text)
 | 
			
		||||
	return format("<span font='%s' background='%s'>%s</span>", font, bg, text)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Set font, foreground and background
 | 
			
		||||
function markup.fontcolor(font, fg, bg, text)
 | 
			
		||||
    return format("<span font='%s' foreground='%s' background='%s'>%s</span>", font, fg, bg, text)
 | 
			
		||||
	return 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 })
 | 
			
		||||
setmetatable(markup.bg, { __call = function(_, ...) return markup.bg.color(...) end })
 | 
			
		||||
setmetatable(markup.fg, {
 | 
			
		||||
	__call = function(_, ...)
 | 
			
		||||
		return markup.fg.color(...)
 | 
			
		||||
	end,
 | 
			
		||||
})
 | 
			
		||||
setmetatable(markup.bg, {
 | 
			
		||||
	__call = function(_, ...)
 | 
			
		||||
		return markup.bg.color(...)
 | 
			
		||||
	end,
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
-- link markup(...) calls to markup.fg.color(...)
 | 
			
		||||
return setmetatable(markup, { __call = function(_, ...) return markup.fg.color(...) end })
 | 
			
		||||
return setmetatable(markup, {
 | 
			
		||||
	__call = function(_, ...)
 | 
			
		||||
		return markup.fg.color(...)
 | 
			
		||||
	end,
 | 
			
		||||
})
 | 
			
		||||
 
 | 
			
		||||
@@ -10,28 +10,30 @@
 | 
			
		||||
-- Menu iterator with Naughty notifications
 | 
			
		||||
-- lain.util.menu_iterator
 | 
			
		||||
 | 
			
		||||
local naughty = require("naughty")
 | 
			
		||||
local helpers = require("lain.helpers")
 | 
			
		||||
local atable  = require("awful.util").table
 | 
			
		||||
local assert  = assert
 | 
			
		||||
local pairs   = pairs
 | 
			
		||||
local naughty = require('naughty')
 | 
			
		||||
local helpers = require('lain.helpers')
 | 
			
		||||
local atable = require('awful.util').table
 | 
			
		||||
local assert = assert
 | 
			
		||||
local pairs = pairs
 | 
			
		||||
local tconcat = table.concat
 | 
			
		||||
local unpack = unpack or table.unpack -- lua 5.1 retro-compatibility
 | 
			
		||||
 | 
			
		||||
local state = { cid = nil }
 | 
			
		||||
 | 
			
		||||
local function naughty_destroy_callback(reason)
 | 
			
		||||
    local closed = naughty.notificationClosedReason
 | 
			
		||||
    if reason == closed.expired or reason == closed.dismissedByUser then
 | 
			
		||||
        local actions = state.index and state.menu[state.index - 1][2]
 | 
			
		||||
        if actions then
 | 
			
		||||
            for _,action in pairs(actions) do
 | 
			
		||||
                -- don't try to call nil callbacks
 | 
			
		||||
                if action then action() end
 | 
			
		||||
            end
 | 
			
		||||
            state.index = nil
 | 
			
		||||
        end
 | 
			
		||||
    end
 | 
			
		||||
	local closed = naughty.notificationClosedReason
 | 
			
		||||
	if reason == closed.expired or reason == closed.dismissedByUser then
 | 
			
		||||
		local actions = state.index and state.menu[state.index - 1][2]
 | 
			
		||||
		if actions then
 | 
			
		||||
			for _, action in pairs(actions) do
 | 
			
		||||
				-- don't try to call nil callbacks
 | 
			
		||||
				if action then
 | 
			
		||||
					action()
 | 
			
		||||
				end
 | 
			
		||||
			end
 | 
			
		||||
			state.index = nil
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Iterates over a menu.
 | 
			
		||||
@@ -41,35 +43,35 @@ end
 | 
			
		||||
-- * timeout: time to wait before confirming the menu selection
 | 
			
		||||
-- * icon:    icon to display in the notification of the chosen label
 | 
			
		||||
local function iterate(menu, timeout, icon)
 | 
			
		||||
    timeout = timeout or 4 -- default timeout for each menu entry
 | 
			
		||||
    icon    = icon or nil  -- icon to display on the menu
 | 
			
		||||
	timeout = timeout or 4 -- default timeout for each menu entry
 | 
			
		||||
	icon = icon or nil -- icon to display on the menu
 | 
			
		||||
 | 
			
		||||
    -- Build the list of choices
 | 
			
		||||
    if not state.index then
 | 
			
		||||
        state.menu = menu
 | 
			
		||||
        state.index = 1
 | 
			
		||||
    end
 | 
			
		||||
	-- Build the list of choices
 | 
			
		||||
	if not state.index then
 | 
			
		||||
		state.menu = menu
 | 
			
		||||
		state.index = 1
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    -- Select one and display the appropriate notification
 | 
			
		||||
    local label
 | 
			
		||||
    local next = state.menu[state.index]
 | 
			
		||||
    state.index = state.index + 1
 | 
			
		||||
	-- Select one and display the appropriate notification
 | 
			
		||||
	local label
 | 
			
		||||
	local next = state.menu[state.index]
 | 
			
		||||
	state.index = state.index + 1
 | 
			
		||||
 | 
			
		||||
    if not next then
 | 
			
		||||
        label = "Cancel"
 | 
			
		||||
        state.index = nil
 | 
			
		||||
    else
 | 
			
		||||
        label, _ = unpack(next)
 | 
			
		||||
    end
 | 
			
		||||
	if not next then
 | 
			
		||||
		label = 'Cancel'
 | 
			
		||||
		state.index = nil
 | 
			
		||||
	else
 | 
			
		||||
		label, _ = unpack(next)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    state.cid = naughty.notify({
 | 
			
		||||
        text        = label,
 | 
			
		||||
        icon        = icon,
 | 
			
		||||
        timeout     = timeout,
 | 
			
		||||
        screen      = mouse.screen,
 | 
			
		||||
        replaces_id = state.cid,
 | 
			
		||||
        destroy     = naughty_destroy_callback
 | 
			
		||||
    }).id
 | 
			
		||||
	state.cid = naughty.notify({
 | 
			
		||||
		text = label,
 | 
			
		||||
		icon = icon,
 | 
			
		||||
		timeout = timeout,
 | 
			
		||||
		screen = mouse.screen,
 | 
			
		||||
		replaces_id = state.cid,
 | 
			
		||||
		destroy = naughty_destroy_callback,
 | 
			
		||||
	}).id
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Generates a menu compatible with the first argument of `iterate` function and
 | 
			
		||||
@@ -95,50 +97,55 @@ end
 | 
			
		||||
-- Output:
 | 
			
		||||
-- * m: menu to be iterated over.
 | 
			
		||||
local function menu(args)
 | 
			
		||||
    local choices       = assert(args.choices or args[1])
 | 
			
		||||
    local name          = assert(args.name or args[2])
 | 
			
		||||
    local selected_cb   = args.selected_cb
 | 
			
		||||
    local rejected_cb   = args.rejected_cb
 | 
			
		||||
    local extra_choices = args.extra_choices or {}
 | 
			
		||||
	local choices = assert(args.choices or args[1])
 | 
			
		||||
	local name = assert(args.name or args[2])
 | 
			
		||||
	local selected_cb = args.selected_cb
 | 
			
		||||
	local rejected_cb = args.rejected_cb
 | 
			
		||||
	local extra_choices = args.extra_choices or {}
 | 
			
		||||
 | 
			
		||||
    local ch_combinations = args.combination == "powerset" and helpers.powerset(choices) or helpers.trivial_partition_set(choices)
 | 
			
		||||
	local ch_combinations = args.combination == 'powerset' and helpers.powerset(choices)
 | 
			
		||||
		or helpers.trivial_partition_set(choices)
 | 
			
		||||
 | 
			
		||||
    for _, c in pairs(extra_choices) do
 | 
			
		||||
        ch_combinations = atable.join(ch_combinations, {{c[1]}})
 | 
			
		||||
    end
 | 
			
		||||
	for _, c in pairs(extra_choices) do
 | 
			
		||||
		ch_combinations = atable.join(ch_combinations, { { c[1] } })
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    local m = {} -- the menu
 | 
			
		||||
	local m = {} -- the menu
 | 
			
		||||
 | 
			
		||||
    for _,c in pairs(ch_combinations) do
 | 
			
		||||
        if #c > 0 then
 | 
			
		||||
            local cbs = {}
 | 
			
		||||
	for _, c in pairs(ch_combinations) do
 | 
			
		||||
		if #c > 0 then
 | 
			
		||||
			local cbs = {}
 | 
			
		||||
 | 
			
		||||
            -- selected choices
 | 
			
		||||
            for _,ch in pairs(c) do
 | 
			
		||||
                if atable.hasitem(choices, ch) then
 | 
			
		||||
                    cbs[#cbs + 1] = selected_cb and function() selected_cb(ch) end or nil
 | 
			
		||||
                end
 | 
			
		||||
            end
 | 
			
		||||
			-- selected choices
 | 
			
		||||
			for _, ch in pairs(c) do
 | 
			
		||||
				if atable.hasitem(choices, ch) then
 | 
			
		||||
					cbs[#cbs + 1] = selected_cb and function()
 | 
			
		||||
						selected_cb(ch)
 | 
			
		||||
					end or nil
 | 
			
		||||
				end
 | 
			
		||||
			end
 | 
			
		||||
 | 
			
		||||
            -- rejected choices
 | 
			
		||||
            for _,ch in pairs(choices) do
 | 
			
		||||
                if not atable.hasitem(c, ch) and atable.hasitem(choices, ch) then
 | 
			
		||||
                    cbs[#cbs + 1] = rejected_cb and function() rejected_cb(ch) end or nil
 | 
			
		||||
                end
 | 
			
		||||
            end
 | 
			
		||||
			-- rejected choices
 | 
			
		||||
			for _, ch in pairs(choices) do
 | 
			
		||||
				if not atable.hasitem(c, ch) and atable.hasitem(choices, ch) then
 | 
			
		||||
					cbs[#cbs + 1] = rejected_cb and function()
 | 
			
		||||
						rejected_cb(ch)
 | 
			
		||||
					end or nil
 | 
			
		||||
				end
 | 
			
		||||
			end
 | 
			
		||||
 | 
			
		||||
            -- add user extra choices (like the choice "None" for example)
 | 
			
		||||
            for _,x in pairs(extra_choices) do
 | 
			
		||||
                if x[1] == c[1] then
 | 
			
		||||
                    cbs[#cbs + 1] = x[2]
 | 
			
		||||
                end
 | 
			
		||||
            end
 | 
			
		||||
			-- add user extra choices (like the choice "None" for example)
 | 
			
		||||
			for _, x in pairs(extra_choices) do
 | 
			
		||||
				if x[1] == c[1] then
 | 
			
		||||
					cbs[#cbs + 1] = x[2]
 | 
			
		||||
				end
 | 
			
		||||
			end
 | 
			
		||||
 | 
			
		||||
            m[#m + 1] = { name .. ": " .. tconcat(c, " + "), cbs }
 | 
			
		||||
        end
 | 
			
		||||
    end
 | 
			
		||||
			m[#m + 1] = { name .. ': ' .. tconcat(c, ' + '), cbs }
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    return m
 | 
			
		||||
	return m
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
return { iterate = iterate, menu = menu }
 | 
			
		||||
 
 | 
			
		||||
@@ -6,12 +6,12 @@
 | 
			
		||||
 | 
			
		||||
--]]
 | 
			
		||||
 | 
			
		||||
local awful        = require("awful")
 | 
			
		||||
local capi         = { client = client }
 | 
			
		||||
local math         = math
 | 
			
		||||
local string       = string
 | 
			
		||||
local pairs        = pairs
 | 
			
		||||
local screen       = screen
 | 
			
		||||
local awful = require('awful')
 | 
			
		||||
local capi = { client = client }
 | 
			
		||||
local math = math
 | 
			
		||||
local string = string
 | 
			
		||||
local pairs = pairs
 | 
			
		||||
local screen = screen
 | 
			
		||||
local setmetatable = setmetatable
 | 
			
		||||
 | 
			
		||||
-- Quake-like Dropdown application spawn
 | 
			
		||||
@@ -22,158 +22,182 @@ local quake = {}
 | 
			
		||||
-- run into problems with focus.
 | 
			
		||||
 | 
			
		||||
function quake:display()
 | 
			
		||||
    if self.followtag then self.screen = awful.screen.focused() end
 | 
			
		||||
	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)
 | 
			
		||||
    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
 | 
			
		||||
	-- 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)
 | 
			
		||||
	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 and not self.visible then
 | 
			
		||||
		return
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    if not client then
 | 
			
		||||
        -- The client does not exist, we spawn it
 | 
			
		||||
        local 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
 | 
			
		||||
	if not client then
 | 
			
		||||
		-- The client does not exist, we spawn it
 | 
			
		||||
		local 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
 | 
			
		||||
    local maximized = client.maximized
 | 
			
		||||
    local fullscreen = client.fullscreen
 | 
			
		||||
    client:geometry(self.geometry[self.screen.index] or self:compute_size())
 | 
			
		||||
	-- Set geometry
 | 
			
		||||
	client.floating = true
 | 
			
		||||
	client.border_width = self.border
 | 
			
		||||
	client.size_hints_honor = false
 | 
			
		||||
	local maximized = client.maximized
 | 
			
		||||
	local fullscreen = client.fullscreen
 | 
			
		||||
	client:geometry(self.geometry[self.screen.index] or self:compute_size())
 | 
			
		||||
 | 
			
		||||
    -- Set not sticky and on top
 | 
			
		||||
    client.sticky = false
 | 
			
		||||
    client.ontop = true
 | 
			
		||||
    client.above = true
 | 
			
		||||
    client.skip_taskbar = true
 | 
			
		||||
	-- 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
 | 
			
		||||
	-- Additional user settings
 | 
			
		||||
	if self.settings then
 | 
			
		||||
		self.settings(client)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    -- Toggle display
 | 
			
		||||
    if self.visible then
 | 
			
		||||
        client.hidden = false
 | 
			
		||||
        client.maximized = self.maximized
 | 
			
		||||
        client.fullscreen = self.fullscreen
 | 
			
		||||
        client:raise()
 | 
			
		||||
        self.last_tag = self.screen.selected_tag
 | 
			
		||||
        client:tags({self.screen.selected_tag})
 | 
			
		||||
        capi.client.focus = client
 | 
			
		||||
    else
 | 
			
		||||
        self.maximized = maximized
 | 
			
		||||
        self.fullscreen = fullscreen
 | 
			
		||||
        client.maximized = false
 | 
			
		||||
        client.fullscreen = false
 | 
			
		||||
        client.hidden = true
 | 
			
		||||
        local ctags = client:tags()
 | 
			
		||||
        for j, _ in pairs(ctags) do
 | 
			
		||||
            ctags[j] = nil
 | 
			
		||||
        end
 | 
			
		||||
        client:tags(ctags)
 | 
			
		||||
    end
 | 
			
		||||
	-- Toggle display
 | 
			
		||||
	if self.visible then
 | 
			
		||||
		client.hidden = false
 | 
			
		||||
		client.maximized = self.maximized
 | 
			
		||||
		client.fullscreen = self.fullscreen
 | 
			
		||||
		client:raise()
 | 
			
		||||
		self.last_tag = self.screen.selected_tag
 | 
			
		||||
		client:tags({ self.screen.selected_tag })
 | 
			
		||||
		capi.client.focus = client
 | 
			
		||||
	else
 | 
			
		||||
		self.maximized = maximized
 | 
			
		||||
		self.fullscreen = fullscreen
 | 
			
		||||
		client.maximized = false
 | 
			
		||||
		client.fullscreen = false
 | 
			
		||||
		client.hidden = true
 | 
			
		||||
		local ctags = client:tags()
 | 
			
		||||
		for j, _ in pairs(ctags) do
 | 
			
		||||
			ctags[j] = nil
 | 
			
		||||
		end
 | 
			
		||||
		client:tags(ctags)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    return client
 | 
			
		||||
	return client
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function quake:compute_size()
 | 
			
		||||
    -- skip if we already have a geometry for this screen
 | 
			
		||||
    if not self.geometry[self.screen.index] then
 | 
			
		||||
        local geom
 | 
			
		||||
        if not self.overlap then
 | 
			
		||||
            geom = screen[self.screen.index].workarea
 | 
			
		||||
        else
 | 
			
		||||
            geom = screen[self.screen.index].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.index] = { x = x, y = y, width = width, height = height }
 | 
			
		||||
    end
 | 
			
		||||
    return self.geometry[self.screen.index]
 | 
			
		||||
	-- skip if we already have a geometry for this screen
 | 
			
		||||
	if not self.geometry[self.screen.index] then
 | 
			
		||||
		local geom
 | 
			
		||||
		if not self.overlap then
 | 
			
		||||
			geom = screen[self.screen.index].workarea
 | 
			
		||||
		else
 | 
			
		||||
			geom = screen[self.screen.index].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.index] = { x = x, y = y, width = width, height = height }
 | 
			
		||||
	end
 | 
			
		||||
	return self.geometry[self.screen.index]
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function quake:toggle()
 | 
			
		||||
     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
 | 
			
		||||
         local c=self:display()
 | 
			
		||||
         if c then
 | 
			
		||||
            c:move_to_tag(current_tag)
 | 
			
		||||
        end
 | 
			
		||||
     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
 | 
			
		||||
		local c = self:display()
 | 
			
		||||
		if c then
 | 
			
		||||
			c:move_to_tag(current_tag)
 | 
			
		||||
		end
 | 
			
		||||
	else
 | 
			
		||||
		self.visible = not self.visible
 | 
			
		||||
		self:display()
 | 
			
		||||
	end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function quake.new(conf)
 | 
			
		||||
    conf = conf or {}
 | 
			
		||||
	conf = conf 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
 | 
			
		||||
	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
 | 
			
		||||
	-- 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
 | 
			
		||||
 | 
			
		||||
    conf.maximized = false
 | 
			
		||||
    conf.fullscreen = false
 | 
			
		||||
	conf.maximized = false
 | 
			
		||||
	conf.fullscreen = false
 | 
			
		||||
 | 
			
		||||
    local dropdown = setmetatable(conf, { __index = quake })
 | 
			
		||||
	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)
 | 
			
		||||
	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)
 | 
			
		||||
 | 
			
		||||
    return dropdown
 | 
			
		||||
	return dropdown
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
return setmetatable(quake, { __call = function(_, ...) return quake.new(...) end })
 | 
			
		||||
return setmetatable(quake, {
 | 
			
		||||
	__call = function(_, ...)
 | 
			
		||||
		return quake.new(...)
 | 
			
		||||
	end,
 | 
			
		||||
})
 | 
			
		||||
 
 | 
			
		||||
@@ -6,9 +6,9 @@
 | 
			
		||||
 | 
			
		||||
--]]
 | 
			
		||||
 | 
			
		||||
local wibox = require("wibox")
 | 
			
		||||
local gears = require("gears")
 | 
			
		||||
local beautiful = require("beautiful")
 | 
			
		||||
local wibox = require('wibox')
 | 
			
		||||
local gears = require('gears')
 | 
			
		||||
local beautiful = require('beautiful')
 | 
			
		||||
 | 
			
		||||
-- Lain Cairo separators util submodule
 | 
			
		||||
-- lain.util.separators
 | 
			
		||||
@@ -18,99 +18,99 @@ local separators = { height = beautiful.separators_height or 0, width = beautifu
 | 
			
		||||
 | 
			
		||||
-- Right
 | 
			
		||||
function separators.arrow_right(col1, col2)
 | 
			
		||||
    local widget = wibox.widget.base.make_widget()
 | 
			
		||||
    widget.col1 = col1
 | 
			
		||||
    widget.col2 = col2
 | 
			
		||||
	local widget = wibox.widget.base.make_widget()
 | 
			
		||||
	widget.col1 = col1
 | 
			
		||||
	widget.col2 = col2
 | 
			
		||||
 | 
			
		||||
    widget.fit = function(_, _, _)
 | 
			
		||||
        return separators.width, separators.height
 | 
			
		||||
    end
 | 
			
		||||
	widget.fit = function(_, _, _)
 | 
			
		||||
		return separators.width, separators.height
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    widget.update = function(_, _)
 | 
			
		||||
        widget.col1 = col1
 | 
			
		||||
        widget.col2 = col2
 | 
			
		||||
        widget:emit_signal("widget::redraw_needed")
 | 
			
		||||
    end
 | 
			
		||||
	widget.update = function(_, _)
 | 
			
		||||
		widget.col1 = col1
 | 
			
		||||
		widget.col2 = col2
 | 
			
		||||
		widget:emit_signal('widget::redraw_needed')
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    widget.draw = function(_, _, cr, width, height)
 | 
			
		||||
        if widget.col2 ~= "alpha" then
 | 
			
		||||
            cr:set_source_rgba(gears.color.parse_color(widget.col2))
 | 
			
		||||
            cr:new_path()
 | 
			
		||||
            cr:move_to(0, 0)
 | 
			
		||||
            cr:line_to(width, height/2)
 | 
			
		||||
            cr:line_to(width, 0)
 | 
			
		||||
            cr:close_path()
 | 
			
		||||
            cr:fill()
 | 
			
		||||
	widget.draw = function(_, _, cr, width, height)
 | 
			
		||||
		if widget.col2 ~= 'alpha' then
 | 
			
		||||
			cr:set_source_rgba(gears.color.parse_color(widget.col2))
 | 
			
		||||
			cr:new_path()
 | 
			
		||||
			cr:move_to(0, 0)
 | 
			
		||||
			cr:line_to(width, height / 2)
 | 
			
		||||
			cr:line_to(width, 0)
 | 
			
		||||
			cr:close_path()
 | 
			
		||||
			cr:fill()
 | 
			
		||||
 | 
			
		||||
            cr:new_path()
 | 
			
		||||
            cr:move_to(0, height)
 | 
			
		||||
            cr:line_to(width, height/2)
 | 
			
		||||
            cr:line_to(width, height)
 | 
			
		||||
            cr:close_path()
 | 
			
		||||
            cr:fill()
 | 
			
		||||
        end
 | 
			
		||||
			cr:new_path()
 | 
			
		||||
			cr:move_to(0, height)
 | 
			
		||||
			cr:line_to(width, height / 2)
 | 
			
		||||
			cr:line_to(width, height)
 | 
			
		||||
			cr:close_path()
 | 
			
		||||
			cr:fill()
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
        if widget.col1 ~= "alpha" then
 | 
			
		||||
            cr:set_source_rgba(gears.color.parse_color(widget.col1))
 | 
			
		||||
            cr:new_path()
 | 
			
		||||
            cr:move_to(0, 0)
 | 
			
		||||
            cr:line_to(width, height/2)
 | 
			
		||||
            cr:line_to(0, height)
 | 
			
		||||
            cr:close_path()
 | 
			
		||||
            cr:fill()
 | 
			
		||||
        end
 | 
			
		||||
   end
 | 
			
		||||
		if widget.col1 ~= 'alpha' then
 | 
			
		||||
			cr:set_source_rgba(gears.color.parse_color(widget.col1))
 | 
			
		||||
			cr:new_path()
 | 
			
		||||
			cr:move_to(0, 0)
 | 
			
		||||
			cr:line_to(width, height / 2)
 | 
			
		||||
			cr:line_to(0, height)
 | 
			
		||||
			cr:close_path()
 | 
			
		||||
			cr:fill()
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
   return widget
 | 
			
		||||
	return widget
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- Left
 | 
			
		||||
function separators.arrow_left(col1, col2)
 | 
			
		||||
    local widget = wibox.widget.base.make_widget()
 | 
			
		||||
    widget.col1 = col1
 | 
			
		||||
    widget.col2 = col2
 | 
			
		||||
	local widget = wibox.widget.base.make_widget()
 | 
			
		||||
	widget.col1 = col1
 | 
			
		||||
	widget.col2 = col2
 | 
			
		||||
 | 
			
		||||
    widget.fit = function(_,  _, _)
 | 
			
		||||
        return separators.width, separators.height
 | 
			
		||||
    end
 | 
			
		||||
	widget.fit = function(_, _, _)
 | 
			
		||||
		return separators.width, separators.height
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    widget.update = function(c1, c2)
 | 
			
		||||
        widget.col1 = c1
 | 
			
		||||
        widget.col2 = c2
 | 
			
		||||
        widget:emit_signal("widget::redraw_needed")
 | 
			
		||||
    end
 | 
			
		||||
	widget.update = function(c1, c2)
 | 
			
		||||
		widget.col1 = c1
 | 
			
		||||
		widget.col2 = c2
 | 
			
		||||
		widget:emit_signal('widget::redraw_needed')
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    widget.draw = function(_, _, cr, width, height)
 | 
			
		||||
        if widget.col1 ~= "alpha" then
 | 
			
		||||
            cr:set_source_rgba(gears.color.parse_color(widget.col1))
 | 
			
		||||
            cr:new_path()
 | 
			
		||||
            cr:move_to(width, 0)
 | 
			
		||||
            cr:line_to(0, height/2)
 | 
			
		||||
            cr:line_to(0, 0)
 | 
			
		||||
            cr:close_path()
 | 
			
		||||
            cr:fill()
 | 
			
		||||
	widget.draw = function(_, _, cr, width, height)
 | 
			
		||||
		if widget.col1 ~= 'alpha' then
 | 
			
		||||
			cr:set_source_rgba(gears.color.parse_color(widget.col1))
 | 
			
		||||
			cr:new_path()
 | 
			
		||||
			cr:move_to(width, 0)
 | 
			
		||||
			cr:line_to(0, height / 2)
 | 
			
		||||
			cr:line_to(0, 0)
 | 
			
		||||
			cr:close_path()
 | 
			
		||||
			cr:fill()
 | 
			
		||||
 | 
			
		||||
            cr:new_path()
 | 
			
		||||
            cr:move_to(width, height)
 | 
			
		||||
            cr:line_to(0, height/2)
 | 
			
		||||
            cr:line_to(0, height)
 | 
			
		||||
            cr:close_path()
 | 
			
		||||
            cr:fill()
 | 
			
		||||
        end
 | 
			
		||||
			cr:new_path()
 | 
			
		||||
			cr:move_to(width, height)
 | 
			
		||||
			cr:line_to(0, height / 2)
 | 
			
		||||
			cr:line_to(0, height)
 | 
			
		||||
			cr:close_path()
 | 
			
		||||
			cr:fill()
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
        if widget.col2 ~= "alpha" then
 | 
			
		||||
            cr:new_path()
 | 
			
		||||
            cr:move_to(width, 0)
 | 
			
		||||
            cr:line_to(0, height/2)
 | 
			
		||||
            cr:line_to(width, height)
 | 
			
		||||
            cr:close_path()
 | 
			
		||||
		if widget.col2 ~= 'alpha' then
 | 
			
		||||
			cr:new_path()
 | 
			
		||||
			cr:move_to(width, 0)
 | 
			
		||||
			cr:line_to(0, height / 2)
 | 
			
		||||
			cr:line_to(width, height)
 | 
			
		||||
			cr:close_path()
 | 
			
		||||
 | 
			
		||||
            cr:set_source_rgba(gears.color.parse_color(widget.col2))
 | 
			
		||||
            cr:fill()
 | 
			
		||||
        end
 | 
			
		||||
   end
 | 
			
		||||
			cr:set_source_rgba(gears.color.parse_color(widget.col2))
 | 
			
		||||
			cr:fill()
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
   return widget
 | 
			
		||||
	return widget
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- ]]
 | 
			
		||||
 
 | 
			
		||||
@@ -6,49 +6,52 @@
 | 
			
		||||
 | 
			
		||||
--]]
 | 
			
		||||
 | 
			
		||||
local helpers = require("lain.helpers")
 | 
			
		||||
local shell   = require("awful.util").shell
 | 
			
		||||
local wibox   = require("wibox")
 | 
			
		||||
local string  = string
 | 
			
		||||
local helpers = require('lain.helpers')
 | 
			
		||||
local shell = require('awful.util').shell
 | 
			
		||||
local wibox = require('wibox')
 | 
			
		||||
local string = string
 | 
			
		||||
 | 
			
		||||
-- ALSA volume
 | 
			
		||||
-- lain.widget.alsa
 | 
			
		||||
 | 
			
		||||
local function factory(args)
 | 
			
		||||
    args           = args or {}
 | 
			
		||||
    local alsa     = { widget = args.widget or wibox.widget.textbox() }
 | 
			
		||||
    local timeout  = args.timeout or 5
 | 
			
		||||
    local settings = args.settings or function() end
 | 
			
		||||
	args = args or {}
 | 
			
		||||
	local alsa = { widget = args.widget or wibox.widget.textbox() }
 | 
			
		||||
	local timeout = args.timeout or 5
 | 
			
		||||
	local settings = args.settings or function() end
 | 
			
		||||
 | 
			
		||||
    alsa.cmd           = args.cmd or "amixer"
 | 
			
		||||
    alsa.channel       = args.channel or "Master"
 | 
			
		||||
    alsa.togglechannel = args.togglechannel
 | 
			
		||||
	alsa.cmd = args.cmd or 'amixer'
 | 
			
		||||
	alsa.channel = args.channel or 'Master'
 | 
			
		||||
	alsa.togglechannel = args.togglechannel
 | 
			
		||||
 | 
			
		||||
    local format_cmd = string.format("%s get %s", alsa.cmd, alsa.channel)
 | 
			
		||||
	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
 | 
			
		||||
	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 = {}
 | 
			
		||||
	alsa.last = {}
 | 
			
		||||
 | 
			
		||||
    function alsa.update()
 | 
			
		||||
        helpers.async(format_cmd, function(mixer)
 | 
			
		||||
            local l,s = string.match(mixer, "([%d]+)%%.*%[([%l]*)")
 | 
			
		||||
            l = tonumber(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
 | 
			
		||||
	function alsa.update()
 | 
			
		||||
		helpers.async(format_cmd, function(mixer)
 | 
			
		||||
			local l, s = string.match(mixer, '([%d]+)%%.*%[([%l]*)')
 | 
			
		||||
			l = tonumber(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
 | 
			
		||||
 | 
			
		||||
    helpers.newtimer(string.format("alsa-%s-%s", alsa.cmd, alsa.channel), timeout, alsa.update)
 | 
			
		||||
	helpers.newtimer(string.format('alsa-%s-%s', alsa.cmd, alsa.channel), timeout, alsa.update)
 | 
			
		||||
 | 
			
		||||
    return alsa
 | 
			
		||||
	return alsa
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
return factory
 | 
			
		||||
 
 | 
			
		||||
@@ -6,161 +6,167 @@
 | 
			
		||||
 | 
			
		||||
--]]
 | 
			
		||||
 | 
			
		||||
local helpers  = require("lain.helpers")
 | 
			
		||||
local awful    = require("awful")
 | 
			
		||||
local naughty  = require("naughty")
 | 
			
		||||
local wibox    = require("wibox")
 | 
			
		||||
local math     = math
 | 
			
		||||
local string   = string
 | 
			
		||||
local type     = type
 | 
			
		||||
local helpers = require('lain.helpers')
 | 
			
		||||
local awful = require('awful')
 | 
			
		||||
local naughty = require('naughty')
 | 
			
		||||
local wibox = require('wibox')
 | 
			
		||||
local math = math
 | 
			
		||||
local string = string
 | 
			
		||||
local type = type
 | 
			
		||||
local tonumber = tonumber
 | 
			
		||||
 | 
			
		||||
-- ALSA volume bar
 | 
			
		||||
-- lain.widget.alsabar
 | 
			
		||||
 | 
			
		||||
local function factory(args)
 | 
			
		||||
    local alsabar = {
 | 
			
		||||
        colors = {
 | 
			
		||||
            background = "#000000",
 | 
			
		||||
            mute       = "#EB8F8F",
 | 
			
		||||
            unmute     = "#A4CE8A"
 | 
			
		||||
        },
 | 
			
		||||
	local alsabar = {
 | 
			
		||||
		colors = {
 | 
			
		||||
			background = '#000000',
 | 
			
		||||
			mute = '#EB8F8F',
 | 
			
		||||
			unmute = '#A4CE8A',
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
        _current_level = 0,
 | 
			
		||||
        _playback      = "off"
 | 
			
		||||
    }
 | 
			
		||||
		_current_level = 0,
 | 
			
		||||
		_playback = 'off',
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    args             = args or {}
 | 
			
		||||
	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.height or 1
 | 
			
		||||
    local margins    = args.margins or 1
 | 
			
		||||
    local ticks      = args.ticks or false
 | 
			
		||||
    local ticks_size = args.ticks_size or 7
 | 
			
		||||
    local tick       = args.tick or "|"
 | 
			
		||||
    local tick_pre   = args.tick_pre or "["
 | 
			
		||||
    local tick_post  = args.tick_post or "]"
 | 
			
		||||
    local tick_none  = args.tick_none or " "
 | 
			
		||||
	local timeout = args.timeout or 5
 | 
			
		||||
	local settings = args.settings or function() end
 | 
			
		||||
	local width = args.width or 63
 | 
			
		||||
	local height = args.height or 1
 | 
			
		||||
	local margins = args.margins or 1
 | 
			
		||||
	local ticks = args.ticks or false
 | 
			
		||||
	local ticks_size = args.ticks_size or 7
 | 
			
		||||
	local tick = args.tick or '|'
 | 
			
		||||
	local tick_pre = args.tick_pre or '['
 | 
			
		||||
	local tick_post = args.tick_post or ']'
 | 
			
		||||
	local tick_none = args.tick_none or ' '
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
	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 = { font = "Monospace 10" }
 | 
			
		||||
    end
 | 
			
		||||
	if not alsabar.notification_preset then
 | 
			
		||||
		alsabar.notification_preset = { font = 'Monospace 10' }
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    local format_cmd = string.format("%s get %s", alsabar.cmd, alsabar.channel)
 | 
			
		||||
	local format_cmd = string.format('%s get %s', alsabar.cmd, alsabar.channel)
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
	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 {
 | 
			
		||||
        color            = alsabar.colors.unmute,
 | 
			
		||||
        background_color = alsabar.colors.background,
 | 
			
		||||
        forced_height    = height,
 | 
			
		||||
        forced_width     = width,
 | 
			
		||||
        margins          = margins,
 | 
			
		||||
        paddings         = margins,
 | 
			
		||||
        ticks            = ticks,
 | 
			
		||||
        ticks_size       = ticks_size,
 | 
			
		||||
        widget           = wibox.widget.progressbar
 | 
			
		||||
    }
 | 
			
		||||
	alsabar.bar = wibox.widget({
 | 
			
		||||
		color = alsabar.colors.unmute,
 | 
			
		||||
		background_color = alsabar.colors.background,
 | 
			
		||||
		forced_height = height,
 | 
			
		||||
		forced_width = width,
 | 
			
		||||
		margins = margins,
 | 
			
		||||
		paddings = margins,
 | 
			
		||||
		ticks = ticks,
 | 
			
		||||
		ticks_size = ticks_size,
 | 
			
		||||
		widget = wibox.widget.progressbar,
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
    alsabar.tooltip = awful.tooltip({ objects = { alsabar.bar } })
 | 
			
		||||
	alsabar.tooltip = awful.tooltip({ objects = { alsabar.bar } })
 | 
			
		||||
 | 
			
		||||
    function alsabar.update(callback)
 | 
			
		||||
        helpers.async(format_cmd, function(mixer)
 | 
			
		||||
            local vol, playback = string.match(mixer, "([%d]+)%%.*%[([%l]*)")
 | 
			
		||||
	function alsabar.update(callback)
 | 
			
		||||
		helpers.async(format_cmd, function(mixer)
 | 
			
		||||
			local vol, playback = string.match(mixer, '([%d]+)%%.*%[([%l]*)')
 | 
			
		||||
 | 
			
		||||
            if not vol or not playback then return end
 | 
			
		||||
			if not vol or not playback then
 | 
			
		||||
				return
 | 
			
		||||
			end
 | 
			
		||||
 | 
			
		||||
            if vol ~= alsabar._current_level or playback ~= alsabar._playback then
 | 
			
		||||
                alsabar._current_level = tonumber(vol)
 | 
			
		||||
                alsabar.bar:set_value(alsabar._current_level / 100)
 | 
			
		||||
                if alsabar._current_level == 0 or playback == "off" then
 | 
			
		||||
                    alsabar._playback = playback
 | 
			
		||||
                    alsabar.tooltip:set_text("[Muted]")
 | 
			
		||||
                    alsabar.bar.color = alsabar.colors.mute
 | 
			
		||||
                else
 | 
			
		||||
                    alsabar._playback = "on"
 | 
			
		||||
                    alsabar.tooltip:set_text(string.format("%s: %s", alsabar.channel, vol))
 | 
			
		||||
                    alsabar.bar.color = alsabar.colors.unmute
 | 
			
		||||
                end
 | 
			
		||||
			if vol ~= alsabar._current_level or playback ~= alsabar._playback then
 | 
			
		||||
				alsabar._current_level = tonumber(vol)
 | 
			
		||||
				alsabar.bar:set_value(alsabar._current_level / 100)
 | 
			
		||||
				if alsabar._current_level == 0 or playback == 'off' then
 | 
			
		||||
					alsabar._playback = playback
 | 
			
		||||
					alsabar.tooltip:set_text('[Muted]')
 | 
			
		||||
					alsabar.bar.color = alsabar.colors.mute
 | 
			
		||||
				else
 | 
			
		||||
					alsabar._playback = 'on'
 | 
			
		||||
					alsabar.tooltip:set_text(string.format('%s: %s', alsabar.channel, vol))
 | 
			
		||||
					alsabar.bar.color = alsabar.colors.unmute
 | 
			
		||||
				end
 | 
			
		||||
 | 
			
		||||
                volume_now = {
 | 
			
		||||
                    level  = alsabar._current_level,
 | 
			
		||||
                    status = alsabar._playback
 | 
			
		||||
                }
 | 
			
		||||
				volume_now = {
 | 
			
		||||
					level = alsabar._current_level,
 | 
			
		||||
					status = alsabar._playback,
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
                settings()
 | 
			
		||||
				settings()
 | 
			
		||||
 | 
			
		||||
                if type(callback) == "function" then callback() end
 | 
			
		||||
            end
 | 
			
		||||
        end)
 | 
			
		||||
    end
 | 
			
		||||
				if type(callback) == 'function' then
 | 
			
		||||
					callback()
 | 
			
		||||
				end
 | 
			
		||||
			end
 | 
			
		||||
		end)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    function alsabar.notify()
 | 
			
		||||
        alsabar.update(function()
 | 
			
		||||
            local preset = alsabar.notification_preset
 | 
			
		||||
	function alsabar.notify()
 | 
			
		||||
		alsabar.update(function()
 | 
			
		||||
			local preset = alsabar.notification_preset
 | 
			
		||||
 | 
			
		||||
            preset.title = string.format("%s - %s%%", alsabar.channel, alsabar._current_level)
 | 
			
		||||
			preset.title = string.format('%s - %s%%', alsabar.channel, alsabar._current_level)
 | 
			
		||||
 | 
			
		||||
            if alsabar._playback == "off" then
 | 
			
		||||
                preset.title = preset.title .. " Muted"
 | 
			
		||||
            end
 | 
			
		||||
			if alsabar._playback == 'off' then
 | 
			
		||||
				preset.title = preset.title .. ' Muted'
 | 
			
		||||
			end
 | 
			
		||||
 | 
			
		||||
            -- tot is the maximum number of ticks to display in the notification
 | 
			
		||||
            local tot = alsabar.notification_preset.max_ticks
 | 
			
		||||
			-- tot is the maximum number of ticks to display in the notification
 | 
			
		||||
			local tot = alsabar.notification_preset.max_ticks
 | 
			
		||||
 | 
			
		||||
            if not tot then
 | 
			
		||||
                local wib = awful.screen.focused().mywibox
 | 
			
		||||
                -- if we can grab mywibox, tot is defined as its height if
 | 
			
		||||
                -- horizontal, or width otherwise
 | 
			
		||||
                if wib then
 | 
			
		||||
                    if wib.position == "left" or wib.position == "right" then
 | 
			
		||||
                        tot = wib.width
 | 
			
		||||
                    else
 | 
			
		||||
                        tot = wib.height
 | 
			
		||||
                    end
 | 
			
		||||
                -- fallback: default horizontal wibox height
 | 
			
		||||
                else
 | 
			
		||||
                    tot = 20
 | 
			
		||||
                end
 | 
			
		||||
            end
 | 
			
		||||
			if not tot then
 | 
			
		||||
				local wib = awful.screen.focused().mywibox
 | 
			
		||||
				-- if we can grab mywibox, tot is defined as its height if
 | 
			
		||||
				-- horizontal, or width otherwise
 | 
			
		||||
				if wib then
 | 
			
		||||
					if wib.position == 'left' or wib.position == 'right' then
 | 
			
		||||
						tot = wib.width
 | 
			
		||||
					else
 | 
			
		||||
						tot = wib.height
 | 
			
		||||
					end
 | 
			
		||||
				-- fallback: default horizontal wibox height
 | 
			
		||||
				else
 | 
			
		||||
					tot = 20
 | 
			
		||||
				end
 | 
			
		||||
			end
 | 
			
		||||
 | 
			
		||||
            local int = math.modf((alsabar._current_level / 100) * tot)
 | 
			
		||||
            preset.text = string.format(
 | 
			
		||||
                "%s%s%s%s",
 | 
			
		||||
                tick_pre,
 | 
			
		||||
                string.rep(tick, int),
 | 
			
		||||
                string.rep(tick_none, tot - int),
 | 
			
		||||
                tick_post
 | 
			
		||||
            )
 | 
			
		||||
			local int = math.modf((alsabar._current_level / 100) * tot)
 | 
			
		||||
			preset.text =
 | 
			
		||||
				string.format('%s%s%s%s', tick_pre, string.rep(tick, int), string.rep(tick_none, tot - int), tick_post)
 | 
			
		||||
 | 
			
		||||
            if alsabar.followtag then preset.screen = awful.screen.focused() end
 | 
			
		||||
			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
 | 
			
		||||
			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
 | 
			
		||||
 | 
			
		||||
    helpers.newtimer(string.format("alsabar-%s-%s", alsabar.cmd, alsabar.channel), timeout, alsabar.update)
 | 
			
		||||
	helpers.newtimer(string.format('alsabar-%s-%s', alsabar.cmd, alsabar.channel), timeout, alsabar.update)
 | 
			
		||||
 | 
			
		||||
    return alsabar
 | 
			
		||||
	return alsabar
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
return factory
 | 
			
		||||
 
 | 
			
		||||
@@ -6,231 +6,232 @@
 | 
			
		||||
 | 
			
		||||
--]]
 | 
			
		||||
 | 
			
		||||
local helpers  = require("lain.helpers")
 | 
			
		||||
local fs       = require("gears.filesystem")
 | 
			
		||||
local naughty  = require("naughty")
 | 
			
		||||
local wibox    = require("wibox")
 | 
			
		||||
local math     = math
 | 
			
		||||
local string   = string
 | 
			
		||||
local ipairs   = ipairs
 | 
			
		||||
local helpers = require('lain.helpers')
 | 
			
		||||
local fs = require('gears.filesystem')
 | 
			
		||||
local naughty = require('naughty')
 | 
			
		||||
local wibox = require('wibox')
 | 
			
		||||
local math = math
 | 
			
		||||
local string = string
 | 
			
		||||
local ipairs = ipairs
 | 
			
		||||
local tonumber = tonumber
 | 
			
		||||
 | 
			
		||||
-- Battery infos
 | 
			
		||||
-- lain.widget.bat
 | 
			
		||||
 | 
			
		||||
local function factory(args)
 | 
			
		||||
    local pspath = args.pspath or "/sys/class/power_supply/"
 | 
			
		||||
	local pspath = args.pspath or '/sys/class/power_supply/'
 | 
			
		||||
 | 
			
		||||
    if not fs.is_dir(pspath) then
 | 
			
		||||
        naughty.notify { text = "lain.widget.bat: invalid power supply path", timeout = 0 }
 | 
			
		||||
        return
 | 
			
		||||
    end
 | 
			
		||||
	if not fs.is_dir(pspath) then
 | 
			
		||||
		naughty.notify({ text = 'lain.widget.bat: invalid power supply path', timeout = 0 })
 | 
			
		||||
		return
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    args              = args or {}
 | 
			
		||||
	args = args or {}
 | 
			
		||||
 | 
			
		||||
    local bat         = { widget = args.widget or wibox.widget.textbox() }
 | 
			
		||||
    local timeout     = args.timeout or 30
 | 
			
		||||
    local notify      = args.notify or "on"
 | 
			
		||||
    local full_notify = args.full_notify or notify
 | 
			
		||||
    local n_perc      = args.n_perc or { 5, 15 }
 | 
			
		||||
    local batteries   = args.batteries or (args.battery and {args.battery}) or {}
 | 
			
		||||
    local ac          = args.ac or "AC0"
 | 
			
		||||
    local settings    = args.settings or function() end
 | 
			
		||||
	local bat = { widget = args.widget or wibox.widget.textbox() }
 | 
			
		||||
	local timeout = args.timeout or 30
 | 
			
		||||
	local notify = args.notify or 'on'
 | 
			
		||||
	local full_notify = args.full_notify or notify
 | 
			
		||||
	local n_perc = args.n_perc or { 5, 15 }
 | 
			
		||||
	local batteries = args.batteries or (args.battery and { args.battery }) or {}
 | 
			
		||||
	local ac = args.ac or 'AC0'
 | 
			
		||||
	local settings = args.settings or function() end
 | 
			
		||||
 | 
			
		||||
    function bat.get_batteries()
 | 
			
		||||
        helpers.line_callback("ls -1 " .. pspath, function(line)
 | 
			
		||||
            local bstr =  string.match(line, "BAT%w+")
 | 
			
		||||
            if bstr then
 | 
			
		||||
                batteries[#batteries + 1] = bstr
 | 
			
		||||
            else
 | 
			
		||||
                ac = string.match(line, "A%w+") or ac
 | 
			
		||||
            end
 | 
			
		||||
        end)
 | 
			
		||||
    end
 | 
			
		||||
	function bat.get_batteries()
 | 
			
		||||
		helpers.line_callback('ls -1 ' .. pspath, function(line)
 | 
			
		||||
			local bstr = string.match(line, 'BAT%w+')
 | 
			
		||||
			if bstr then
 | 
			
		||||
				batteries[#batteries + 1] = bstr
 | 
			
		||||
			else
 | 
			
		||||
				ac = string.match(line, 'A%w+') or ac
 | 
			
		||||
			end
 | 
			
		||||
		end)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    if #batteries == 0 then bat.get_batteries() end
 | 
			
		||||
	if #batteries == 0 then
 | 
			
		||||
		bat.get_batteries()
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    bat_notification_critical_preset = {
 | 
			
		||||
        title   = "Battery exhausted",
 | 
			
		||||
        text    = "Shutdown imminent",
 | 
			
		||||
        timeout = 15,
 | 
			
		||||
        fg      = "#000000",
 | 
			
		||||
        bg      = "#FFFFFF"
 | 
			
		||||
    }
 | 
			
		||||
	bat_notification_critical_preset = {
 | 
			
		||||
		title = 'Battery exhausted',
 | 
			
		||||
		text = 'Shutdown imminent',
 | 
			
		||||
		timeout = 15,
 | 
			
		||||
		fg = '#000000',
 | 
			
		||||
		bg = '#FFFFFF',
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    bat_notification_low_preset = {
 | 
			
		||||
        title   = "Battery low",
 | 
			
		||||
        text    = "Plug the cable!",
 | 
			
		||||
        timeout = 15,
 | 
			
		||||
        fg      = "#202020",
 | 
			
		||||
        bg      = "#CDCDCD"
 | 
			
		||||
    }
 | 
			
		||||
	bat_notification_low_preset = {
 | 
			
		||||
		title = 'Battery low',
 | 
			
		||||
		text = 'Plug the cable!',
 | 
			
		||||
		timeout = 15,
 | 
			
		||||
		fg = '#202020',
 | 
			
		||||
		bg = '#CDCDCD',
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    bat_notification_charged_preset = {
 | 
			
		||||
        title   = "Battery full",
 | 
			
		||||
        text    = "You can unplug the cable",
 | 
			
		||||
        timeout = 15,
 | 
			
		||||
        fg      = "#202020",
 | 
			
		||||
        bg      = "#CDCDCD"
 | 
			
		||||
    }
 | 
			
		||||
	bat_notification_charged_preset = {
 | 
			
		||||
		title = 'Battery full',
 | 
			
		||||
		text = 'You can unplug the cable',
 | 
			
		||||
		timeout = 15,
 | 
			
		||||
		fg = '#202020',
 | 
			
		||||
		bg = '#CDCDCD',
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    bat_now = {
 | 
			
		||||
        status    = "N/A",
 | 
			
		||||
        ac_status = "N/A",
 | 
			
		||||
        perc      = "N/A",
 | 
			
		||||
        time      = "N/A",
 | 
			
		||||
        watt      = "N/A",
 | 
			
		||||
        capacity  = "N/A"
 | 
			
		||||
    }
 | 
			
		||||
	bat_now = {
 | 
			
		||||
		status = 'N/A',
 | 
			
		||||
		ac_status = 'N/A',
 | 
			
		||||
		perc = 'N/A',
 | 
			
		||||
		time = 'N/A',
 | 
			
		||||
		watt = 'N/A',
 | 
			
		||||
		capacity = 'N/A',
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    bat_now.n_status   = {}
 | 
			
		||||
    bat_now.n_perc     = {}
 | 
			
		||||
    bat_now.n_capacity = {}
 | 
			
		||||
    for i = 1, #batteries do
 | 
			
		||||
        bat_now.n_status[i] = "N/A"
 | 
			
		||||
        bat_now.n_perc[i] = 0
 | 
			
		||||
        bat_now.n_capacity[i] = 0
 | 
			
		||||
    end
 | 
			
		||||
	bat_now.n_status = {}
 | 
			
		||||
	bat_now.n_perc = {}
 | 
			
		||||
	bat_now.n_capacity = {}
 | 
			
		||||
	for i = 1, #batteries do
 | 
			
		||||
		bat_now.n_status[i] = 'N/A'
 | 
			
		||||
		bat_now.n_perc[i] = 0
 | 
			
		||||
		bat_now.n_capacity[i] = 0
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    -- used to notify full charge only once before discharging
 | 
			
		||||
    local fullnotification = false
 | 
			
		||||
	-- used to notify full charge only once before discharging
 | 
			
		||||
	local fullnotification = false
 | 
			
		||||
 | 
			
		||||
    function bat.update()
 | 
			
		||||
        -- luacheck: globals bat_now
 | 
			
		||||
        local sum_rate_current  = 0
 | 
			
		||||
        local sum_rate_voltage  = 0
 | 
			
		||||
        local sum_rate_power    = 0
 | 
			
		||||
        local sum_rate_energy   = 0
 | 
			
		||||
        local sum_energy_now    = 0
 | 
			
		||||
        local sum_energy_full   = 0
 | 
			
		||||
        local sum_charge_full   = 0
 | 
			
		||||
        local sum_charge_design = 0
 | 
			
		||||
	function bat.update()
 | 
			
		||||
		-- luacheck: globals bat_now
 | 
			
		||||
		local sum_rate_current = 0
 | 
			
		||||
		local sum_rate_voltage = 0
 | 
			
		||||
		local sum_rate_power = 0
 | 
			
		||||
		local sum_rate_energy = 0
 | 
			
		||||
		local sum_energy_now = 0
 | 
			
		||||
		local sum_energy_full = 0
 | 
			
		||||
		local sum_charge_full = 0
 | 
			
		||||
		local sum_charge_design = 0
 | 
			
		||||
 | 
			
		||||
        for i, battery in ipairs(batteries) do
 | 
			
		||||
            local bstr    = pspath .. battery
 | 
			
		||||
            local present = helpers.first_line(bstr .. "/present")
 | 
			
		||||
		for i, battery in ipairs(batteries) do
 | 
			
		||||
			local bstr = pspath .. battery
 | 
			
		||||
			local present = helpers.first_line(bstr .. '/present')
 | 
			
		||||
 | 
			
		||||
            if tonumber(present) == 1 then
 | 
			
		||||
                -- current_now(I)[uA], voltage_now(U)[uV], power_now(P)[uW]
 | 
			
		||||
                local rate_current = tonumber(helpers.first_line(bstr .. "/current_now"))
 | 
			
		||||
                local rate_voltage = tonumber(helpers.first_line(bstr .. "/voltage_now"))
 | 
			
		||||
                local rate_power   = tonumber(helpers.first_line(bstr .. "/power_now"))
 | 
			
		||||
                local charge_full  = tonumber(helpers.first_line(bstr .. "/charge_full"))
 | 
			
		||||
                local charge_design = tonumber(helpers.first_line(bstr .. "/charge_full_design"))
 | 
			
		||||
			if tonumber(present) == 1 then
 | 
			
		||||
				-- current_now(I)[uA], voltage_now(U)[uV], power_now(P)[uW]
 | 
			
		||||
				local rate_current = tonumber(helpers.first_line(bstr .. '/current_now'))
 | 
			
		||||
				local rate_voltage = tonumber(helpers.first_line(bstr .. '/voltage_now'))
 | 
			
		||||
				local rate_power = tonumber(helpers.first_line(bstr .. '/power_now'))
 | 
			
		||||
				local charge_full = tonumber(helpers.first_line(bstr .. '/charge_full'))
 | 
			
		||||
				local charge_design = tonumber(helpers.first_line(bstr .. '/charge_full_design'))
 | 
			
		||||
 | 
			
		||||
                -- energy_now(P)[uWh], charge_now(I)[uAh]
 | 
			
		||||
                local energy_now = tonumber(helpers.first_line(bstr .. "/energy_now") or
 | 
			
		||||
                                   helpers.first_line(bstr .. "/charge_now"))
 | 
			
		||||
				-- energy_now(P)[uWh], charge_now(I)[uAh]
 | 
			
		||||
				local energy_now =
 | 
			
		||||
					tonumber(helpers.first_line(bstr .. '/energy_now') or helpers.first_line(bstr .. '/charge_now'))
 | 
			
		||||
 | 
			
		||||
                -- energy_full(P)[uWh], charge_full(I)[uAh]
 | 
			
		||||
                local energy_full = tonumber(helpers.first_line(bstr .. "/energy_full") or
 | 
			
		||||
                                    charge_full)
 | 
			
		||||
				-- energy_full(P)[uWh], charge_full(I)[uAh]
 | 
			
		||||
				local energy_full = tonumber(helpers.first_line(bstr .. '/energy_full') or charge_full)
 | 
			
		||||
 | 
			
		||||
                local energy_percentage = tonumber(helpers.first_line(bstr .. "/capacity")) or
 | 
			
		||||
                                          math.floor((energy_now / energy_full) * 100)
 | 
			
		||||
				local energy_percentage = tonumber(helpers.first_line(bstr .. '/capacity'))
 | 
			
		||||
					or math.floor((energy_now / energy_full) * 100)
 | 
			
		||||
 | 
			
		||||
                bat_now.n_status[i] = helpers.first_line(bstr .. "/status") or "N/A"
 | 
			
		||||
                bat_now.n_perc[i]   = energy_percentage or bat_now.n_perc[i]
 | 
			
		||||
				bat_now.n_status[i] = helpers.first_line(bstr .. '/status') or 'N/A'
 | 
			
		||||
				bat_now.n_perc[i] = energy_percentage or bat_now.n_perc[i]
 | 
			
		||||
 | 
			
		||||
                if not charge_design or charge_design == 0 then
 | 
			
		||||
                    bat_now.n_capacity[i] = 0
 | 
			
		||||
                else
 | 
			
		||||
                    bat_now.n_capacity[i] = math.floor((charge_full / charge_design) * 100)
 | 
			
		||||
                end
 | 
			
		||||
				if not charge_design or charge_design == 0 then
 | 
			
		||||
					bat_now.n_capacity[i] = 0
 | 
			
		||||
				else
 | 
			
		||||
					bat_now.n_capacity[i] = math.floor((charge_full / charge_design) * 100)
 | 
			
		||||
				end
 | 
			
		||||
 | 
			
		||||
                sum_rate_current  = sum_rate_current + (rate_current or 0)
 | 
			
		||||
                sum_rate_voltage  = sum_rate_voltage + (rate_voltage or 0)
 | 
			
		||||
                sum_rate_power    = sum_rate_power + (rate_power or 0)
 | 
			
		||||
                sum_rate_energy   = sum_rate_energy + (rate_power or (((rate_voltage or 0) * (rate_current or 0)) / 1e6))
 | 
			
		||||
                sum_energy_now    = sum_energy_now + (energy_now or 0)
 | 
			
		||||
                sum_energy_full   = sum_energy_full + (energy_full or 0)
 | 
			
		||||
                sum_charge_full   = sum_charge_full + (charge_full or 0)
 | 
			
		||||
                sum_charge_design = sum_charge_design + (charge_design or 0)
 | 
			
		||||
            end
 | 
			
		||||
        end
 | 
			
		||||
				sum_rate_current = sum_rate_current + (rate_current or 0)
 | 
			
		||||
				sum_rate_voltage = sum_rate_voltage + (rate_voltage or 0)
 | 
			
		||||
				sum_rate_power = sum_rate_power + (rate_power or 0)
 | 
			
		||||
				sum_rate_energy = sum_rate_energy + (rate_power or (((rate_voltage or 0) * (rate_current or 0)) / 1e6))
 | 
			
		||||
				sum_energy_now = sum_energy_now + (energy_now or 0)
 | 
			
		||||
				sum_energy_full = sum_energy_full + (energy_full or 0)
 | 
			
		||||
				sum_charge_full = sum_charge_full + (charge_full or 0)
 | 
			
		||||
				sum_charge_design = sum_charge_design + (charge_design or 0)
 | 
			
		||||
			end
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
        bat_now.capacity = math.floor(math.min(100, (sum_charge_full / sum_charge_design) * 100))
 | 
			
		||||
		bat_now.capacity = math.floor(math.min(100, (sum_charge_full / sum_charge_design) * 100))
 | 
			
		||||
 | 
			
		||||
        -- 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] or "N/A"
 | 
			
		||||
        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(helpers.first_line(string.format("%s%s/online", pspath, ac))) or "N/A"
 | 
			
		||||
		-- 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] or 'N/A'
 | 
			
		||||
		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(helpers.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
 | 
			
		||||
		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
 | 
			
		||||
            elseif bat_now.status ~= "Full" then
 | 
			
		||||
                local rate_time = 0
 | 
			
		||||
                -- 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
 | 
			
		||||
			-- update {perc,time,watt} iff battery not full and rate > 0
 | 
			
		||||
			elseif bat_now.status ~= 'Full' then
 | 
			
		||||
				local rate_time = 0
 | 
			
		||||
				-- 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 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
 | 
			
		||||
					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" then
 | 
			
		||||
                bat_now.perc  = 100
 | 
			
		||||
                bat_now.time  = "00:00"
 | 
			
		||||
                bat_now.watt  = 0
 | 
			
		||||
            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' then
 | 
			
		||||
				bat_now.perc = 100
 | 
			
		||||
				bat_now.time = '00:00'
 | 
			
		||||
				bat_now.watt = 0
 | 
			
		||||
			end
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
        widget = bat.widget
 | 
			
		||||
        settings()
 | 
			
		||||
		widget = bat.widget
 | 
			
		||||
		settings()
 | 
			
		||||
 | 
			
		||||
        -- notifications for critical, low, and full levels
 | 
			
		||||
        if notify == "on" then
 | 
			
		||||
            if bat_now.status == "Discharging" then
 | 
			
		||||
                if tonumber(bat_now.perc) <= n_perc[1] then
 | 
			
		||||
                    bat.id = naughty.notify({
 | 
			
		||||
                        preset = bat_notification_critical_preset,
 | 
			
		||||
                        replaces_id = bat.id
 | 
			
		||||
                    }).id
 | 
			
		||||
                elseif tonumber(bat_now.perc) <= n_perc[2] then
 | 
			
		||||
                    bat.id = naughty.notify({
 | 
			
		||||
                        preset = bat_notification_low_preset,
 | 
			
		||||
                        replaces_id = bat.id
 | 
			
		||||
                    }).id
 | 
			
		||||
                end
 | 
			
		||||
                fullnotification = false
 | 
			
		||||
            elseif bat_now.status == "Full" and full_notify == "on" and not fullnotification then
 | 
			
		||||
                bat.id = naughty.notify({
 | 
			
		||||
                    preset = bat_notification_charged_preset,
 | 
			
		||||
                    replaces_id = bat.id
 | 
			
		||||
                }).id
 | 
			
		||||
                fullnotification = true
 | 
			
		||||
            end
 | 
			
		||||
        end
 | 
			
		||||
    end
 | 
			
		||||
		-- notifications for critical, low, and full levels
 | 
			
		||||
		if notify == 'on' then
 | 
			
		||||
			if bat_now.status == 'Discharging' then
 | 
			
		||||
				if tonumber(bat_now.perc) <= n_perc[1] then
 | 
			
		||||
					bat.id = naughty.notify({
 | 
			
		||||
						preset = bat_notification_critical_preset,
 | 
			
		||||
						replaces_id = bat.id,
 | 
			
		||||
					}).id
 | 
			
		||||
				elseif tonumber(bat_now.perc) <= n_perc[2] then
 | 
			
		||||
					bat.id = naughty.notify({
 | 
			
		||||
						preset = bat_notification_low_preset,
 | 
			
		||||
						replaces_id = bat.id,
 | 
			
		||||
					}).id
 | 
			
		||||
				end
 | 
			
		||||
				fullnotification = false
 | 
			
		||||
			elseif bat_now.status == 'Full' and full_notify == 'on' and not fullnotification then
 | 
			
		||||
				bat.id = naughty.notify({
 | 
			
		||||
					preset = bat_notification_charged_preset,
 | 
			
		||||
					replaces_id = bat.id,
 | 
			
		||||
				}).id
 | 
			
		||||
				fullnotification = true
 | 
			
		||||
			end
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    helpers.newtimer("batteries", timeout, bat.update)
 | 
			
		||||
	helpers.newtimer('batteries', timeout, bat.update)
 | 
			
		||||
 | 
			
		||||
    return bat
 | 
			
		||||
	return bat
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
return factory
 | 
			
		||||
 
 | 
			
		||||
@@ -5,16 +5,16 @@
 | 
			
		||||
 | 
			
		||||
--]]
 | 
			
		||||
 | 
			
		||||
local helpers  = require("lain.helpers")
 | 
			
		||||
local markup   = require("lain.util.markup")
 | 
			
		||||
local awful    = require("awful")
 | 
			
		||||
local naughty  = require("naughty")
 | 
			
		||||
local floor    = math.floor
 | 
			
		||||
local os       = os
 | 
			
		||||
local pairs    = pairs
 | 
			
		||||
local string   = string
 | 
			
		||||
local tconcat  = table.concat
 | 
			
		||||
local type     = type
 | 
			
		||||
local helpers = require('lain.helpers')
 | 
			
		||||
local markup = require('lain.util.markup')
 | 
			
		||||
local awful = require('awful')
 | 
			
		||||
local naughty = require('naughty')
 | 
			
		||||
local floor = math.floor
 | 
			
		||||
local os = os
 | 
			
		||||
local pairs = pairs
 | 
			
		||||
local string = string
 | 
			
		||||
local tconcat = table.concat
 | 
			
		||||
local type = type
 | 
			
		||||
local tonumber = tonumber
 | 
			
		||||
local tostring = tostring
 | 
			
		||||
 | 
			
		||||
@@ -22,170 +22,205 @@ local tostring = tostring
 | 
			
		||||
-- lain.widget.cal
 | 
			
		||||
 | 
			
		||||
local function factory(args)
 | 
			
		||||
    args = args or {}
 | 
			
		||||
    local cal = {
 | 
			
		||||
        attach_to           = args.attach_to or {},
 | 
			
		||||
        week_start          = args.week_start or 2,
 | 
			
		||||
        three               = args.three or false,
 | 
			
		||||
        followtag           = args.followtag or false,
 | 
			
		||||
        week_number         = args.week_number or "none",
 | 
			
		||||
        week_number_format  = args.week_number_format or args.week_number == "left" and "%3d | " or "| %-3d",
 | 
			
		||||
        icons               = args.icons or helpers.icons_dir .. "cal/white/",
 | 
			
		||||
        notification_preset = args.notification_preset or {
 | 
			
		||||
            font = "Monospace 10", fg = "#FFFFFF", bg = "#000000"
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
	args = args or {}
 | 
			
		||||
	local cal = {
 | 
			
		||||
		attach_to = args.attach_to or {},
 | 
			
		||||
		week_start = args.week_start or 2,
 | 
			
		||||
		three = args.three or false,
 | 
			
		||||
		followtag = args.followtag or false,
 | 
			
		||||
		week_number = args.week_number or 'none',
 | 
			
		||||
		week_number_format = args.week_number_format or args.week_number == 'left' and '%3d | ' or '| %-3d',
 | 
			
		||||
		icons = args.icons or helpers.icons_dir .. 'cal/white/',
 | 
			
		||||
		notification_preset = args.notification_preset or {
 | 
			
		||||
			font = 'Monospace 10',
 | 
			
		||||
			fg = '#FFFFFF',
 | 
			
		||||
			bg = '#000000',
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    function cal.get_week_number(m, st_day, x)
 | 
			
		||||
        local date = os.date("*t", m)
 | 
			
		||||
	function cal.get_week_number(m, st_day, x)
 | 
			
		||||
		local date = os.date('*t', m)
 | 
			
		||||
 | 
			
		||||
        local week_step = (x ~= 0 and floor((x + st_day) / 7) - 1 or 0);
 | 
			
		||||
		local week_step = (x ~= 0 and floor((x + st_day) / 7) - 1 or 0)
 | 
			
		||||
 | 
			
		||||
        local display_time = os.time {
 | 
			
		||||
            year = date.year, month = date.month, day = date.day + 7 * week_step
 | 
			
		||||
        }
 | 
			
		||||
		local display_time = os.time({
 | 
			
		||||
			year = date.year,
 | 
			
		||||
			month = date.month,
 | 
			
		||||
			day = date.day + 7 * week_step,
 | 
			
		||||
		})
 | 
			
		||||
 | 
			
		||||
        return string.format(cal.week_number_format, os.date("%V", display_time))
 | 
			
		||||
    end
 | 
			
		||||
		return string.format(cal.week_number_format, os.date('%V', display_time))
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    function cal.sum_week_days(x, y)
 | 
			
		||||
        return (x + y) % 7
 | 
			
		||||
    end
 | 
			
		||||
	function cal.sum_week_days(x, y)
 | 
			
		||||
		return (x + y) % 7
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    function cal.build(month, year)
 | 
			
		||||
        local current_month, current_year = tonumber(os.date("%m")), tonumber(os.date("%Y"))
 | 
			
		||||
        local is_current_month = (not month or not year) or (month == current_month and year == current_year)
 | 
			
		||||
        local today = is_current_month and tonumber(os.date("%d")) -- otherwise nil and not highlighted
 | 
			
		||||
        local t = os.time { year = year or current_year, month = month and month+1 or current_month+1, day = 0 }
 | 
			
		||||
        local d = os.date("*t", t)
 | 
			
		||||
        local mth_days, st_day, this_month = d.day, (d.wday-d.day-cal.week_start+1)%7, os.date("%B %Y", t)
 | 
			
		||||
        local notifytable = { [1] = string.format("%s%s\n", string.rep(" ", floor((28 - this_month:len())/2)), markup.bold(this_month)) }
 | 
			
		||||
        for day_num = 0, 6 do
 | 
			
		||||
            notifytable[#notifytable+1] = string.format("%3s ", os.date("%a", os.time { year = 2006, month = 1, day = day_num + cal.week_start }))
 | 
			
		||||
        end
 | 
			
		||||
        notifytable[#notifytable] = string.format("%s\n%s", notifytable[#notifytable]:sub(1, -2), string.rep(" ", st_day*4))
 | 
			
		||||
        local strx
 | 
			
		||||
        for x = 1,mth_days do
 | 
			
		||||
            strx = x
 | 
			
		||||
            if x == today then
 | 
			
		||||
                if x < 10 then x = " " .. x end
 | 
			
		||||
                strx = markup.bold(markup.color(cal.notification_preset.bg, cal.notification_preset.fg, x) .. " ")
 | 
			
		||||
            end
 | 
			
		||||
            strx = string.format("%s%s", string.rep(" ", 3 - tostring(x):len()), strx)
 | 
			
		||||
            notifytable[#notifytable+1] = string.format("%-4s%s", strx, (x+st_day)%7==0 and x ~= mth_days and "\n" or "")
 | 
			
		||||
        end
 | 
			
		||||
        if string.len(cal.icons or "") > 0 and today then cal.icon = cal.icons .. today .. ".png" end
 | 
			
		||||
        cal.month, cal.year = d.month, d.year
 | 
			
		||||
	function cal.build(month, year)
 | 
			
		||||
		local current_month, current_year = tonumber(os.date('%m')), tonumber(os.date('%Y'))
 | 
			
		||||
		local is_current_month = (not month or not year) or (month == current_month and year == current_year)
 | 
			
		||||
		local today = is_current_month and tonumber(os.date('%d')) -- otherwise nil and not highlighted
 | 
			
		||||
		local t = os.time({ year = year or current_year, month = month and month + 1 or current_month + 1, day = 0 })
 | 
			
		||||
		local d = os.date('*t', t)
 | 
			
		||||
		local mth_days, st_day, this_month = d.day, (d.wday - d.day - cal.week_start + 1) % 7, os.date('%B %Y', t)
 | 
			
		||||
		local notifytable = {
 | 
			
		||||
			[1] = string.format('%s%s\n', string.rep(' ', floor((28 - this_month:len()) / 2)), markup.bold(this_month)),
 | 
			
		||||
		}
 | 
			
		||||
		for day_num = 0, 6 do
 | 
			
		||||
			notifytable[#notifytable + 1] = string.format(
 | 
			
		||||
				'%3s ',
 | 
			
		||||
				os.date('%a', os.time({ year = 2006, month = 1, day = day_num + cal.week_start }))
 | 
			
		||||
			)
 | 
			
		||||
		end
 | 
			
		||||
		notifytable[#notifytable] =
 | 
			
		||||
			string.format('%s\n%s', notifytable[#notifytable]:sub(1, -2), string.rep(' ', st_day * 4))
 | 
			
		||||
		local strx
 | 
			
		||||
		for x = 1, mth_days do
 | 
			
		||||
			strx = x
 | 
			
		||||
			if x == today then
 | 
			
		||||
				if x < 10 then
 | 
			
		||||
					x = ' ' .. x
 | 
			
		||||
				end
 | 
			
		||||
				strx = markup.bold(markup.color(cal.notification_preset.bg, cal.notification_preset.fg, x) .. ' ')
 | 
			
		||||
			end
 | 
			
		||||
			strx = string.format('%s%s', string.rep(' ', 3 - tostring(x):len()), strx)
 | 
			
		||||
			notifytable[#notifytable + 1] =
 | 
			
		||||
				string.format('%-4s%s', strx, (x + st_day) % 7 == 0 and x ~= mth_days and '\n' or '')
 | 
			
		||||
		end
 | 
			
		||||
		if string.len(cal.icons or '') > 0 and today then
 | 
			
		||||
			cal.icon = cal.icons .. today .. '.png'
 | 
			
		||||
		end
 | 
			
		||||
		cal.month, cal.year = d.month, d.year
 | 
			
		||||
 | 
			
		||||
        if cal.week_number ~= "none" then
 | 
			
		||||
            local m = os.time { year = year or current_year, month = month and month or current_month, day = 1 }
 | 
			
		||||
            local head_prepend = string.rep(" ", tostring(string.format(cal.week_number_format, 0)):len())
 | 
			
		||||
		if cal.week_number ~= 'none' then
 | 
			
		||||
			local m = os.time({ year = year or current_year, month = month and month or current_month, day = 1 })
 | 
			
		||||
			local head_prepend = string.rep(' ', tostring(string.format(cal.week_number_format, 0)):len())
 | 
			
		||||
 | 
			
		||||
            if cal.week_number == "left" then
 | 
			
		||||
                notifytable[1] = head_prepend .. notifytable[1] -- month-year row
 | 
			
		||||
                notifytable[2] = head_prepend .. notifytable[2] -- weekdays row
 | 
			
		||||
                notifytable[8] = notifytable[8]:gsub("\n", "\n" .. cal.get_week_number(m, st_day, 0)) -- first week of the month
 | 
			
		||||
			if cal.week_number == 'left' then
 | 
			
		||||
				notifytable[1] = head_prepend .. notifytable[1] -- month-year row
 | 
			
		||||
				notifytable[2] = head_prepend .. notifytable[2] -- weekdays row
 | 
			
		||||
				notifytable[8] = notifytable[8]:gsub('\n', '\n' .. cal.get_week_number(m, st_day, 0)) -- first week of the month
 | 
			
		||||
 | 
			
		||||
                for x = 10,#notifytable do
 | 
			
		||||
                    if cal.sum_week_days(st_day, x) == 2 then
 | 
			
		||||
                        notifytable[x] = cal.get_week_number(m, st_day, x) .. notifytable[x]
 | 
			
		||||
                    end
 | 
			
		||||
                end
 | 
			
		||||
            elseif cal.week_number == "right" then
 | 
			
		||||
                notifytable[8] = notifytable[8]:gsub("\n", head_prepend .. "\n") -- weekdays row
 | 
			
		||||
                for x = 9,#notifytable do
 | 
			
		||||
                    if cal.sum_week_days(st_day, x) == 1 then
 | 
			
		||||
                        notifytable[x] = notifytable[x]:gsub("\n", cal.get_week_number(m, st_day, x - 7) .. "\n")
 | 
			
		||||
                    end
 | 
			
		||||
                end
 | 
			
		||||
                -- last week of the month
 | 
			
		||||
                local end_days = cal.sum_week_days(st_day, mth_days)
 | 
			
		||||
                if end_days ~= 0 then end_days = 7 - end_days end
 | 
			
		||||
                notifytable[#notifytable] = notifytable[#notifytable] .. string.rep(" ", 4 * end_days) .. cal.get_week_number(m, st_day, mth_days + end_days)
 | 
			
		||||
            end
 | 
			
		||||
        end
 | 
			
		||||
				for x = 10, #notifytable do
 | 
			
		||||
					if cal.sum_week_days(st_day, x) == 2 then
 | 
			
		||||
						notifytable[x] = cal.get_week_number(m, st_day, x) .. notifytable[x]
 | 
			
		||||
					end
 | 
			
		||||
				end
 | 
			
		||||
			elseif cal.week_number == 'right' then
 | 
			
		||||
				notifytable[8] = notifytable[8]:gsub('\n', head_prepend .. '\n') -- weekdays row
 | 
			
		||||
				for x = 9, #notifytable do
 | 
			
		||||
					if cal.sum_week_days(st_day, x) == 1 then
 | 
			
		||||
						notifytable[x] = notifytable[x]:gsub('\n', cal.get_week_number(m, st_day, x - 7) .. '\n')
 | 
			
		||||
					end
 | 
			
		||||
				end
 | 
			
		||||
				-- last week of the month
 | 
			
		||||
				local end_days = cal.sum_week_days(st_day, mth_days)
 | 
			
		||||
				if end_days ~= 0 then
 | 
			
		||||
					end_days = 7 - end_days
 | 
			
		||||
				end
 | 
			
		||||
				notifytable[#notifytable] = notifytable[#notifytable]
 | 
			
		||||
					.. string.rep(' ', 4 * end_days)
 | 
			
		||||
					.. cal.get_week_number(m, st_day, mth_days + end_days)
 | 
			
		||||
			end
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
        return notifytable
 | 
			
		||||
    end
 | 
			
		||||
		return notifytable
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    function cal.getdate(month, year, offset)
 | 
			
		||||
        if not month or not year then
 | 
			
		||||
            month = tonumber(os.date("%m"))
 | 
			
		||||
            year  = tonumber(os.date("%Y"))
 | 
			
		||||
        end
 | 
			
		||||
	function cal.getdate(month, year, offset)
 | 
			
		||||
		if not month or not year then
 | 
			
		||||
			month = tonumber(os.date('%m'))
 | 
			
		||||
			year = tonumber(os.date('%Y'))
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
        month = month + offset
 | 
			
		||||
		month = month + offset
 | 
			
		||||
 | 
			
		||||
        while month > 12 do
 | 
			
		||||
            month = month - 12
 | 
			
		||||
            year = year + 1
 | 
			
		||||
        end
 | 
			
		||||
		while month > 12 do
 | 
			
		||||
			month = month - 12
 | 
			
		||||
			year = year + 1
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
        while month < 1 do
 | 
			
		||||
            month = month + 12
 | 
			
		||||
            year = year - 1
 | 
			
		||||
        end
 | 
			
		||||
		while month < 1 do
 | 
			
		||||
			month = month + 12
 | 
			
		||||
			year = year - 1
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
        return month, year
 | 
			
		||||
    end
 | 
			
		||||
		return month, year
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    function cal.hide()
 | 
			
		||||
        if not cal.notification then return end
 | 
			
		||||
        naughty.destroy(cal.notification)
 | 
			
		||||
        cal.notification = nil
 | 
			
		||||
    end
 | 
			
		||||
	function cal.hide()
 | 
			
		||||
		if not cal.notification then
 | 
			
		||||
			return
 | 
			
		||||
		end
 | 
			
		||||
		naughty.destroy(cal.notification)
 | 
			
		||||
		cal.notification = nil
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    function cal.show(seconds, month, year, scr)
 | 
			
		||||
        local text = tconcat(cal.build(month, year))
 | 
			
		||||
	function cal.show(seconds, month, year, scr)
 | 
			
		||||
		local text = tconcat(cal.build(month, year))
 | 
			
		||||
 | 
			
		||||
        if cal.three then
 | 
			
		||||
            local current_month, current_year = cal.month, cal.year
 | 
			
		||||
            local prev_month, prev_year = cal.getdate(cal.month, cal.year, -1)
 | 
			
		||||
            local next_month, next_year = cal.getdate(cal.month, cal.year,  1)
 | 
			
		||||
            text = string.format("%s\n\n%s\n\n%s",
 | 
			
		||||
            tconcat(cal.build(prev_month, prev_year)), text,
 | 
			
		||||
            tconcat(cal.build(next_month, next_year)))
 | 
			
		||||
            cal.month, cal.year = current_month, current_year
 | 
			
		||||
        end
 | 
			
		||||
		if cal.three then
 | 
			
		||||
			local current_month, current_year = cal.month, cal.year
 | 
			
		||||
			local prev_month, prev_year = cal.getdate(cal.month, cal.year, -1)
 | 
			
		||||
			local next_month, next_year = cal.getdate(cal.month, cal.year, 1)
 | 
			
		||||
			text = string.format(
 | 
			
		||||
				'%s\n\n%s\n\n%s',
 | 
			
		||||
				tconcat(cal.build(prev_month, prev_year)),
 | 
			
		||||
				text,
 | 
			
		||||
				tconcat(cal.build(next_month, next_year))
 | 
			
		||||
			)
 | 
			
		||||
			cal.month, cal.year = current_month, current_year
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
        if cal.notification then
 | 
			
		||||
            local title = cal.notification_preset.title or nil
 | 
			
		||||
            naughty.replace_text(cal.notification, title, text)
 | 
			
		||||
            return
 | 
			
		||||
        end
 | 
			
		||||
		if cal.notification then
 | 
			
		||||
			local title = cal.notification_preset.title or nil
 | 
			
		||||
			naughty.replace_text(cal.notification, title, text)
 | 
			
		||||
			return
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
        cal.notification = naughty.notify {
 | 
			
		||||
            preset  = cal.notification_preset,
 | 
			
		||||
            screen  = cal.followtag and awful.screen.focused() or scr or 1,
 | 
			
		||||
            icon    = cal.icon,
 | 
			
		||||
            timeout = type(seconds) == "number" and seconds or cal.notification_preset.timeout or 5,
 | 
			
		||||
            text    = text
 | 
			
		||||
        }
 | 
			
		||||
    end
 | 
			
		||||
		cal.notification = naughty.notify({
 | 
			
		||||
			preset = cal.notification_preset,
 | 
			
		||||
			screen = cal.followtag and awful.screen.focused() or scr or 1,
 | 
			
		||||
			icon = cal.icon,
 | 
			
		||||
			timeout = type(seconds) == 'number' and seconds or cal.notification_preset.timeout or 5,
 | 
			
		||||
			text = text,
 | 
			
		||||
		})
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    function cal.hover_on() cal.show(0) end
 | 
			
		||||
    function cal.move(offset)
 | 
			
		||||
        offset = offset or 0
 | 
			
		||||
        cal.month, cal.year = cal.getdate(cal.month, cal.year, offset)
 | 
			
		||||
        cal.show(0, cal.month, cal.year)
 | 
			
		||||
    end
 | 
			
		||||
    function cal.prev() cal.move(-1) end
 | 
			
		||||
    function cal.next() cal.move( 1) end
 | 
			
		||||
	function cal.hover_on()
 | 
			
		||||
		cal.show(0)
 | 
			
		||||
	end
 | 
			
		||||
	function cal.move(offset)
 | 
			
		||||
		offset = offset or 0
 | 
			
		||||
		cal.month, cal.year = cal.getdate(cal.month, cal.year, offset)
 | 
			
		||||
		cal.show(0, cal.month, cal.year)
 | 
			
		||||
	end
 | 
			
		||||
	function cal.prev()
 | 
			
		||||
		cal.move(-1)
 | 
			
		||||
	end
 | 
			
		||||
	function cal.next()
 | 
			
		||||
		cal.move(1)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    function cal.attach(widget)
 | 
			
		||||
        widget:connect_signal("mouse::enter", cal.hover_on)
 | 
			
		||||
        widget:connect_signal("mouse::leave", cal.hide)
 | 
			
		||||
        widget:buttons(awful.util.table.join(
 | 
			
		||||
                    awful.button({}, 1, cal.prev),
 | 
			
		||||
                    awful.button({}, 3, cal.next),
 | 
			
		||||
                    awful.button({}, 2, cal.hover_on),
 | 
			
		||||
                    awful.button({}, 5, cal.prev),
 | 
			
		||||
                    awful.button({}, 4, cal.next)))
 | 
			
		||||
    end
 | 
			
		||||
	function cal.attach(widget)
 | 
			
		||||
		widget:connect_signal('mouse::enter', cal.hover_on)
 | 
			
		||||
		widget:connect_signal('mouse::leave', cal.hide)
 | 
			
		||||
		widget:buttons(
 | 
			
		||||
			awful.util.table.join(
 | 
			
		||||
				awful.button({}, 1, cal.prev),
 | 
			
		||||
				awful.button({}, 3, cal.next),
 | 
			
		||||
				awful.button({}, 2, cal.hover_on),
 | 
			
		||||
				awful.button({}, 5, cal.prev),
 | 
			
		||||
				awful.button({}, 4, cal.next)
 | 
			
		||||
			)
 | 
			
		||||
		)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    for _, widget in pairs(cal.attach_to) do cal.attach(widget) end
 | 
			
		||||
	for _, widget in pairs(cal.attach_to) do
 | 
			
		||||
		cal.attach(widget)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    return cal
 | 
			
		||||
	return cal
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
return factory
 | 
			
		||||
 
 | 
			
		||||
@@ -10,9 +10,9 @@
 | 
			
		||||
 | 
			
		||||
--]]
 | 
			
		||||
 | 
			
		||||
local wrequire     = require("lain.helpers").wrequire
 | 
			
		||||
local wrequire = require('lain.helpers').wrequire
 | 
			
		||||
local setmetatable = setmetatable
 | 
			
		||||
 | 
			
		||||
local widget = { _NAME = "lain.widget.contrib" }
 | 
			
		||||
local widget = { _NAME = 'lain.widget.contrib' }
 | 
			
		||||
 | 
			
		||||
return setmetatable(widget, { __index = wrequire })
 | 
			
		||||
 
 | 
			
		||||
@@ -5,93 +5,103 @@
 | 
			
		||||
 | 
			
		||||
--]]
 | 
			
		||||
 | 
			
		||||
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           = os
 | 
			
		||||
local string       = string
 | 
			
		||||
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 = os
 | 
			
		||||
local string = string
 | 
			
		||||
 | 
			
		||||
-- MOC audio player
 | 
			
		||||
-- lain.widget.contrib.moc
 | 
			
		||||
 | 
			
		||||
local function factory(args)
 | 
			
		||||
    args                = args or {}
 | 
			
		||||
	args = args or {}
 | 
			
		||||
 | 
			
		||||
    local moc           = { widget = args.widget or wibox.widget.textbox() }
 | 
			
		||||
    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 moc = { widget = args.widget or wibox.widget.textbox() }
 | 
			
		||||
	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
 | 
			
		||||
 | 
			
		||||
    moc_notification_preset = { title = "Now playing", timeout = 6 }
 | 
			
		||||
	moc_notification_preset = { title = 'Now playing', timeout = 6 }
 | 
			
		||||
 | 
			
		||||
    helpers.set_map("current moc track", nil)
 | 
			
		||||
	helpers.set_map('current moc track', nil)
 | 
			
		||||
 | 
			
		||||
    function moc.update()
 | 
			
		||||
        helpers.async("mocp -i", function(f)
 | 
			
		||||
            moc_now = {
 | 
			
		||||
                state   = "N/A",
 | 
			
		||||
                file    = "N/A",
 | 
			
		||||
                artist  = "N/A",
 | 
			
		||||
                title   = "N/A",
 | 
			
		||||
                album   = "N/A",
 | 
			
		||||
                elapsed = "N/A",
 | 
			
		||||
                total   = "N/A"
 | 
			
		||||
            }
 | 
			
		||||
	function moc.update()
 | 
			
		||||
		helpers.async('mocp -i', function(f)
 | 
			
		||||
			moc_now = {
 | 
			
		||||
				state = 'N/A',
 | 
			
		||||
				file = 'N/A',
 | 
			
		||||
				artist = 'N/A',
 | 
			
		||||
				title = 'N/A',
 | 
			
		||||
				album = 'N/A',
 | 
			
		||||
				elapsed = 'N/A',
 | 
			
		||||
				total = 'N/A',
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
            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)
 | 
			
		||||
                    elseif k == "CurrentTime" then moc_now.elapsed = escape_f(v)
 | 
			
		||||
                    elseif k == "TotalTime"   then moc_now.total   = escape_f(v)
 | 
			
		||||
                    end
 | 
			
		||||
                end
 | 
			
		||||
            end
 | 
			
		||||
			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)
 | 
			
		||||
					elseif k == 'CurrentTime' then
 | 
			
		||||
						moc_now.elapsed = escape_f(v)
 | 
			
		||||
					elseif k == 'TotalTime' then
 | 
			
		||||
						moc_now.total = escape_f(v)
 | 
			
		||||
					end
 | 
			
		||||
				end
 | 
			
		||||
			end
 | 
			
		||||
 | 
			
		||||
            moc_notification_preset.text = string.format("%s (%s) - %s\n%s", moc_now.artist,
 | 
			
		||||
                                           moc_now.album, moc_now.total, moc_now.title)
 | 
			
		||||
            widget = moc.widget
 | 
			
		||||
            settings()
 | 
			
		||||
			moc_notification_preset.text =
 | 
			
		||||
				string.format('%s (%s) - %s\n%s', moc_now.artist, moc_now.album, moc_now.total, moc_now.title)
 | 
			
		||||
			widget = moc.widget
 | 
			
		||||
			settings()
 | 
			
		||||
 | 
			
		||||
            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)
 | 
			
		||||
			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)
 | 
			
		||||
 | 
			
		||||
                    if followtag then moc_notification_preset.screen = focused() end
 | 
			
		||||
					if followtag then
 | 
			
		||||
						moc_notification_preset.screen = focused()
 | 
			
		||||
					end
 | 
			
		||||
 | 
			
		||||
                    local common =  {
 | 
			
		||||
                        preset      = moc_notification_preset,
 | 
			
		||||
                        icon        = default_art,
 | 
			
		||||
                        icon_size   = cover_size,
 | 
			
		||||
                        replaces_id = moc.id,
 | 
			
		||||
                    }
 | 
			
		||||
					local common = {
 | 
			
		||||
						preset = moc_notification_preset,
 | 
			
		||||
						icon = default_art,
 | 
			
		||||
						icon_size = cover_size,
 | 
			
		||||
						replaces_id = moc.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)
 | 
			
		||||
            end
 | 
			
		||||
        end)
 | 
			
		||||
    end
 | 
			
		||||
					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)
 | 
			
		||||
			end
 | 
			
		||||
		end)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    moc.timer = helpers.newtimer("moc", timeout, moc.update, true, true)
 | 
			
		||||
	moc.timer = helpers.newtimer('moc', timeout, moc.update, true, true)
 | 
			
		||||
 | 
			
		||||
    return moc
 | 
			
		||||
	return moc
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
return factory
 | 
			
		||||
 
 | 
			
		||||
@@ -6,36 +6,36 @@
 | 
			
		||||
 | 
			
		||||
--]]
 | 
			
		||||
 | 
			
		||||
local async   = require("lain.helpers").async
 | 
			
		||||
local awful   = require("awful")
 | 
			
		||||
local async = require('lain.helpers').async
 | 
			
		||||
local awful = require('awful')
 | 
			
		||||
local execute = os.execute
 | 
			
		||||
local type    = type
 | 
			
		||||
local type = type
 | 
			
		||||
 | 
			
		||||
-- Redshift
 | 
			
		||||
-- lain.widget.contrib.redshift
 | 
			
		||||
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
 | 
			
		||||
	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
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
	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
 | 
			
		||||
@@ -44,11 +44,15 @@ end
 | 
			
		||||
-- @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, 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
 | 
			
		||||
	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
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
return redshift
 | 
			
		||||
 
 | 
			
		||||
@@ -5,88 +5,92 @@
 | 
			
		||||
 | 
			
		||||
--]]
 | 
			
		||||
 | 
			
		||||
local helpers = require("lain.helpers")
 | 
			
		||||
local markup  = require("lain.util").markup
 | 
			
		||||
local awful   = require("awful")
 | 
			
		||||
local naughty = require("naughty")
 | 
			
		||||
local mouse   = mouse
 | 
			
		||||
local helpers = require('lain.helpers')
 | 
			
		||||
local markup = require('lain.util').markup
 | 
			
		||||
local awful = require('awful')
 | 
			
		||||
local naughty = require('naughty')
 | 
			
		||||
local mouse = mouse
 | 
			
		||||
 | 
			
		||||
-- Taskwarrior notification
 | 
			
		||||
-- lain.widget.contrib.task
 | 
			
		||||
local task = {}
 | 
			
		||||
 | 
			
		||||
function task.hide()
 | 
			
		||||
    if not task.notification then return end
 | 
			
		||||
    naughty.destroy(task.notification)
 | 
			
		||||
    task.notification = nil
 | 
			
		||||
	if not task.notification then
 | 
			
		||||
		return
 | 
			
		||||
	end
 | 
			
		||||
	naughty.destroy(task.notification)
 | 
			
		||||
	task.notification = nil
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
function task.show(scr)
 | 
			
		||||
    task.notification_preset.screen = task.followtag and awful.screen.focused() or scr or 1
 | 
			
		||||
	task.notification_preset.screen = task.followtag and awful.screen.focused() or scr or 1
 | 
			
		||||
 | 
			
		||||
    helpers.async({ awful.util.shell, "-c", task.show_cmd }, function(f)
 | 
			
		||||
        local widget_focused = true
 | 
			
		||||
	helpers.async({ awful.util.shell, '-c', task.show_cmd }, function(f)
 | 
			
		||||
		local widget_focused = true
 | 
			
		||||
 | 
			
		||||
        if mouse.current_widgets then
 | 
			
		||||
            widget_focused = false
 | 
			
		||||
            for _,v in ipairs(mouse.current_widgets) do
 | 
			
		||||
                if task.widget == v then
 | 
			
		||||
                    widget_focused = true
 | 
			
		||||
                    break
 | 
			
		||||
                end
 | 
			
		||||
            end
 | 
			
		||||
        end
 | 
			
		||||
		if mouse.current_widgets then
 | 
			
		||||
			widget_focused = false
 | 
			
		||||
			for _, v in ipairs(mouse.current_widgets) do
 | 
			
		||||
				if task.widget == v then
 | 
			
		||||
					widget_focused = true
 | 
			
		||||
					break
 | 
			
		||||
				end
 | 
			
		||||
			end
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
        if widget_focused then
 | 
			
		||||
            task.hide()
 | 
			
		||||
            task.notification = naughty.notify {
 | 
			
		||||
                preset = task.notification_preset,
 | 
			
		||||
                title  = "task next",
 | 
			
		||||
                text   = markup.font(task.notification_preset.font,
 | 
			
		||||
                         awful.util.escape(f:gsub("\n*$", "")))
 | 
			
		||||
            }
 | 
			
		||||
        end
 | 
			
		||||
    end)
 | 
			
		||||
		if widget_focused then
 | 
			
		||||
			task.hide()
 | 
			
		||||
			task.notification = naughty.notify({
 | 
			
		||||
				preset = task.notification_preset,
 | 
			
		||||
				title = 'task next',
 | 
			
		||||
				text = markup.font(task.notification_preset.font, awful.util.escape(f:gsub('\n*$', ''))),
 | 
			
		||||
			})
 | 
			
		||||
		end
 | 
			
		||||
	end)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
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"
 | 
			
		||||
    }
 | 
			
		||||
	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)
 | 
			
		||||
    args                     = args or {}
 | 
			
		||||
	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
 | 
			
		||||
    task.widget              = widget
 | 
			
		||||
	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
 | 
			
		||||
	task.widget = widget
 | 
			
		||||
 | 
			
		||||
    if not task.notification_preset then
 | 
			
		||||
        task.notification_preset = {
 | 
			
		||||
            font = "Monospace 10",
 | 
			
		||||
            icon = helpers.icons_dir .. "/taskwarrior.png"
 | 
			
		||||
        }
 | 
			
		||||
    end
 | 
			
		||||
	if not task.notification_preset then
 | 
			
		||||
		task.notification_preset = {
 | 
			
		||||
			font = 'Monospace 10',
 | 
			
		||||
			icon = helpers.icons_dir .. '/taskwarrior.png',
 | 
			
		||||
		}
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    if widget then
 | 
			
		||||
        widget:connect_signal("mouse::enter", function () task.show() end)
 | 
			
		||||
        widget:connect_signal("mouse::leave", function () task.hide() end)
 | 
			
		||||
    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 task
 | 
			
		||||
 
 | 
			
		||||
@@ -6,142 +6,159 @@
 | 
			
		||||
 | 
			
		||||
--]]
 | 
			
		||||
 | 
			
		||||
local helpers = require("lain.helpers")
 | 
			
		||||
local focused = require("awful.screen").focused
 | 
			
		||||
local naughty = require("naughty")
 | 
			
		||||
local wibox   = require("wibox")
 | 
			
		||||
local string  = string
 | 
			
		||||
local type    = type
 | 
			
		||||
local helpers = require('lain.helpers')
 | 
			
		||||
local focused = require('awful.screen').focused
 | 
			
		||||
local naughty = require('naughty')
 | 
			
		||||
local wibox = require('wibox')
 | 
			
		||||
local string = string
 | 
			
		||||
local type = type
 | 
			
		||||
 | 
			
		||||
-- ThinkPad battery infos and widget creator
 | 
			
		||||
-- http://www.thinkwiki.org/wiki/Tp_smapi
 | 
			
		||||
-- lain.widget.contrib.tp_smapi
 | 
			
		||||
 | 
			
		||||
local function factory(apipath)
 | 
			
		||||
    local tp_smapi = {
 | 
			
		||||
        path = apipath or "/sys/devices/platform/smapi"
 | 
			
		||||
    }
 | 
			
		||||
	local tp_smapi = {
 | 
			
		||||
		path = apipath or '/sys/devices/platform/smapi',
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    function tp_smapi.get(batid, feature)
 | 
			
		||||
        return helpers.first_line(string.format("%s/%s/%s", tp_smapi.path, batid or "BAT0", feature or ""))
 | 
			
		||||
    end
 | 
			
		||||
	function tp_smapi.get(batid, feature)
 | 
			
		||||
		return helpers.first_line(string.format('%s/%s/%s', tp_smapi.path, batid or 'BAT0', feature or ''))
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    function tp_smapi.installed(batid)
 | 
			
		||||
        return tp_smapi.get(batid, "installed") == "1"
 | 
			
		||||
    end
 | 
			
		||||
	function tp_smapi.installed(batid)
 | 
			
		||||
		return tp_smapi.get(batid, 'installed') == '1'
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    function tp_smapi.status(batid)
 | 
			
		||||
        return tp_smapi.get(batid, "state")
 | 
			
		||||
    end
 | 
			
		||||
	function tp_smapi.status(batid)
 | 
			
		||||
		return tp_smapi.get(batid, 'state')
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    function tp_smapi.percentage(batid)
 | 
			
		||||
        return tp_smapi.get(batid, "remaining_percent")
 | 
			
		||||
    end
 | 
			
		||||
	function tp_smapi.percentage(batid)
 | 
			
		||||
		return tp_smapi.get(batid, 'remaining_percent')
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    -- either running or charging time
 | 
			
		||||
    function tp_smapi.time(batid)
 | 
			
		||||
        local status = tp_smapi.status(batid)
 | 
			
		||||
        local mins_left = tp_smapi.get(batid, string.match(string.lower(status), "discharging") and "remaining_running_time" or "remaining_charging_time")
 | 
			
		||||
        if not string.find(mins_left, "^%d+") then return "N/A" end
 | 
			
		||||
        return string.format("%02d:%02d", math.floor(mins_left / 60), mins_left % 60) -- HH:mm
 | 
			
		||||
    end
 | 
			
		||||
	-- either running or charging time
 | 
			
		||||
	function tp_smapi.time(batid)
 | 
			
		||||
		local status = tp_smapi.status(batid)
 | 
			
		||||
		local mins_left = tp_smapi.get(
 | 
			
		||||
			batid,
 | 
			
		||||
			string.match(string.lower(status), 'discharging') and 'remaining_running_time' or 'remaining_charging_time'
 | 
			
		||||
		)
 | 
			
		||||
		if not string.find(mins_left, '^%d+') then
 | 
			
		||||
			return 'N/A'
 | 
			
		||||
		end
 | 
			
		||||
		return string.format('%02d:%02d', math.floor(mins_left / 60), mins_left % 60) -- HH:mm
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    function tp_smapi.hide()
 | 
			
		||||
        if not tp_smapi.notification then return end
 | 
			
		||||
        naughty.destroy(tp_smapi.notification)
 | 
			
		||||
        tp_smapi.notification = nil
 | 
			
		||||
    end
 | 
			
		||||
	function tp_smapi.hide()
 | 
			
		||||
		if not tp_smapi.notification then
 | 
			
		||||
			return
 | 
			
		||||
		end
 | 
			
		||||
		naughty.destroy(tp_smapi.notification)
 | 
			
		||||
		tp_smapi.notification = nil
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    function tp_smapi.show(batid, seconds, scr)
 | 
			
		||||
        if not tp_smapi.installed(batid) then return end
 | 
			
		||||
	function tp_smapi.show(batid, seconds, scr)
 | 
			
		||||
		if not tp_smapi.installed(batid) then
 | 
			
		||||
			return
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
        local mfgr   = tp_smapi.get(batid, "manufacturer") or "no_mfgr"
 | 
			
		||||
        local model  = tp_smapi.get(batid, "model") or "no_model"
 | 
			
		||||
        local chem   = tp_smapi.get(batid, "chemistry") or "no_chem"
 | 
			
		||||
        local status = tp_smapi.get(batid, "state")
 | 
			
		||||
        local time   = tp_smapi.time(batid)
 | 
			
		||||
        local msg
 | 
			
		||||
		local mfgr = tp_smapi.get(batid, 'manufacturer') or 'no_mfgr'
 | 
			
		||||
		local model = tp_smapi.get(batid, 'model') or 'no_model'
 | 
			
		||||
		local chem = tp_smapi.get(batid, 'chemistry') or 'no_chem'
 | 
			
		||||
		local status = tp_smapi.get(batid, 'state')
 | 
			
		||||
		local time = tp_smapi.time(batid)
 | 
			
		||||
		local msg
 | 
			
		||||
 | 
			
		||||
        if status and status ~= "idle" then
 | 
			
		||||
            msg = string.format("[%s] %s %s", status, time ~= "N/A" and time or "unknown remaining time",
 | 
			
		||||
                  string.lower(status):gsub(" ", ""):gsub("\n", "") == "charging" and " until charged" or " remaining")
 | 
			
		||||
        else
 | 
			
		||||
            msg = "On AC power"
 | 
			
		||||
        end
 | 
			
		||||
		if status and status ~= 'idle' then
 | 
			
		||||
			msg = string.format(
 | 
			
		||||
				'[%s] %s %s',
 | 
			
		||||
				status,
 | 
			
		||||
				time ~= 'N/A' and time or 'unknown remaining time',
 | 
			
		||||
				string.lower(status):gsub(' ', ''):gsub('\n', '') == 'charging' and ' until charged' or ' remaining'
 | 
			
		||||
			)
 | 
			
		||||
		else
 | 
			
		||||
			msg = 'On AC power'
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
        tp_smapi.hide()
 | 
			
		||||
        tp_smapi.notification = naughty.notify {
 | 
			
		||||
            title   = string.format("%s: %s %s (%s)", batid, mfgr, model, chem),
 | 
			
		||||
            text    = msg,
 | 
			
		||||
            timeout = type(seconds) == "number" and seconds or 0,
 | 
			
		||||
            screen  = scr or focused()
 | 
			
		||||
        }
 | 
			
		||||
    end
 | 
			
		||||
		tp_smapi.hide()
 | 
			
		||||
		tp_smapi.notification = naughty.notify({
 | 
			
		||||
			title = string.format('%s: %s %s (%s)', batid, mfgr, model, chem),
 | 
			
		||||
			text = msg,
 | 
			
		||||
			timeout = type(seconds) == 'number' and seconds or 0,
 | 
			
		||||
			screen = scr or focused(),
 | 
			
		||||
		})
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    function tp_smapi.create_widget(args)
 | 
			
		||||
        args            = args or {}
 | 
			
		||||
	function tp_smapi.create_widget(args)
 | 
			
		||||
		args = args or {}
 | 
			
		||||
 | 
			
		||||
        local pspath    = args.pspath or "/sys/class/power_supply/"
 | 
			
		||||
        local batteries = args.batteries or (args.battery and {args.battery}) or {}
 | 
			
		||||
        local timeout   = args.timeout or 30
 | 
			
		||||
        local settings  = args.settings or function() end
 | 
			
		||||
		local pspath = args.pspath or '/sys/class/power_supply/'
 | 
			
		||||
		local batteries = args.batteries or (args.battery and { args.battery }) or {}
 | 
			
		||||
		local timeout = args.timeout or 30
 | 
			
		||||
		local settings = args.settings or function() end
 | 
			
		||||
 | 
			
		||||
        if #batteries == 0 then
 | 
			
		||||
            helpers.line_callback("ls -1 " .. pspath, function(line)
 | 
			
		||||
                local bstr = string.match(line, "BAT%w+")
 | 
			
		||||
                if bstr then batteries[#batteries + 1] = bstr end
 | 
			
		||||
            end)
 | 
			
		||||
        end
 | 
			
		||||
		if #batteries == 0 then
 | 
			
		||||
			helpers.line_callback('ls -1 ' .. pspath, function(line)
 | 
			
		||||
				local bstr = string.match(line, 'BAT%w+')
 | 
			
		||||
				if bstr then
 | 
			
		||||
					batteries[#batteries + 1] = bstr
 | 
			
		||||
				end
 | 
			
		||||
			end)
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
        local all_batteries_installed = true
 | 
			
		||||
		local all_batteries_installed = true
 | 
			
		||||
 | 
			
		||||
        for _, battery in ipairs(batteries) do
 | 
			
		||||
            if not tp_smapi.installed(battery) then
 | 
			
		||||
                naughty.notify {
 | 
			
		||||
                    preset = naughty.config.critical,
 | 
			
		||||
                    title  = "tp_smapi: error while creating widget",
 | 
			
		||||
                    text   = string.format("battery %s is not installed", battery)
 | 
			
		||||
                }
 | 
			
		||||
                all_batteries_installed = false
 | 
			
		||||
                break
 | 
			
		||||
            end
 | 
			
		||||
        end
 | 
			
		||||
		for _, battery in ipairs(batteries) do
 | 
			
		||||
			if not tp_smapi.installed(battery) then
 | 
			
		||||
				naughty.notify({
 | 
			
		||||
					preset = naughty.config.critical,
 | 
			
		||||
					title = 'tp_smapi: error while creating widget',
 | 
			
		||||
					text = string.format('battery %s is not installed', battery),
 | 
			
		||||
				})
 | 
			
		||||
				all_batteries_installed = false
 | 
			
		||||
				break
 | 
			
		||||
			end
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
        if not all_batteries_installed then return end
 | 
			
		||||
		if not all_batteries_installed then
 | 
			
		||||
			return
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
        tpbat = {
 | 
			
		||||
            batteries = batteries,
 | 
			
		||||
            widget    = args.widget or wibox.widget.textbox()
 | 
			
		||||
        }
 | 
			
		||||
		tpbat = {
 | 
			
		||||
			batteries = batteries,
 | 
			
		||||
			widget = args.widget or wibox.widget.textbox(),
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        function tpbat.update()
 | 
			
		||||
            tpbat_now = {
 | 
			
		||||
                n_status = {},
 | 
			
		||||
                n_perc   = {},
 | 
			
		||||
                n_time   = {},
 | 
			
		||||
                status   = "N/A"
 | 
			
		||||
            }
 | 
			
		||||
		function tpbat.update()
 | 
			
		||||
			tpbat_now = {
 | 
			
		||||
				n_status = {},
 | 
			
		||||
				n_perc = {},
 | 
			
		||||
				n_time = {},
 | 
			
		||||
				status = 'N/A',
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
            for i = 1, #batteries do
 | 
			
		||||
                tpbat_now.n_status[i] = tp_smapi.status(batteries[i]) or "N/A"
 | 
			
		||||
                tpbat_now.n_perc[i] = tp_smapi.percentage(batteries[i])
 | 
			
		||||
                tpbat_now.n_time[i] = tp_smapi.time(batteries[i]) or "N/A"
 | 
			
		||||
			for i = 1, #batteries do
 | 
			
		||||
				tpbat_now.n_status[i] = tp_smapi.status(batteries[i]) or 'N/A'
 | 
			
		||||
				tpbat_now.n_perc[i] = tp_smapi.percentage(batteries[i])
 | 
			
		||||
				tpbat_now.n_time[i] = tp_smapi.time(batteries[i]) or 'N/A'
 | 
			
		||||
 | 
			
		||||
                if not tpbat_now.n_status[i]:lower():match("full") then
 | 
			
		||||
                    tpbat_now.status = tpbat_now.n_status[i]
 | 
			
		||||
                end
 | 
			
		||||
            end
 | 
			
		||||
				if not tpbat_now.n_status[i]:lower():match('full') then
 | 
			
		||||
					tpbat_now.status = tpbat_now.n_status[i]
 | 
			
		||||
				end
 | 
			
		||||
			end
 | 
			
		||||
 | 
			
		||||
            widget = tpbat.widget -- backwards compatibility
 | 
			
		||||
            settings()
 | 
			
		||||
        end
 | 
			
		||||
			widget = tpbat.widget -- backwards compatibility
 | 
			
		||||
			settings()
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
        helpers.newtimer("thinkpad-batteries", timeout, tpbat.update)
 | 
			
		||||
		helpers.newtimer('thinkpad-batteries', timeout, tpbat.update)
 | 
			
		||||
 | 
			
		||||
        return tpbat
 | 
			
		||||
    end
 | 
			
		||||
		return tpbat
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    return tp_smapi
 | 
			
		||||
	return tp_smapi
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
return factory
 | 
			
		||||
 
 | 
			
		||||
@@ -6,70 +6,69 @@
 | 
			
		||||
 | 
			
		||||
--]]
 | 
			
		||||
 | 
			
		||||
local helpers  = require("lain.helpers")
 | 
			
		||||
local wibox    = require("wibox")
 | 
			
		||||
local math     = math
 | 
			
		||||
local string   = string
 | 
			
		||||
local helpers = require('lain.helpers')
 | 
			
		||||
local wibox = require('wibox')
 | 
			
		||||
local math = math
 | 
			
		||||
local string = string
 | 
			
		||||
 | 
			
		||||
-- CPU usage
 | 
			
		||||
-- lain.widget.cpu
 | 
			
		||||
 | 
			
		||||
local function factory(args)
 | 
			
		||||
    args           = args or {}
 | 
			
		||||
	args = args or {}
 | 
			
		||||
 | 
			
		||||
    local cpu      = { core = {}, widget = args.widget or wibox.widget.textbox() }
 | 
			
		||||
    local timeout  = args.timeout or 2
 | 
			
		||||
    local settings = args.settings or function() end
 | 
			
		||||
	local cpu = { core = {}, widget = args.widget or wibox.widget.textbox() }
 | 
			
		||||
	local timeout = args.timeout or 2
 | 
			
		||||
	local settings = args.settings or function() end
 | 
			
		||||
 | 
			
		||||
    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.
 | 
			
		||||
        for index,time in pairs(helpers.lines_match("cpu","/proc/stat")) 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
 | 
			
		||||
	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.
 | 
			
		||||
		for index, time in pairs(helpers.lines_match('cpu', '/proc/stat')) 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
 | 
			
		||||
                -- 4 = idle, 5 = ioWait. Essentially, the CPUs have done
 | 
			
		||||
                -- nothing during these times.
 | 
			
		||||
                if at == 4 or at == 5 then
 | 
			
		||||
                    idle = idle + field
 | 
			
		||||
                end
 | 
			
		||||
                total = total + field
 | 
			
		||||
                at = at + 1
 | 
			
		||||
            end
 | 
			
		||||
			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
 | 
			
		||||
					idle = idle + field
 | 
			
		||||
				end
 | 
			
		||||
				total = total + field
 | 
			
		||||
				at = at + 1
 | 
			
		||||
			end
 | 
			
		||||
 | 
			
		||||
            local active = total - idle
 | 
			
		||||
			local active = total - idle
 | 
			
		||||
 | 
			
		||||
            if core.last_active ~= active or core.last_total ~= total then
 | 
			
		||||
                -- Read current data and calculate relative values.
 | 
			
		||||
                local dactive = active - core.last_active
 | 
			
		||||
                local dtotal  = total - core.last_total
 | 
			
		||||
                local usage   = math.ceil(math.abs((dactive / dtotal) * 100))
 | 
			
		||||
			if core.last_active ~= active or core.last_total ~= total then
 | 
			
		||||
				-- Read current data and calculate relative values.
 | 
			
		||||
				local dactive = active - core.last_active
 | 
			
		||||
				local dtotal = total - core.last_total
 | 
			
		||||
				local usage = math.ceil(math.abs((dactive / dtotal) * 100))
 | 
			
		||||
 | 
			
		||||
                core.last_active = active
 | 
			
		||||
                core.last_total  = total
 | 
			
		||||
                core.usage       = usage
 | 
			
		||||
				core.last_active = active
 | 
			
		||||
				core.last_total = total
 | 
			
		||||
				core.usage = usage
 | 
			
		||||
 | 
			
		||||
                -- Save current data for the next run.
 | 
			
		||||
                cpu.core[coreid] = core
 | 
			
		||||
            end
 | 
			
		||||
        end
 | 
			
		||||
				-- Save current data for the next run.
 | 
			
		||||
				cpu.core[coreid] = core
 | 
			
		||||
			end
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
        cpu_now = cpu.core
 | 
			
		||||
        cpu_now.usage = cpu_now[0].usage
 | 
			
		||||
        widget = cpu.widget
 | 
			
		||||
		cpu_now = cpu.core
 | 
			
		||||
		cpu_now.usage = cpu_now[0].usage
 | 
			
		||||
		widget = cpu.widget
 | 
			
		||||
 | 
			
		||||
        settings()
 | 
			
		||||
    end
 | 
			
		||||
		settings()
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    helpers.newtimer("cpu", timeout, cpu.update)
 | 
			
		||||
	helpers.newtimer('cpu', timeout, cpu.update)
 | 
			
		||||
 | 
			
		||||
    return cpu
 | 
			
		||||
	return cpu
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
return factory
 | 
			
		||||
 
 | 
			
		||||
@@ -7,150 +7,168 @@
 | 
			
		||||
 | 
			
		||||
--]]
 | 
			
		||||
 | 
			
		||||
local helpers    = require("lain.helpers")
 | 
			
		||||
local Gio        = require("lgi").Gio
 | 
			
		||||
local focused    = require("awful.screen").focused
 | 
			
		||||
local wibox      = require("wibox")
 | 
			
		||||
local naughty    = require("naughty")
 | 
			
		||||
local gears      = require("gears")
 | 
			
		||||
local math       = math
 | 
			
		||||
local string     = string
 | 
			
		||||
local tconcat    = table.concat
 | 
			
		||||
local type       = type
 | 
			
		||||
local helpers = require('lain.helpers')
 | 
			
		||||
local Gio = require('lgi').Gio
 | 
			
		||||
local focused = require('awful.screen').focused
 | 
			
		||||
local wibox = require('wibox')
 | 
			
		||||
local naughty = require('naughty')
 | 
			
		||||
local gears = require('gears')
 | 
			
		||||
local math = math
 | 
			
		||||
local string = string
 | 
			
		||||
local tconcat = table.concat
 | 
			
		||||
local type = type
 | 
			
		||||
local query_size = Gio.FILE_ATTRIBUTE_FILESYSTEM_SIZE
 | 
			
		||||
local query_free = Gio.FILE_ATTRIBUTE_FILESYSTEM_FREE
 | 
			
		||||
local query_used = Gio.FILE_ATTRIBUTE_FILESYSTEM_USED
 | 
			
		||||
local query      = query_size .. "," .. query_free .. "," .. query_used
 | 
			
		||||
local query = query_size .. ',' .. query_free .. ',' .. query_used
 | 
			
		||||
 | 
			
		||||
-- File systems info
 | 
			
		||||
-- lain.widget.fs
 | 
			
		||||
 | 
			
		||||
local function factory(args)
 | 
			
		||||
    args     = args or {}
 | 
			
		||||
	args = args or {}
 | 
			
		||||
 | 
			
		||||
    local fs = {
 | 
			
		||||
        widget = args.widget or wibox.widget.textbox(),
 | 
			
		||||
        units = {
 | 
			
		||||
            [1] = "Kb", [2] = "Mb", [3] = "Gb",
 | 
			
		||||
            [4] = "Tb", [5] = "Pb", [6] = "Eb",
 | 
			
		||||
            [7] = "Zb", [8] = "Yb"
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
	local fs = {
 | 
			
		||||
		widget = args.widget or wibox.widget.textbox(),
 | 
			
		||||
		units = {
 | 
			
		||||
			[1] = 'Kb',
 | 
			
		||||
			[2] = 'Mb',
 | 
			
		||||
			[3] = 'Gb',
 | 
			
		||||
			[4] = 'Tb',
 | 
			
		||||
			[5] = 'Pb',
 | 
			
		||||
			[6] = 'Eb',
 | 
			
		||||
			[7] = 'Zb',
 | 
			
		||||
			[8] = 'Yb',
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    function fs.hide()
 | 
			
		||||
        if not fs.notification then return end
 | 
			
		||||
        naughty.destroy(fs.notification)
 | 
			
		||||
        fs.notification = nil
 | 
			
		||||
    end
 | 
			
		||||
	function fs.hide()
 | 
			
		||||
		if not fs.notification then
 | 
			
		||||
			return
 | 
			
		||||
		end
 | 
			
		||||
		naughty.destroy(fs.notification)
 | 
			
		||||
		fs.notification = nil
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    function fs.show(seconds, scr)
 | 
			
		||||
        fs.hide()
 | 
			
		||||
        fs.update(function()
 | 
			
		||||
            fs.notification_preset.screen = fs.followtag and focused() or scr or 1
 | 
			
		||||
            fs.notification = naughty.notify {
 | 
			
		||||
                preset  = fs.notification_preset,
 | 
			
		||||
                timeout = type(seconds) == "number" and seconds or 5
 | 
			
		||||
            }
 | 
			
		||||
        end)
 | 
			
		||||
    end
 | 
			
		||||
	function fs.show(seconds, scr)
 | 
			
		||||
		fs.hide()
 | 
			
		||||
		fs.update(function()
 | 
			
		||||
			fs.notification_preset.screen = fs.followtag and focused() or scr or 1
 | 
			
		||||
			fs.notification = naughty.notify({
 | 
			
		||||
				preset = fs.notification_preset,
 | 
			
		||||
				timeout = type(seconds) == 'number' and seconds or 5,
 | 
			
		||||
			})
 | 
			
		||||
		end)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    local timeout   = args.timeout or 600
 | 
			
		||||
    local partition = args.partition
 | 
			
		||||
    local threshold = args.threshold or 99
 | 
			
		||||
    local showpopup = args.showpopup or "on"
 | 
			
		||||
    local settings  = args.settings or function() end
 | 
			
		||||
	local timeout = args.timeout or 600
 | 
			
		||||
	local partition = args.partition
 | 
			
		||||
	local threshold = args.threshold or 99
 | 
			
		||||
	local showpopup = args.showpopup or 'on'
 | 
			
		||||
	local settings = args.settings or function() end
 | 
			
		||||
 | 
			
		||||
    fs.followtag           = args.followtag or false
 | 
			
		||||
    fs.notification_preset = args.notification_preset
 | 
			
		||||
	fs.followtag = args.followtag or false
 | 
			
		||||
	fs.notification_preset = args.notification_preset
 | 
			
		||||
 | 
			
		||||
    if not fs.notification_preset then
 | 
			
		||||
        fs.notification_preset = {
 | 
			
		||||
            font = "Monospace 10",
 | 
			
		||||
            fg   = "#FFFFFF",
 | 
			
		||||
            bg   = "#000000"
 | 
			
		||||
        }
 | 
			
		||||
    end
 | 
			
		||||
	if not fs.notification_preset then
 | 
			
		||||
		fs.notification_preset = {
 | 
			
		||||
			font = 'Monospace 10',
 | 
			
		||||
			fg = '#FFFFFF',
 | 
			
		||||
			bg = '#000000',
 | 
			
		||||
		}
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    local function update_synced()
 | 
			
		||||
        local pathlen = 10
 | 
			
		||||
        fs_now = {}
 | 
			
		||||
	local function update_synced()
 | 
			
		||||
		local pathlen = 10
 | 
			
		||||
		fs_now = {}
 | 
			
		||||
 | 
			
		||||
        local notifypaths = {}
 | 
			
		||||
        for _, mount in ipairs(Gio.unix_mounts_get()) do
 | 
			
		||||
            local path = Gio.unix_mount_get_mount_path(mount)
 | 
			
		||||
            local root = Gio.File.new_for_path(path)
 | 
			
		||||
            local info = root:query_filesystem_info(query)
 | 
			
		||||
		local notifypaths = {}
 | 
			
		||||
		for _, mount in ipairs(Gio.unix_mounts_get()) do
 | 
			
		||||
			local path = Gio.unix_mount_get_mount_path(mount)
 | 
			
		||||
			local root = Gio.File.new_for_path(path)
 | 
			
		||||
			local info = root:query_filesystem_info(query)
 | 
			
		||||
 | 
			
		||||
            if info then
 | 
			
		||||
                local size = info:get_attribute_uint64(query_size)
 | 
			
		||||
                local used = info:get_attribute_uint64(query_used)
 | 
			
		||||
                local free = info:get_attribute_uint64(query_free)
 | 
			
		||||
			if info then
 | 
			
		||||
				local size = info:get_attribute_uint64(query_size)
 | 
			
		||||
				local used = info:get_attribute_uint64(query_used)
 | 
			
		||||
				local free = info:get_attribute_uint64(query_free)
 | 
			
		||||
 | 
			
		||||
                if size > 0 then
 | 
			
		||||
                    local units = math.floor(math.log(size)/math.log(1024))
 | 
			
		||||
				if size > 0 then
 | 
			
		||||
					local units = math.floor(math.log(size) / math.log(1024))
 | 
			
		||||
 | 
			
		||||
                    fs_now[path] = {
 | 
			
		||||
                        units      = fs.units[units],
 | 
			
		||||
                        percentage = math.floor(100 * used / size), -- used percentage
 | 
			
		||||
                        size       = size / math.pow(1024, units),
 | 
			
		||||
                        used       = used / math.pow(1024, units),
 | 
			
		||||
                        free       = free / math.pow(1024, units)
 | 
			
		||||
                    }
 | 
			
		||||
					fs_now[path] = {
 | 
			
		||||
						units = fs.units[units],
 | 
			
		||||
						percentage = math.floor(100 * used / size), -- used percentage
 | 
			
		||||
						size = size / math.pow(1024, units),
 | 
			
		||||
						used = used / math.pow(1024, units),
 | 
			
		||||
						free = free / math.pow(1024, units),
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
                    if fs_now[path].percentage > 0 then -- don't notify unused file systems
 | 
			
		||||
                        notifypaths[#notifypaths+1] = path
 | 
			
		||||
					if fs_now[path].percentage > 0 then -- don't notify unused file systems
 | 
			
		||||
						notifypaths[#notifypaths + 1] = path
 | 
			
		||||
 | 
			
		||||
                        if #path > pathlen then
 | 
			
		||||
                            pathlen = #path
 | 
			
		||||
                        end
 | 
			
		||||
                    end
 | 
			
		||||
                end
 | 
			
		||||
            end
 | 
			
		||||
        end
 | 
			
		||||
						if #path > pathlen then
 | 
			
		||||
							pathlen = #path
 | 
			
		||||
						end
 | 
			
		||||
					end
 | 
			
		||||
				end
 | 
			
		||||
			end
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
        widget = fs.widget
 | 
			
		||||
        settings()
 | 
			
		||||
		widget = fs.widget
 | 
			
		||||
		settings()
 | 
			
		||||
 | 
			
		||||
        if partition and fs_now[partition] and fs_now[partition].percentage >= threshold then
 | 
			
		||||
            if not helpers.get_map(partition) then
 | 
			
		||||
                naughty.notify {
 | 
			
		||||
                    preset = naughty.config.presets.critical,
 | 
			
		||||
                    title  = "Warning",
 | 
			
		||||
                    text   = string.format("%s is above %d%% (%d%%)", partition, threshold, fs_now[partition].percentage)
 | 
			
		||||
                }
 | 
			
		||||
                helpers.set_map(partition, true)
 | 
			
		||||
            else
 | 
			
		||||
                helpers.set_map(partition, false)
 | 
			
		||||
            end
 | 
			
		||||
        end
 | 
			
		||||
		if partition and fs_now[partition] and fs_now[partition].percentage >= threshold then
 | 
			
		||||
			if not helpers.get_map(partition) then
 | 
			
		||||
				naughty.notify({
 | 
			
		||||
					preset = naughty.config.presets.critical,
 | 
			
		||||
					title = 'Warning',
 | 
			
		||||
					text = string.format('%s is above %d%% (%d%%)', partition, threshold, fs_now[partition].percentage),
 | 
			
		||||
				})
 | 
			
		||||
				helpers.set_map(partition, true)
 | 
			
		||||
			else
 | 
			
		||||
				helpers.set_map(partition, false)
 | 
			
		||||
			end
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
        local fmt = "%-" .. tostring(pathlen) .. "s %4s\t%6s\t%6s\n"
 | 
			
		||||
        local notifytable = { [1] = string.format(fmt, "path", "used", "free", "size") }
 | 
			
		||||
        fmt = "\n%-" .. tostring(pathlen) .. "s %3s%%\t%6.2f\t%6.2f %s"
 | 
			
		||||
        for _, path in ipairs(notifypaths) do
 | 
			
		||||
            notifytable[#notifytable+1] = string.format(fmt, path, fs_now[path].percentage, fs_now[path].free, fs_now[path].size, fs_now[path].units)
 | 
			
		||||
        end
 | 
			
		||||
		local fmt = '%-' .. tostring(pathlen) .. 's %4s\t%6s\t%6s\n'
 | 
			
		||||
		local notifytable = { [1] = string.format(fmt, 'path', 'used', 'free', 'size') }
 | 
			
		||||
		fmt = '\n%-' .. tostring(pathlen) .. 's %3s%%\t%6.2f\t%6.2f %s'
 | 
			
		||||
		for _, path in ipairs(notifypaths) do
 | 
			
		||||
			notifytable[#notifytable + 1] = string.format(
 | 
			
		||||
				fmt,
 | 
			
		||||
				path,
 | 
			
		||||
				fs_now[path].percentage,
 | 
			
		||||
				fs_now[path].free,
 | 
			
		||||
				fs_now[path].size,
 | 
			
		||||
				fs_now[path].units
 | 
			
		||||
			)
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
        fs.notification_preset.text = tconcat(notifytable)
 | 
			
		||||
    end
 | 
			
		||||
		fs.notification_preset.text = tconcat(notifytable)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    function fs.update(callback)
 | 
			
		||||
        Gio.Async.start(gears.protected_call.call)(function()
 | 
			
		||||
            update_synced()
 | 
			
		||||
            if type(callback) == "function" and callback then
 | 
			
		||||
                callback()
 | 
			
		||||
            end
 | 
			
		||||
        end)
 | 
			
		||||
    end
 | 
			
		||||
	function fs.update(callback)
 | 
			
		||||
		Gio.Async.start(gears.protected_call.call)(function()
 | 
			
		||||
			update_synced()
 | 
			
		||||
			if type(callback) == 'function' and callback then
 | 
			
		||||
				callback()
 | 
			
		||||
			end
 | 
			
		||||
		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)
 | 
			
		||||
    end
 | 
			
		||||
	if showpopup == 'on' then
 | 
			
		||||
		fs.widget:connect_signal('mouse::enter', function()
 | 
			
		||||
			fs.show(0)
 | 
			
		||||
		end)
 | 
			
		||||
		fs.widget:connect_signal('mouse::leave', function()
 | 
			
		||||
			fs.hide()
 | 
			
		||||
		end)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    helpers.newtimer(partition or "fs", timeout, fs.update)
 | 
			
		||||
	helpers.newtimer(partition or 'fs', timeout, fs.update)
 | 
			
		||||
 | 
			
		||||
    return fs
 | 
			
		||||
	return fs
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
return factory
 | 
			
		||||
 
 | 
			
		||||
@@ -5,90 +5,111 @@
 | 
			
		||||
 | 
			
		||||
--]]
 | 
			
		||||
 | 
			
		||||
local helpers  = require("lain.helpers")
 | 
			
		||||
local naughty  = require("naughty")
 | 
			
		||||
local wibox    = require("wibox")
 | 
			
		||||
local awful    = require("awful")
 | 
			
		||||
local string   = string
 | 
			
		||||
local type     = type
 | 
			
		||||
local helpers = require('lain.helpers')
 | 
			
		||||
local naughty = require('naughty')
 | 
			
		||||
local wibox = require('wibox')
 | 
			
		||||
local awful = require('awful')
 | 
			
		||||
local string = string
 | 
			
		||||
local type = type
 | 
			
		||||
local tonumber = tonumber
 | 
			
		||||
 | 
			
		||||
-- Mail IMAP check
 | 
			
		||||
-- lain.widget.imap
 | 
			
		||||
 | 
			
		||||
local function factory(args)
 | 
			
		||||
    args             = args or {}
 | 
			
		||||
	args = args or {}
 | 
			
		||||
 | 
			
		||||
    local imap       = { widget = args.widget or wibox.widget.textbox() }
 | 
			
		||||
    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 pwdtimeout = args.pwdtimeout or 10
 | 
			
		||||
    local is_plain   = args.is_plain or false
 | 
			
		||||
    local followtag  = args.followtag or false
 | 
			
		||||
    local notify     = args.notify or "on"
 | 
			
		||||
    local settings   = args.settings or function() end
 | 
			
		||||
	local imap = { widget = args.widget or wibox.widget.textbox() }
 | 
			
		||||
	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 pwdtimeout = args.pwdtimeout or 10
 | 
			
		||||
	local is_plain = args.is_plain or false
 | 
			
		||||
	local followtag = args.followtag or false
 | 
			
		||||
	local notify = args.notify or 'on'
 | 
			
		||||
	local settings = args.settings or function() end
 | 
			
		||||
 | 
			
		||||
    local head_command = "curl --connect-timeout 3 -fsm 3"
 | 
			
		||||
    local request = "-X 'STATUS INBOX (MESSAGES RECENT UNSEEN)'"
 | 
			
		||||
	local head_command = 'curl --connect-timeout 3 -fsm 3'
 | 
			
		||||
	local request = "-X 'STATUS INBOX (MESSAGES RECENT UNSEEN)'"
 | 
			
		||||
 | 
			
		||||
    if not server or not mail or not password then return end
 | 
			
		||||
	if not server or not mail or not password then
 | 
			
		||||
		return
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    mail_notification_preset = {
 | 
			
		||||
        icon     = helpers.icons_dir .. "mail.png",
 | 
			
		||||
        position = "top_left"
 | 
			
		||||
    }
 | 
			
		||||
	mail_notification_preset = {
 | 
			
		||||
		icon = helpers.icons_dir .. 'mail.png',
 | 
			
		||||
		position = 'top_left',
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    helpers.set_map(mail, 0)
 | 
			
		||||
	helpers.set_map(mail, 0)
 | 
			
		||||
 | 
			
		||||
    if not is_plain then
 | 
			
		||||
        if type(password) == "string" or type(password) == "table" then
 | 
			
		||||
            helpers.async(password, function(f) password = f:gsub("\n", "") end)
 | 
			
		||||
        elseif type(password) == "function" then
 | 
			
		||||
            imap.pwdtimer = helpers.newtimer(mail .. "-password", pwdtimeout, function()
 | 
			
		||||
                local retrieved_password, try_again = password()
 | 
			
		||||
                if not try_again then
 | 
			
		||||
                    imap.pwdtimer:stop() -- stop trying to retrieve
 | 
			
		||||
                    password = retrieved_password or "" -- failsafe
 | 
			
		||||
                end
 | 
			
		||||
            end, true, true)
 | 
			
		||||
        end
 | 
			
		||||
    end
 | 
			
		||||
	if not is_plain then
 | 
			
		||||
		if type(password) == 'string' or type(password) == 'table' then
 | 
			
		||||
			helpers.async(password, function(f)
 | 
			
		||||
				password = f:gsub('\n', '')
 | 
			
		||||
			end)
 | 
			
		||||
		elseif type(password) == 'function' then
 | 
			
		||||
			imap.pwdtimer = helpers.newtimer(mail .. '-password', pwdtimeout, function()
 | 
			
		||||
				local retrieved_password, try_again = password()
 | 
			
		||||
				if not try_again then
 | 
			
		||||
					imap.pwdtimer:stop() -- stop trying to retrieve
 | 
			
		||||
					password = retrieved_password or '' -- failsafe
 | 
			
		||||
				end
 | 
			
		||||
			end, true, true)
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    function imap.update()
 | 
			
		||||
        -- do not update if the password has not been retrieved yet
 | 
			
		||||
        if type(password) ~= "string" then return end
 | 
			
		||||
	function imap.update()
 | 
			
		||||
		-- do not update if the password has not been retrieved yet
 | 
			
		||||
		if type(password) ~= 'string' then
 | 
			
		||||
			return
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
        local curl = string.format("%s --url imaps://%s:%s/INBOX -u %s:'%s' %s -k",
 | 
			
		||||
                     head_command, server, port, mail, password, request)
 | 
			
		||||
		local curl = string.format(
 | 
			
		||||
			"%s --url imaps://%s:%s/INBOX -u %s:'%s' %s -k",
 | 
			
		||||
			head_command,
 | 
			
		||||
			server,
 | 
			
		||||
			port,
 | 
			
		||||
			mail,
 | 
			
		||||
			password,
 | 
			
		||||
			request
 | 
			
		||||
		)
 | 
			
		||||
 | 
			
		||||
        helpers.async(curl, function(f)
 | 
			
		||||
            imap_now = { ["MESSAGES"] = 0, ["RECENT"] = 0, ["UNSEEN"] = 0 }
 | 
			
		||||
		helpers.async(curl, function(f)
 | 
			
		||||
			imap_now = { ['MESSAGES'] = 0, ['RECENT'] = 0, ['UNSEEN'] = 0 }
 | 
			
		||||
 | 
			
		||||
            for s,d in f:gmatch("(%w+)%s+(%d+)") do imap_now[s] = tonumber(d) end
 | 
			
		||||
            mailcount = imap_now["UNSEEN"] -- backwards compatibility
 | 
			
		||||
            widget = imap.widget
 | 
			
		||||
			for s, d in f:gmatch('(%w+)%s+(%d+)') do
 | 
			
		||||
				imap_now[s] = tonumber(d)
 | 
			
		||||
			end
 | 
			
		||||
			mailcount = imap_now['UNSEEN'] -- backwards compatibility
 | 
			
		||||
			widget = imap.widget
 | 
			
		||||
 | 
			
		||||
            settings()
 | 
			
		||||
			settings()
 | 
			
		||||
 | 
			
		||||
            if notify == "on" and mailcount and mailcount >= 1 and mailcount > helpers.get_map(mail) then
 | 
			
		||||
                if followtag then mail_notification_preset.screen = awful.screen.focused() end
 | 
			
		||||
                naughty.notify {
 | 
			
		||||
                    preset = mail_notification_preset,
 | 
			
		||||
                    text   = string.format("%s has <b>%d</b> new message%s", mail, mailcount, mailcount == 1 and "" or "s")
 | 
			
		||||
                }
 | 
			
		||||
            end
 | 
			
		||||
			if notify == 'on' and mailcount and mailcount >= 1 and mailcount > helpers.get_map(mail) then
 | 
			
		||||
				if followtag then
 | 
			
		||||
					mail_notification_preset.screen = awful.screen.focused()
 | 
			
		||||
				end
 | 
			
		||||
				naughty.notify({
 | 
			
		||||
					preset = mail_notification_preset,
 | 
			
		||||
					text = string.format(
 | 
			
		||||
						'%s has <b>%d</b> new message%s',
 | 
			
		||||
						mail,
 | 
			
		||||
						mailcount,
 | 
			
		||||
						mailcount == 1 and '' or 's'
 | 
			
		||||
					),
 | 
			
		||||
				})
 | 
			
		||||
			end
 | 
			
		||||
 | 
			
		||||
            helpers.set_map(mail, imap_now["UNSEEN"])
 | 
			
		||||
        end)
 | 
			
		||||
			helpers.set_map(mail, imap_now['UNSEEN'])
 | 
			
		||||
		end)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    end
 | 
			
		||||
	imap.timer = helpers.newtimer(mail, timeout, imap.update, true, true)
 | 
			
		||||
 | 
			
		||||
    imap.timer = helpers.newtimer(mail, timeout, imap.update, true, true)
 | 
			
		||||
 | 
			
		||||
    return imap
 | 
			
		||||
	return imap
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
return factory
 | 
			
		||||
 
 | 
			
		||||
@@ -11,9 +11,9 @@
 | 
			
		||||
 | 
			
		||||
--]]
 | 
			
		||||
 | 
			
		||||
local wrequire     = require("lain.helpers").wrequire
 | 
			
		||||
local wrequire = require('lain.helpers').wrequire
 | 
			
		||||
local setmetatable = setmetatable
 | 
			
		||||
 | 
			
		||||
local widget = { _NAME = "lain.widget" }
 | 
			
		||||
local widget = { _NAME = 'lain.widget' }
 | 
			
		||||
 | 
			
		||||
return setmetatable(widget, { __index = wrequire })
 | 
			
		||||
 
 | 
			
		||||
@@ -6,46 +6,53 @@
 | 
			
		||||
 | 
			
		||||
--]]
 | 
			
		||||
 | 
			
		||||
local helpers              = require("lain.helpers")
 | 
			
		||||
local wibox                = require("wibox")
 | 
			
		||||
local helpers = require('lain.helpers')
 | 
			
		||||
local wibox = require('wibox')
 | 
			
		||||
local gmatch, lines, floor = string.gmatch, io.lines, math.floor
 | 
			
		||||
 | 
			
		||||
-- Memory usage (ignoring caches)
 | 
			
		||||
-- lain.widget.mem
 | 
			
		||||
 | 
			
		||||
local function factory(args)
 | 
			
		||||
    args           = args or {}
 | 
			
		||||
	args = args or {}
 | 
			
		||||
 | 
			
		||||
    local mem      = { widget = args.widget or wibox.widget.textbox() }
 | 
			
		||||
    local timeout  = args.timeout or 2
 | 
			
		||||
    local settings = args.settings or function() end
 | 
			
		||||
	local mem = { widget = args.widget or wibox.widget.textbox() }
 | 
			
		||||
	local timeout = args.timeout or 2
 | 
			
		||||
	local settings = args.settings or function() end
 | 
			
		||||
 | 
			
		||||
    function mem.update()
 | 
			
		||||
        mem_now = {}
 | 
			
		||||
        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
 | 
			
		||||
	function mem.update()
 | 
			
		||||
		mem_now = {}
 | 
			
		||||
		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.srec
 | 
			
		||||
        mem_now.swapused = mem_now.swap - mem_now.swapf
 | 
			
		||||
        mem_now.perc = math.floor(mem_now.used / mem_now.total * 100)
 | 
			
		||||
		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)
 | 
			
		||||
 | 
			
		||||
        widget = mem.widget
 | 
			
		||||
        settings()
 | 
			
		||||
    end
 | 
			
		||||
		widget = mem.widget
 | 
			
		||||
		settings()
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    helpers.newtimer("mem", timeout, mem.update)
 | 
			
		||||
	helpers.newtimer('mem', timeout, mem.update)
 | 
			
		||||
 | 
			
		||||
    return mem
 | 
			
		||||
	return mem
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
return factory
 | 
			
		||||
 
 | 
			
		||||
@@ -6,130 +6,154 @@
 | 
			
		||||
 | 
			
		||||
--]]
 | 
			
		||||
 | 
			
		||||
local helpers  = require("lain.helpers")
 | 
			
		||||
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       = os
 | 
			
		||||
local string   = string
 | 
			
		||||
local helpers = require('lain.helpers')
 | 
			
		||||
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 = os
 | 
			
		||||
local string = string
 | 
			
		||||
 | 
			
		||||
-- MPD infos
 | 
			
		||||
-- lain.widget.mpd
 | 
			
		||||
 | 
			
		||||
local function factory(args)
 | 
			
		||||
    args                = args or {}
 | 
			
		||||
	args = args or {}
 | 
			
		||||
 | 
			
		||||
    local mpd           = { widget = args.widget or wibox.widget.textbox() }
 | 
			
		||||
    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 os.getenv("MPD_HOST") or "127.0.0.1"
 | 
			
		||||
    local port          = args.port or os.getenv("MPD_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 mpd = { widget = args.widget or wibox.widget.textbox() }
 | 
			
		||||
	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 os.getenv('MPD_HOST') or '127.0.0.1'
 | 
			
		||||
	local port = args.port or os.getenv('MPD_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)
 | 
			
		||||
	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_notification_preset = { title = "Now playing", timeout = 6 }
 | 
			
		||||
	mpd_notification_preset = { title = 'Now playing', timeout = 6 }
 | 
			
		||||
 | 
			
		||||
    helpers.set_map("current mpd track", nil)
 | 
			
		||||
	helpers.set_map('current mpd track', nil)
 | 
			
		||||
 | 
			
		||||
    function mpd.update()
 | 
			
		||||
        helpers.async({ shell, "-c", cmd }, function(f)
 | 
			
		||||
            mpd_now = {
 | 
			
		||||
                random_mode  = false,
 | 
			
		||||
                single_mode  = false,
 | 
			
		||||
                repeat_mode  = false,
 | 
			
		||||
                consume_mode = false,
 | 
			
		||||
                pls_pos      = "N/A",
 | 
			
		||||
                pls_len      = "N/A",
 | 
			
		||||
                state        = "N/A",
 | 
			
		||||
                file         = "N/A",
 | 
			
		||||
                name         = "N/A",
 | 
			
		||||
                artist       = "N/A",
 | 
			
		||||
                title        = "N/A",
 | 
			
		||||
                album        = "N/A",
 | 
			
		||||
                genre        = "N/A",
 | 
			
		||||
                track        = "N/A",
 | 
			
		||||
                date         = "N/A",
 | 
			
		||||
                time         = "N/A",
 | 
			
		||||
                elapsed      = "N/A",
 | 
			
		||||
                volume       = "N/A"
 | 
			
		||||
            }
 | 
			
		||||
	function mpd.update()
 | 
			
		||||
		helpers.async({ shell, '-c', cmd }, function(f)
 | 
			
		||||
			mpd_now = {
 | 
			
		||||
				random_mode = false,
 | 
			
		||||
				single_mode = false,
 | 
			
		||||
				repeat_mode = false,
 | 
			
		||||
				consume_mode = false,
 | 
			
		||||
				pls_pos = 'N/A',
 | 
			
		||||
				pls_len = 'N/A',
 | 
			
		||||
				state = 'N/A',
 | 
			
		||||
				file = 'N/A',
 | 
			
		||||
				name = 'N/A',
 | 
			
		||||
				artist = 'N/A',
 | 
			
		||||
				title = 'N/A',
 | 
			
		||||
				album = 'N/A',
 | 
			
		||||
				genre = 'N/A',
 | 
			
		||||
				track = 'N/A',
 | 
			
		||||
				date = 'N/A',
 | 
			
		||||
				time = 'N/A',
 | 
			
		||||
				elapsed = 'N/A',
 | 
			
		||||
				volume = 'N/A',
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
            for line in string.gmatch(f, "[^\n]+") do
 | 
			
		||||
                for k, v in string.gmatch(line, "([%w]+):[%s](.*)$") do
 | 
			
		||||
                    if     k == "state"          then mpd_now.state        = v
 | 
			
		||||
                    elseif k == "file"           then mpd_now.file         = v
 | 
			
		||||
                    elseif k == "Name"           then mpd_now.name         = escape_f(v)
 | 
			
		||||
                    elseif k == "Artist"         then mpd_now.artist       = escape_f(v)
 | 
			
		||||
                    elseif k == "Title"          then mpd_now.title        = escape_f(v)
 | 
			
		||||
                    elseif k == "Album"          then mpd_now.album        = escape_f(v)
 | 
			
		||||
                    elseif k == "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+")
 | 
			
		||||
                    elseif k == "song"           then mpd_now.pls_pos      = v
 | 
			
		||||
                    elseif k == "playlistlength" then mpd_now.pls_len      = v
 | 
			
		||||
                    elseif k == "repeat"         then mpd_now.repeat_mode  = v ~= "0"
 | 
			
		||||
                    elseif k == "single"         then mpd_now.single_mode  = v ~= "0"
 | 
			
		||||
                    elseif k == "random"         then mpd_now.random_mode  = v ~= "0"
 | 
			
		||||
                    elseif k == "consume"        then mpd_now.consume_mode = v ~= "0"
 | 
			
		||||
                    elseif k == "volume"         then mpd_now.volume       = v
 | 
			
		||||
                    end
 | 
			
		||||
                end
 | 
			
		||||
            end
 | 
			
		||||
			for line in string.gmatch(f, '[^\n]+') do
 | 
			
		||||
				for k, v in string.gmatch(line, '([%w]+):[%s](.*)$') do
 | 
			
		||||
					if k == 'state' then
 | 
			
		||||
						mpd_now.state = v
 | 
			
		||||
					elseif k == 'file' then
 | 
			
		||||
						mpd_now.file = v
 | 
			
		||||
					elseif k == 'Name' then
 | 
			
		||||
						mpd_now.name = escape_f(v)
 | 
			
		||||
					elseif k == 'Artist' then
 | 
			
		||||
						mpd_now.artist = escape_f(v)
 | 
			
		||||
					elseif k == 'Title' then
 | 
			
		||||
						mpd_now.title = escape_f(v)
 | 
			
		||||
					elseif k == 'Album' then
 | 
			
		||||
						mpd_now.album = escape_f(v)
 | 
			
		||||
					elseif k == '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+')
 | 
			
		||||
					elseif k == 'song' then
 | 
			
		||||
						mpd_now.pls_pos = v
 | 
			
		||||
					elseif k == 'playlistlength' then
 | 
			
		||||
						mpd_now.pls_len = v
 | 
			
		||||
					elseif k == 'repeat' then
 | 
			
		||||
						mpd_now.repeat_mode = v ~= '0'
 | 
			
		||||
					elseif k == 'single' then
 | 
			
		||||
						mpd_now.single_mode = v ~= '0'
 | 
			
		||||
					elseif k == 'random' then
 | 
			
		||||
						mpd_now.random_mode = v ~= '0'
 | 
			
		||||
					elseif k == 'consume' then
 | 
			
		||||
						mpd_now.consume_mode = v ~= '0'
 | 
			
		||||
					elseif k == 'volume' then
 | 
			
		||||
						mpd_now.volume = v
 | 
			
		||||
					end
 | 
			
		||||
				end
 | 
			
		||||
			end
 | 
			
		||||
 | 
			
		||||
            mpd_notification_preset.text = string.format("%s (%s) - %s\n%s", mpd_now.artist,
 | 
			
		||||
                                           mpd_now.album, mpd_now.date, mpd_now.title)
 | 
			
		||||
            widget = mpd.widget
 | 
			
		||||
            settings()
 | 
			
		||||
			mpd_notification_preset.text =
 | 
			
		||||
				string.format('%s (%s) - %s\n%s', mpd_now.artist, mpd_now.album, mpd_now.date, mpd_now.title)
 | 
			
		||||
			widget = mpd.widget
 | 
			
		||||
			settings()
 | 
			
		||||
 | 
			
		||||
            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 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 followtag then mpd_notification_preset.screen = focused() end
 | 
			
		||||
					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
 | 
			
		||||
                    }
 | 
			
		||||
					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
 | 
			
		||||
					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
 | 
			
		||||
				end
 | 
			
		||||
			elseif mpd_now.state ~= 'pause' then
 | 
			
		||||
				helpers.set_map('current mpd track', nil)
 | 
			
		||||
			end
 | 
			
		||||
		end)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
                end
 | 
			
		||||
            elseif mpd_now.state ~= "pause" then
 | 
			
		||||
                helpers.set_map("current mpd track", nil)
 | 
			
		||||
            end
 | 
			
		||||
        end)
 | 
			
		||||
    end
 | 
			
		||||
	mpd.timer = helpers.newtimer('mpd', timeout, mpd.update, true, true)
 | 
			
		||||
 | 
			
		||||
    mpd.timer = helpers.newtimer("mpd", timeout, mpd.update, true, true)
 | 
			
		||||
 | 
			
		||||
    return mpd
 | 
			
		||||
	return mpd
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
return factory
 | 
			
		||||
 
 | 
			
		||||
@@ -6,117 +6,128 @@
 | 
			
		||||
 | 
			
		||||
--]]
 | 
			
		||||
 | 
			
		||||
local helpers = require("lain.helpers")
 | 
			
		||||
local naughty = require("naughty")
 | 
			
		||||
local wibox   = require("wibox")
 | 
			
		||||
local string  = string
 | 
			
		||||
local helpers = require('lain.helpers')
 | 
			
		||||
local naughty = require('naughty')
 | 
			
		||||
local wibox = require('wibox')
 | 
			
		||||
local string = string
 | 
			
		||||
 | 
			
		||||
-- Network infos
 | 
			
		||||
-- lain.widget.net
 | 
			
		||||
 | 
			
		||||
local function factory(args)
 | 
			
		||||
    args             = args or {}
 | 
			
		||||
	args = args or {}
 | 
			
		||||
 | 
			
		||||
    local net        = { widget = args.widget or wibox.widget.textbox(), devices = {} }
 | 
			
		||||
    local timeout    = args.timeout or 2
 | 
			
		||||
    local units      = args.units or 1024 -- KB
 | 
			
		||||
    local notify     = args.notify or "on"
 | 
			
		||||
    local wifi_state = args.wifi_state or "off"
 | 
			
		||||
    local eth_state  = args.eth_state or "off"
 | 
			
		||||
    local screen     = args.screen or 1
 | 
			
		||||
    local format     = args.format or "%.1f"
 | 
			
		||||
    local settings   = args.settings or function() end
 | 
			
		||||
	local net = { widget = args.widget or wibox.widget.textbox(), devices = {} }
 | 
			
		||||
	local timeout = args.timeout or 2
 | 
			
		||||
	local units = args.units or 1024 -- KB
 | 
			
		||||
	local notify = args.notify or 'on'
 | 
			
		||||
	local wifi_state = args.wifi_state or 'off'
 | 
			
		||||
	local eth_state = args.eth_state or 'off'
 | 
			
		||||
	local screen = args.screen or 1
 | 
			
		||||
	local format = args.format or '%.1f'
 | 
			
		||||
	local settings = args.settings or function() end
 | 
			
		||||
 | 
			
		||||
    -- Compatibility with old API where iface was a string corresponding to 1 interface
 | 
			
		||||
    net.iface = (args.iface and (type(args.iface) == "string" and {args.iface}) or
 | 
			
		||||
                (type(args.iface) == "table" and args.iface)) or {}
 | 
			
		||||
	-- Compatibility with old API where iface was a string corresponding to 1 interface
 | 
			
		||||
	net.iface = (
 | 
			
		||||
		args.iface and (type(args.iface) == 'string' and { args.iface })
 | 
			
		||||
		or (type(args.iface) == 'table' and args.iface)
 | 
			
		||||
	) or {}
 | 
			
		||||
 | 
			
		||||
    function net.get_devices()
 | 
			
		||||
        net.iface = {} -- reset at every call
 | 
			
		||||
        helpers.line_callback("ip link", function(line)
 | 
			
		||||
            net.iface[#net.iface + 1] = not string.match(line, "LOOPBACK") and string.match(line, "(%w+): <") or nil
 | 
			
		||||
        end)
 | 
			
		||||
    end
 | 
			
		||||
	function net.get_devices()
 | 
			
		||||
		net.iface = {} -- reset at every call
 | 
			
		||||
		helpers.line_callback('ip link', function(line)
 | 
			
		||||
			net.iface[#net.iface + 1] = not string.match(line, 'LOOPBACK') and string.match(line, '(%w+): <') or nil
 | 
			
		||||
		end)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    if #net.iface == 0 then net.get_devices() end
 | 
			
		||||
	if #net.iface == 0 then
 | 
			
		||||
		net.get_devices()
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    function net.update()
 | 
			
		||||
        -- These are the totals over all specified interfaces
 | 
			
		||||
        net_now = {
 | 
			
		||||
            devices  = {},
 | 
			
		||||
            -- Bytes since last iteration
 | 
			
		||||
            sent     = 0,
 | 
			
		||||
            received = 0
 | 
			
		||||
        }
 | 
			
		||||
	function net.update()
 | 
			
		||||
		-- These are the totals over all specified interfaces
 | 
			
		||||
		net_now = {
 | 
			
		||||
			devices = {},
 | 
			
		||||
			-- Bytes since last iteration
 | 
			
		||||
			sent = 0,
 | 
			
		||||
			received = 0,
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        for _, 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)
 | 
			
		||||
		for _, 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"
 | 
			
		||||
			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
 | 
			
		||||
			dev_now.sent = (now_t - dev_before.last_t) / timeout / units
 | 
			
		||||
			dev_now.received = (now_r - dev_before.last_r) / timeout / units
 | 
			
		||||
 | 
			
		||||
            net_now.sent     = net_now.sent + dev_now.sent
 | 
			
		||||
            net_now.received = net_now.received + dev_now.received
 | 
			
		||||
			net_now.sent = net_now.sent + dev_now.sent
 | 
			
		||||
			net_now.received = net_now.received + dev_now.received
 | 
			
		||||
 | 
			
		||||
            dev_now.sent     = string.format(format, dev_now.sent)
 | 
			
		||||
            dev_now.received = string.format(format, dev_now.received)
 | 
			
		||||
			dev_now.sent = string.format(format, dev_now.sent)
 | 
			
		||||
			dev_now.received = string.format(format, dev_now.received)
 | 
			
		||||
 | 
			
		||||
            dev_now.last_t   = now_t
 | 
			
		||||
            dev_now.last_r   = now_r
 | 
			
		||||
			dev_now.last_t = now_t
 | 
			
		||||
			dev_now.last_r = now_r
 | 
			
		||||
 | 
			
		||||
            if wifi_state == "on" and helpers.first_line(string.format("/sys/class/net/%s/uevent", dev)) == "DEVTYPE=wlan" then
 | 
			
		||||
                dev_now.wifi   = true
 | 
			
		||||
                if string.match(dev_now.carrier, "1") then
 | 
			
		||||
                        dev_now.signal = tonumber(string.match(helpers.lines_from("/proc/net/wireless")[3], "(%-%d+%.)")) or nil
 | 
			
		||||
                end
 | 
			
		||||
            else
 | 
			
		||||
                dev_now.wifi   = false
 | 
			
		||||
            end
 | 
			
		||||
			if
 | 
			
		||||
				wifi_state == 'on'
 | 
			
		||||
				and helpers.first_line(string.format('/sys/class/net/%s/uevent', dev)) == 'DEVTYPE=wlan'
 | 
			
		||||
			then
 | 
			
		||||
				dev_now.wifi = true
 | 
			
		||||
				if string.match(dev_now.carrier, '1') then
 | 
			
		||||
					dev_now.signal = tonumber(string.match(helpers.lines_from('/proc/net/wireless')[3], '(%-%d+%.)'))
 | 
			
		||||
						or nil
 | 
			
		||||
				end
 | 
			
		||||
			else
 | 
			
		||||
				dev_now.wifi = false
 | 
			
		||||
			end
 | 
			
		||||
 | 
			
		||||
            if eth_state == "on" and helpers.first_line(string.format("/sys/class/net/%s/uevent", dev)) ~= "DEVTYPE=wlan" then
 | 
			
		||||
                dev_now.ethernet = true
 | 
			
		||||
            else
 | 
			
		||||
                dev_now.ethernet = false
 | 
			
		||||
            end
 | 
			
		||||
			if
 | 
			
		||||
				eth_state == 'on'
 | 
			
		||||
				and helpers.first_line(string.format('/sys/class/net/%s/uevent', dev)) ~= 'DEVTYPE=wlan'
 | 
			
		||||
			then
 | 
			
		||||
				dev_now.ethernet = true
 | 
			
		||||
			else
 | 
			
		||||
				dev_now.ethernet = false
 | 
			
		||||
			end
 | 
			
		||||
 | 
			
		||||
            net.devices[dev] = dev_now
 | 
			
		||||
			net.devices[dev] = dev_now
 | 
			
		||||
 | 
			
		||||
            -- Notify only once when connection is lost
 | 
			
		||||
            if string.match(dev_now.carrier, "0") and notify == "on" and helpers.get_map(dev) then
 | 
			
		||||
                naughty.notify {
 | 
			
		||||
                    title    = dev,
 | 
			
		||||
                    text     = "No carrier",
 | 
			
		||||
                    icon     = helpers.icons_dir .. "no_net.png",
 | 
			
		||||
                    screen   = screen
 | 
			
		||||
                }
 | 
			
		||||
                helpers.set_map(dev, false)
 | 
			
		||||
            elseif string.match(dev_now.carrier, "1") then
 | 
			
		||||
                helpers.set_map(dev, true)
 | 
			
		||||
            end
 | 
			
		||||
			-- Notify only once when connection is lost
 | 
			
		||||
			if string.match(dev_now.carrier, '0') and notify == 'on' and helpers.get_map(dev) then
 | 
			
		||||
				naughty.notify({
 | 
			
		||||
					title = dev,
 | 
			
		||||
					text = 'No carrier',
 | 
			
		||||
					icon = helpers.icons_dir .. 'no_net.png',
 | 
			
		||||
					screen = screen,
 | 
			
		||||
				})
 | 
			
		||||
				helpers.set_map(dev, false)
 | 
			
		||||
			elseif string.match(dev_now.carrier, '1') then
 | 
			
		||||
				helpers.set_map(dev, true)
 | 
			
		||||
			end
 | 
			
		||||
 | 
			
		||||
            net_now.carrier = dev_now.carrier
 | 
			
		||||
            net_now.state = dev_now.state
 | 
			
		||||
            net_now.devices[dev] = dev_now
 | 
			
		||||
            -- net_now.sent and net_now.received will be
 | 
			
		||||
            -- the totals across all specified devices
 | 
			
		||||
        end
 | 
			
		||||
			net_now.carrier = dev_now.carrier
 | 
			
		||||
			net_now.state = dev_now.state
 | 
			
		||||
			net_now.devices[dev] = dev_now
 | 
			
		||||
			-- net_now.sent and net_now.received will be
 | 
			
		||||
			-- the totals across all specified devices
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
        net_now.sent = string.format(format, net_now.sent)
 | 
			
		||||
        net_now.received = string.format(format, net_now.received)
 | 
			
		||||
		net_now.sent = string.format(format, net_now.sent)
 | 
			
		||||
		net_now.received = string.format(format, net_now.received)
 | 
			
		||||
 | 
			
		||||
        widget = net.widget
 | 
			
		||||
        settings()
 | 
			
		||||
    end
 | 
			
		||||
		widget = net.widget
 | 
			
		||||
		settings()
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    helpers.newtimer("network", timeout, net.update)
 | 
			
		||||
	helpers.newtimer('network', timeout, net.update)
 | 
			
		||||
 | 
			
		||||
    return net
 | 
			
		||||
	return net
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
return factory
 | 
			
		||||
 
 | 
			
		||||
@@ -5,54 +5,56 @@
 | 
			
		||||
 | 
			
		||||
--]]
 | 
			
		||||
 | 
			
		||||
local helpers = require("lain.helpers")
 | 
			
		||||
local shell   = require("awful.util").shell
 | 
			
		||||
local wibox   = require("wibox")
 | 
			
		||||
local string  = string
 | 
			
		||||
local type    = type
 | 
			
		||||
local helpers = require('lain.helpers')
 | 
			
		||||
local shell = require('awful.util').shell
 | 
			
		||||
local wibox = require('wibox')
 | 
			
		||||
local string = string
 | 
			
		||||
local type = type
 | 
			
		||||
 | 
			
		||||
-- PulseAudio volume
 | 
			
		||||
-- lain.widget.pulse
 | 
			
		||||
 | 
			
		||||
local function factory(args)
 | 
			
		||||
    args           = args or {}
 | 
			
		||||
	args = args or {}
 | 
			
		||||
 | 
			
		||||
    local pulse    = { widget = args.widget or wibox.widget.textbox(), device = "N/A" }
 | 
			
		||||
    local timeout  = args.timeout or 5
 | 
			
		||||
    local settings = args.settings or function() end
 | 
			
		||||
	local pulse = { widget = args.widget or wibox.widget.textbox(), device = 'N/A' }
 | 
			
		||||
	local timeout = args.timeout or 5
 | 
			
		||||
	local settings = args.settings or function() end
 | 
			
		||||
 | 
			
		||||
    pulse.devicetype = args.devicetype or "sink"
 | 
			
		||||
    pulse.cmd = args.cmd or "pacmd list-" .. pulse.devicetype .. "s | sed -n -e '/*/,$!d' -e '/index/p' -e '/base volume/d' -e '/volume:/p' -e '/muted:/p' -e '/device\\.string/p'"
 | 
			
		||||
	pulse.devicetype = args.devicetype or 'sink'
 | 
			
		||||
	pulse.cmd = args.cmd
 | 
			
		||||
		or 'pacmd list-'
 | 
			
		||||
			.. pulse.devicetype
 | 
			
		||||
			.. "s | sed -n -e '/*/,$!d' -e '/index/p' -e '/base volume/d' -e '/volume:/p' -e '/muted:/p' -e '/device\\.string/p'"
 | 
			
		||||
 | 
			
		||||
    function pulse.update()
 | 
			
		||||
        helpers.async({ shell, "-c", type(pulse.cmd) == "string" and pulse.cmd or pulse.cmd() },
 | 
			
		||||
        function(s)
 | 
			
		||||
            volume_now = {
 | 
			
		||||
                index  = string.match(s, "index: (%S+)") or "N/A",
 | 
			
		||||
                device = string.match(s, "device.string = \"(%S+)\"") or "N/A",
 | 
			
		||||
                muted  = string.match(s, "muted: (%S+)") or "N/A"
 | 
			
		||||
            }
 | 
			
		||||
	function pulse.update()
 | 
			
		||||
		helpers.async({ shell, '-c', type(pulse.cmd) == 'string' and pulse.cmd or pulse.cmd() }, function(s)
 | 
			
		||||
			volume_now = {
 | 
			
		||||
				index = string.match(s, 'index: (%S+)') or 'N/A',
 | 
			
		||||
				device = string.match(s, 'device.string = "(%S+)"') or 'N/A',
 | 
			
		||||
				muted = string.match(s, 'muted: (%S+)') or 'N/A',
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
            pulse.device = volume_now.index
 | 
			
		||||
			pulse.device = volume_now.index
 | 
			
		||||
 | 
			
		||||
            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 = pulse.widget
 | 
			
		||||
            settings()
 | 
			
		||||
        end)
 | 
			
		||||
    end
 | 
			
		||||
			widget = pulse.widget
 | 
			
		||||
			settings()
 | 
			
		||||
		end)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    helpers.newtimer("pulse", timeout, pulse.update)
 | 
			
		||||
	helpers.newtimer('pulse', timeout, pulse.update)
 | 
			
		||||
 | 
			
		||||
    return pulse
 | 
			
		||||
	return pulse
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
return factory
 | 
			
		||||
 
 | 
			
		||||
@@ -6,170 +6,180 @@
 | 
			
		||||
 | 
			
		||||
--]]
 | 
			
		||||
 | 
			
		||||
local helpers  = require("lain.helpers")
 | 
			
		||||
local awful    = require("awful")
 | 
			
		||||
local naughty  = require("naughty")
 | 
			
		||||
local wibox    = require("wibox")
 | 
			
		||||
local math     = math
 | 
			
		||||
local string   = string
 | 
			
		||||
local type     = type
 | 
			
		||||
local helpers = require('lain.helpers')
 | 
			
		||||
local awful = require('awful')
 | 
			
		||||
local naughty = require('naughty')
 | 
			
		||||
local wibox = require('wibox')
 | 
			
		||||
local math = math
 | 
			
		||||
local string = string
 | 
			
		||||
local type = type
 | 
			
		||||
local tonumber = tonumber
 | 
			
		||||
 | 
			
		||||
-- PulseAudio volume bar
 | 
			
		||||
-- lain.widget.pulsebar
 | 
			
		||||
 | 
			
		||||
local function factory(args)
 | 
			
		||||
    local pulsebar = {
 | 
			
		||||
        colors = {
 | 
			
		||||
            background      = "#000000",
 | 
			
		||||
            mute_background = "#000000",
 | 
			
		||||
            mute            = "#EB8F8F",
 | 
			
		||||
            unmute          = "#A4CE8A"
 | 
			
		||||
        },
 | 
			
		||||
	local pulsebar = {
 | 
			
		||||
		colors = {
 | 
			
		||||
			background = '#000000',
 | 
			
		||||
			mute_background = '#000000',
 | 
			
		||||
			mute = '#EB8F8F',
 | 
			
		||||
			unmute = '#A4CE8A',
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
        _current_level = 0,
 | 
			
		||||
        _mute          = "no",
 | 
			
		||||
        device         = "N/A"
 | 
			
		||||
    }
 | 
			
		||||
		_current_level = 0,
 | 
			
		||||
		_mute = 'no',
 | 
			
		||||
		device = 'N/A',
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    args             = args or {}
 | 
			
		||||
	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.height or 1
 | 
			
		||||
    local margins    = args.margins or 1
 | 
			
		||||
    local paddings   = args.paddings or 1
 | 
			
		||||
    local ticks      = args.ticks or false
 | 
			
		||||
    local ticks_size = args.ticks_size or 7
 | 
			
		||||
    local tick       = args.tick or "|"
 | 
			
		||||
    local tick_pre   = args.tick_pre or "["
 | 
			
		||||
    local tick_post  = args.tick_post or "]"
 | 
			
		||||
    local tick_none  = args.tick_none or " "
 | 
			
		||||
	local timeout = args.timeout or 5
 | 
			
		||||
	local settings = args.settings or function() end
 | 
			
		||||
	local width = args.width or 63
 | 
			
		||||
	local height = args.height or 1
 | 
			
		||||
	local margins = args.margins or 1
 | 
			
		||||
	local paddings = args.paddings or 1
 | 
			
		||||
	local ticks = args.ticks or false
 | 
			
		||||
	local ticks_size = args.ticks_size or 7
 | 
			
		||||
	local tick = args.tick or '|'
 | 
			
		||||
	local tick_pre = args.tick_pre or '['
 | 
			
		||||
	local tick_post = args.tick_post or ']'
 | 
			
		||||
	local tick_none = args.tick_none or ' '
 | 
			
		||||
 | 
			
		||||
    pulsebar.colors              = args.colors or pulsebar.colors
 | 
			
		||||
    pulsebar.followtag           = args.followtag or false
 | 
			
		||||
    pulsebar.notification_preset = args.notification_preset
 | 
			
		||||
    pulsebar.devicetype          = args.devicetype or "sink"
 | 
			
		||||
    pulsebar.cmd                 = args.cmd or "pacmd list-" .. pulsebar.devicetype .. "s | sed -n -e '/*/,$!d' -e '/index/p' -e '/base volume/d' -e '/volume:/p' -e '/muted:/p' -e '/device\\.string/p'"
 | 
			
		||||
	pulsebar.colors = args.colors or pulsebar.colors
 | 
			
		||||
	pulsebar.followtag = args.followtag or false
 | 
			
		||||
	pulsebar.notification_preset = args.notification_preset
 | 
			
		||||
	pulsebar.devicetype = args.devicetype or 'sink'
 | 
			
		||||
	pulsebar.cmd = args.cmd
 | 
			
		||||
		or 'pacmd list-'
 | 
			
		||||
			.. pulsebar.devicetype
 | 
			
		||||
			.. "s | sed -n -e '/*/,$!d' -e '/index/p' -e '/base volume/d' -e '/volume:/p' -e '/muted:/p' -e '/device\\.string/p'"
 | 
			
		||||
 | 
			
		||||
    if not pulsebar.notification_preset then
 | 
			
		||||
        pulsebar.notification_preset = {
 | 
			
		||||
            font = "Monospace 10"
 | 
			
		||||
        }
 | 
			
		||||
    end
 | 
			
		||||
	if not pulsebar.notification_preset then
 | 
			
		||||
		pulsebar.notification_preset = {
 | 
			
		||||
			font = 'Monospace 10',
 | 
			
		||||
		}
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    pulsebar.bar = wibox.widget {
 | 
			
		||||
        color            = pulsebar.colors.unmute,
 | 
			
		||||
        background_color = pulsebar.colors.background,
 | 
			
		||||
        forced_height    = height,
 | 
			
		||||
        forced_width     = width,
 | 
			
		||||
        margins          = margins,
 | 
			
		||||
        paddings         = paddings,
 | 
			
		||||
        ticks            = ticks,
 | 
			
		||||
        ticks_size       = ticks_size,
 | 
			
		||||
        widget           = wibox.widget.progressbar,
 | 
			
		||||
    }
 | 
			
		||||
	pulsebar.bar = wibox.widget({
 | 
			
		||||
		color = pulsebar.colors.unmute,
 | 
			
		||||
		background_color = pulsebar.colors.background,
 | 
			
		||||
		forced_height = height,
 | 
			
		||||
		forced_width = width,
 | 
			
		||||
		margins = margins,
 | 
			
		||||
		paddings = paddings,
 | 
			
		||||
		ticks = ticks,
 | 
			
		||||
		ticks_size = ticks_size,
 | 
			
		||||
		widget = wibox.widget.progressbar,
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
    pulsebar.tooltip = awful.tooltip({ objects = { pulsebar.bar } })
 | 
			
		||||
	pulsebar.tooltip = awful.tooltip({ objects = { pulsebar.bar } })
 | 
			
		||||
 | 
			
		||||
    function pulsebar.update(callback)
 | 
			
		||||
        helpers.async({ awful.util.shell, "-c", type(pulsebar.cmd) == "string" and pulsebar.cmd or pulsebar.cmd() },
 | 
			
		||||
        function(s)
 | 
			
		||||
            volume_now = {
 | 
			
		||||
                index  = string.match(s, "index: (%S+)") or "N/A",
 | 
			
		||||
                device = string.match(s, "device.string = \"(%S+)\"") or "N/A",
 | 
			
		||||
                muted  = string.match(s, "muted: (%S+)") or "N/A"
 | 
			
		||||
            }
 | 
			
		||||
	function pulsebar.update(callback)
 | 
			
		||||
		helpers.async(
 | 
			
		||||
			{ awful.util.shell, '-c', type(pulsebar.cmd) == 'string' and pulsebar.cmd or pulsebar.cmd() },
 | 
			
		||||
			function(s)
 | 
			
		||||
				volume_now = {
 | 
			
		||||
					index = string.match(s, 'index: (%S+)') or 'N/A',
 | 
			
		||||
					device = string.match(s, 'device.string = "(%S+)"') or 'N/A',
 | 
			
		||||
					muted = string.match(s, 'muted: (%S+)') or 'N/A',
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
            pulsebar.device = volume_now.index
 | 
			
		||||
				pulsebar.device = volume_now.index
 | 
			
		||||
 | 
			
		||||
            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'
 | 
			
		||||
 | 
			
		||||
            local volu = volume_now.left
 | 
			
		||||
            local mute = volume_now.muted
 | 
			
		||||
				local volu = volume_now.left
 | 
			
		||||
				local mute = volume_now.muted
 | 
			
		||||
 | 
			
		||||
            if volu:match("N/A") or mute:match("N/A") then return end
 | 
			
		||||
				if volu:match('N/A') or mute:match('N/A') then
 | 
			
		||||
					return
 | 
			
		||||
				end
 | 
			
		||||
 | 
			
		||||
            if volu ~= pulsebar._current_level or mute ~= pulsebar._mute then
 | 
			
		||||
                pulsebar._current_level = tonumber(volu)
 | 
			
		||||
                pulsebar.bar:set_value(pulsebar._current_level / 100)
 | 
			
		||||
                if pulsebar._current_level == 0 or mute == "yes" then
 | 
			
		||||
                    pulsebar._mute = mute
 | 
			
		||||
                    pulsebar.tooltip:set_text ("[muted]")
 | 
			
		||||
                    pulsebar.bar.color = pulsebar.colors.mute
 | 
			
		||||
                    pulsebar.bar.background_color = pulsebar.colors.mute_background
 | 
			
		||||
                else
 | 
			
		||||
                    pulsebar._mute = "no"
 | 
			
		||||
                    pulsebar.tooltip:set_text(string.format("%s %s: %s", pulsebar.devicetype, pulsebar.device, volu))
 | 
			
		||||
                    pulsebar.bar.color = pulsebar.colors.unmute
 | 
			
		||||
                    pulsebar.bar.background_color = pulsebar.colors.background
 | 
			
		||||
                end
 | 
			
		||||
				if volu ~= pulsebar._current_level or mute ~= pulsebar._mute then
 | 
			
		||||
					pulsebar._current_level = tonumber(volu)
 | 
			
		||||
					pulsebar.bar:set_value(pulsebar._current_level / 100)
 | 
			
		||||
					if pulsebar._current_level == 0 or mute == 'yes' then
 | 
			
		||||
						pulsebar._mute = mute
 | 
			
		||||
						pulsebar.tooltip:set_text('[muted]')
 | 
			
		||||
						pulsebar.bar.color = pulsebar.colors.mute
 | 
			
		||||
						pulsebar.bar.background_color = pulsebar.colors.mute_background
 | 
			
		||||
					else
 | 
			
		||||
						pulsebar._mute = 'no'
 | 
			
		||||
						pulsebar.tooltip:set_text(
 | 
			
		||||
							string.format('%s %s: %s', pulsebar.devicetype, pulsebar.device, volu)
 | 
			
		||||
						)
 | 
			
		||||
						pulsebar.bar.color = pulsebar.colors.unmute
 | 
			
		||||
						pulsebar.bar.background_color = pulsebar.colors.background
 | 
			
		||||
					end
 | 
			
		||||
 | 
			
		||||
                settings()
 | 
			
		||||
					settings()
 | 
			
		||||
 | 
			
		||||
                if type(callback) == "function" then callback() end
 | 
			
		||||
            end
 | 
			
		||||
        end)
 | 
			
		||||
    end
 | 
			
		||||
					if type(callback) == 'function' then
 | 
			
		||||
						callback()
 | 
			
		||||
					end
 | 
			
		||||
				end
 | 
			
		||||
			end
 | 
			
		||||
		)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    function pulsebar.notify()
 | 
			
		||||
        pulsebar.update(function()
 | 
			
		||||
            local preset = pulsebar.notification_preset
 | 
			
		||||
	function pulsebar.notify()
 | 
			
		||||
		pulsebar.update(function()
 | 
			
		||||
			local preset = pulsebar.notification_preset
 | 
			
		||||
 | 
			
		||||
            preset.title = string.format("%s %s - %s%%", pulsebar.devicetype, pulsebar.device, pulsebar._current_level)
 | 
			
		||||
			preset.title = string.format('%s %s - %s%%', pulsebar.devicetype, pulsebar.device, pulsebar._current_level)
 | 
			
		||||
 | 
			
		||||
            if pulsebar._mute == "yes" then
 | 
			
		||||
                preset.title = preset.title .. " muted"
 | 
			
		||||
            end
 | 
			
		||||
			if pulsebar._mute == 'yes' then
 | 
			
		||||
				preset.title = preset.title .. ' muted'
 | 
			
		||||
			end
 | 
			
		||||
 | 
			
		||||
            -- tot is the maximum number of ticks to display in the notification
 | 
			
		||||
            -- fallback: default horizontal wibox height
 | 
			
		||||
            local wib, tot = awful.screen.focused().mywibox, 20
 | 
			
		||||
			-- tot is the maximum number of ticks to display in the notification
 | 
			
		||||
			-- fallback: default horizontal wibox height
 | 
			
		||||
			local wib, tot = awful.screen.focused().mywibox, 20
 | 
			
		||||
 | 
			
		||||
            -- if we can grab mywibox, tot is defined as its height if
 | 
			
		||||
            -- horizontal, or width otherwise
 | 
			
		||||
            if wib then
 | 
			
		||||
                if wib.position == "left" or wib.position == "right" then
 | 
			
		||||
                    tot = wib.width
 | 
			
		||||
                else
 | 
			
		||||
                    tot = wib.height
 | 
			
		||||
                end
 | 
			
		||||
            end
 | 
			
		||||
			-- if we can grab mywibox, tot is defined as its height if
 | 
			
		||||
			-- horizontal, or width otherwise
 | 
			
		||||
			if wib then
 | 
			
		||||
				if wib.position == 'left' or wib.position == 'right' then
 | 
			
		||||
					tot = wib.width
 | 
			
		||||
				else
 | 
			
		||||
					tot = wib.height
 | 
			
		||||
				end
 | 
			
		||||
			end
 | 
			
		||||
 | 
			
		||||
            local int = math.modf((pulsebar._current_level / 100) * tot)
 | 
			
		||||
            preset.text = string.format(
 | 
			
		||||
                "%s%s%s%s",
 | 
			
		||||
                tick_pre,
 | 
			
		||||
                string.rep(tick, int),
 | 
			
		||||
                string.rep(tick_none, tot - int),
 | 
			
		||||
                tick_post
 | 
			
		||||
            )
 | 
			
		||||
			local int = math.modf((pulsebar._current_level / 100) * tot)
 | 
			
		||||
			preset.text =
 | 
			
		||||
				string.format('%s%s%s%s', tick_pre, string.rep(tick, int), string.rep(tick_none, tot - int), tick_post)
 | 
			
		||||
 | 
			
		||||
            if pulsebar.followtag then preset.screen = awful.screen.focused() end
 | 
			
		||||
			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
 | 
			
		||||
                naughty.replace_text(pulsebar.notification, preset.title, preset.text)
 | 
			
		||||
            end
 | 
			
		||||
        end)
 | 
			
		||||
    end
 | 
			
		||||
			if not pulsebar.notification then
 | 
			
		||||
				pulsebar.notification = naughty.notify({
 | 
			
		||||
					preset = preset,
 | 
			
		||||
					destroy = function()
 | 
			
		||||
						pulsebar.notification = nil
 | 
			
		||||
					end,
 | 
			
		||||
				})
 | 
			
		||||
			else
 | 
			
		||||
				naughty.replace_text(pulsebar.notification, preset.title, preset.text)
 | 
			
		||||
			end
 | 
			
		||||
		end)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    helpers.newtimer(string.format("pulsebar-%s-%s", pulsebar.devicetype, pulsebar.device), timeout, pulsebar.update)
 | 
			
		||||
	helpers.newtimer(string.format('pulsebar-%s-%s', pulsebar.devicetype, pulsebar.device), timeout, pulsebar.update)
 | 
			
		||||
 | 
			
		||||
    return pulsebar
 | 
			
		||||
	return pulsebar
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
return factory
 | 
			
		||||
 
 | 
			
		||||
@@ -6,34 +6,34 @@
 | 
			
		||||
 | 
			
		||||
--]]
 | 
			
		||||
 | 
			
		||||
local helpers     = require("lain.helpers")
 | 
			
		||||
local wibox       = require("wibox")
 | 
			
		||||
local helpers = require('lain.helpers')
 | 
			
		||||
local wibox = require('wibox')
 | 
			
		||||
local open, match = io.open, string.match
 | 
			
		||||
 | 
			
		||||
-- System load
 | 
			
		||||
-- lain.widget.sysload
 | 
			
		||||
 | 
			
		||||
local function factory(args)
 | 
			
		||||
    args           = args or {}
 | 
			
		||||
	args = args or {}
 | 
			
		||||
 | 
			
		||||
    local sysload  = { widget = args.widget or wibox.widget.textbox() }
 | 
			
		||||
    local timeout  = args.timeout or 2
 | 
			
		||||
    local settings = args.settings or function() end
 | 
			
		||||
	local sysload = { widget = args.widget or wibox.widget.textbox() }
 | 
			
		||||
	local timeout = args.timeout or 2
 | 
			
		||||
	local settings = args.settings or function() end
 | 
			
		||||
 | 
			
		||||
    function sysload.update()
 | 
			
		||||
        local f = open("/proc/loadavg")
 | 
			
		||||
        local ret = f:read("*all")
 | 
			
		||||
        f:close()
 | 
			
		||||
	function sysload.update()
 | 
			
		||||
		local f = open('/proc/loadavg')
 | 
			
		||||
		local ret = f:read('*all')
 | 
			
		||||
		f:close()
 | 
			
		||||
 | 
			
		||||
        load_1, load_5, load_15 = match(ret, "([^%s]+) ([^%s]+) ([^%s]+)")
 | 
			
		||||
		load_1, load_5, load_15 = match(ret, '([^%s]+) ([^%s]+) ([^%s]+)')
 | 
			
		||||
 | 
			
		||||
        widget = sysload.widget
 | 
			
		||||
        settings()
 | 
			
		||||
    end
 | 
			
		||||
		widget = sysload.widget
 | 
			
		||||
		settings()
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    helpers.newtimer("sysload", timeout, sysload.update)
 | 
			
		||||
	helpers.newtimer('sysload', timeout, sysload.update)
 | 
			
		||||
 | 
			
		||||
    return sysload
 | 
			
		||||
	return sysload
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
return factory
 | 
			
		||||
 
 | 
			
		||||
@@ -5,46 +5,46 @@
 | 
			
		||||
 | 
			
		||||
--]]
 | 
			
		||||
 | 
			
		||||
local helpers  = require("lain.helpers")
 | 
			
		||||
local wibox    = require("wibox")
 | 
			
		||||
local helpers = require('lain.helpers')
 | 
			
		||||
local wibox = require('wibox')
 | 
			
		||||
local tonumber = tonumber
 | 
			
		||||
 | 
			
		||||
-- {thermal,core} temperature info
 | 
			
		||||
-- lain.widget.temp
 | 
			
		||||
 | 
			
		||||
local function factory(args)
 | 
			
		||||
    args           = args or {}
 | 
			
		||||
	args = args or {}
 | 
			
		||||
 | 
			
		||||
    local temp     = { widget = args.widget or wibox.widget.textbox() }
 | 
			
		||||
    local timeout  = args.timeout or 30
 | 
			
		||||
    local tempfile = args.tempfile or "/sys/devices/virtual/thermal/thermal_zone0/temp"
 | 
			
		||||
    local format   = args.format or "%.1f"
 | 
			
		||||
    local settings = args.settings or function() end
 | 
			
		||||
	local temp = { widget = args.widget or wibox.widget.textbox() }
 | 
			
		||||
	local timeout = args.timeout or 30
 | 
			
		||||
	local tempfile = args.tempfile or '/sys/devices/virtual/thermal/thermal_zone0/temp'
 | 
			
		||||
	local format = args.format or '%.1f'
 | 
			
		||||
	local settings = args.settings or function() end
 | 
			
		||||
 | 
			
		||||
    function temp.update()
 | 
			
		||||
        helpers.async({"find", "/sys/devices", "-type", "f", "-name", "*temp*"}, function(f)
 | 
			
		||||
            temp_now = {}
 | 
			
		||||
            local temp_fl, temp_value
 | 
			
		||||
            for t in f:gmatch("[^\n]+") do
 | 
			
		||||
                temp_fl = helpers.first_line(t)
 | 
			
		||||
                if temp_fl then
 | 
			
		||||
                    temp_value = tonumber(temp_fl)
 | 
			
		||||
                    temp_now[t] = temp_value and temp_value/1e3 or temp_fl
 | 
			
		||||
                end
 | 
			
		||||
            end
 | 
			
		||||
            if temp_now[tempfile] then
 | 
			
		||||
                coretemp_now = string.format(format, temp_now[tempfile])
 | 
			
		||||
            else
 | 
			
		||||
                coretemp_now = "N/A"
 | 
			
		||||
            end
 | 
			
		||||
            widget = temp.widget
 | 
			
		||||
            settings()
 | 
			
		||||
        end)
 | 
			
		||||
    end
 | 
			
		||||
	function temp.update()
 | 
			
		||||
		helpers.async({ 'find', '/sys/devices', '-type', 'f', '-name', '*temp*' }, function(f)
 | 
			
		||||
			temp_now = {}
 | 
			
		||||
			local temp_fl, temp_value
 | 
			
		||||
			for t in f:gmatch('[^\n]+') do
 | 
			
		||||
				temp_fl = helpers.first_line(t)
 | 
			
		||||
				if temp_fl then
 | 
			
		||||
					temp_value = tonumber(temp_fl)
 | 
			
		||||
					temp_now[t] = temp_value and temp_value / 1e3 or temp_fl
 | 
			
		||||
				end
 | 
			
		||||
			end
 | 
			
		||||
			if temp_now[tempfile] then
 | 
			
		||||
				coretemp_now = string.format(format, temp_now[tempfile])
 | 
			
		||||
			else
 | 
			
		||||
				coretemp_now = 'N/A'
 | 
			
		||||
			end
 | 
			
		||||
			widget = temp.widget
 | 
			
		||||
			settings()
 | 
			
		||||
		end)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    helpers.newtimer("thermal", timeout, temp.update)
 | 
			
		||||
	helpers.newtimer('thermal', timeout, temp.update)
 | 
			
		||||
 | 
			
		||||
    return temp
 | 
			
		||||
	return temp
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
return factory
 | 
			
		||||
 
 | 
			
		||||
@@ -5,15 +5,15 @@
 | 
			
		||||
 | 
			
		||||
--]]
 | 
			
		||||
 | 
			
		||||
local helpers  = require("lain.helpers")
 | 
			
		||||
local json     = require("lain.util").dkjson
 | 
			
		||||
local focused  = require("awful.screen").focused
 | 
			
		||||
local naughty  = require("naughty")
 | 
			
		||||
local wibox    = require("wibox")
 | 
			
		||||
local math     = math
 | 
			
		||||
local os       = os
 | 
			
		||||
local string   = string
 | 
			
		||||
local type     = type
 | 
			
		||||
local helpers = require('lain.helpers')
 | 
			
		||||
local json = require('lain.util').dkjson
 | 
			
		||||
local focused = require('awful.screen').focused
 | 
			
		||||
local naughty = require('naughty')
 | 
			
		||||
local wibox = require('wibox')
 | 
			
		||||
local math = math
 | 
			
		||||
local os = os
 | 
			
		||||
local string = string
 | 
			
		||||
local type = type
 | 
			
		||||
local tonumber = tonumber
 | 
			
		||||
 | 
			
		||||
-- OpenWeatherMap
 | 
			
		||||
@@ -21,126 +21,131 @@ local tonumber = tonumber
 | 
			
		||||
-- lain.widget.weather
 | 
			
		||||
 | 
			
		||||
local function factory(args)
 | 
			
		||||
    args                        = args or {}
 | 
			
		||||
	args = args or {}
 | 
			
		||||
 | 
			
		||||
    local weather               = { widget = args.widget or wibox.widget.textbox() }
 | 
			
		||||
    local APPID                 = args.APPID -- mandatory
 | 
			
		||||
    local timeout               = args.timeout or 60 * 15 -- 15 min
 | 
			
		||||
    local current_call          = args.current_call  or "curl -s 'https://api.openweathermap.org/data/2.5/weather?id=%s&units=%s&lang=%s&APPID=%s'"
 | 
			
		||||
    local forecast_call         = args.forecast_call or "curl -s 'https://api.openweathermap.org/data/2.5/forecast?id=%s&units=%s&lang=%s&APPID=%s'"
 | 
			
		||||
    local city_id               = args.city_id or 0 -- placeholder
 | 
			
		||||
    local units                 = args.units or "metric"
 | 
			
		||||
    local lang                  = args.lang or "en"
 | 
			
		||||
    local cnt                   = args.cnt or 5
 | 
			
		||||
    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 = os.date("%a %d", wn["dt"])
 | 
			
		||||
                                      local temp = math.floor(wn["main"]["temp"])
 | 
			
		||||
                                      local desc = wn["weather"][1]["description"]
 | 
			
		||||
                                      return string.format("<b>%s</b>: %s, %d ", day, desc, temp)
 | 
			
		||||
                                  end
 | 
			
		||||
    local weather_na_markup     = args.weather_na_markup or " N/A "
 | 
			
		||||
    local followtag             = args.followtag or false
 | 
			
		||||
    local showpopup             = args.showpopup or "on"
 | 
			
		||||
    local settings              = args.settings or function() end
 | 
			
		||||
	local weather = { widget = args.widget or wibox.widget.textbox() }
 | 
			
		||||
	local APPID = args.APPID -- mandatory
 | 
			
		||||
	local timeout = args.timeout or 60 * 15 -- 15 min
 | 
			
		||||
	local current_call = args.current_call
 | 
			
		||||
		or "curl -s 'https://api.openweathermap.org/data/2.5/weather?id=%s&units=%s&lang=%s&APPID=%s'"
 | 
			
		||||
	local forecast_call = args.forecast_call
 | 
			
		||||
		or "curl -s 'https://api.openweathermap.org/data/2.5/forecast?id=%s&units=%s&lang=%s&APPID=%s'"
 | 
			
		||||
	local city_id = args.city_id or 0 -- placeholder
 | 
			
		||||
	local units = args.units or 'metric'
 | 
			
		||||
	local lang = args.lang or 'en'
 | 
			
		||||
	local cnt = args.cnt or 5
 | 
			
		||||
	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 = os.date('%a %d', wn['dt'])
 | 
			
		||||
			local temp = math.floor(wn['main']['temp'])
 | 
			
		||||
			local desc = wn['weather'][1]['description']
 | 
			
		||||
			return string.format('<b>%s</b>: %s, %d ', day, desc, temp)
 | 
			
		||||
		end
 | 
			
		||||
	local weather_na_markup = args.weather_na_markup or ' N/A '
 | 
			
		||||
	local followtag = args.followtag or false
 | 
			
		||||
	local showpopup = args.showpopup or 'on'
 | 
			
		||||
	local settings = args.settings or function() end
 | 
			
		||||
 | 
			
		||||
    weather.widget:set_markup(weather_na_markup)
 | 
			
		||||
    weather.icon_path = icons_path .. "na.png"
 | 
			
		||||
    weather.icon = wibox.widget.imagebox(weather.icon_path)
 | 
			
		||||
	weather.widget:set_markup(weather_na_markup)
 | 
			
		||||
	weather.icon_path = icons_path .. 'na.png'
 | 
			
		||||
	weather.icon = wibox.widget.imagebox(weather.icon_path)
 | 
			
		||||
 | 
			
		||||
    function weather.show(seconds)
 | 
			
		||||
        weather.hide()
 | 
			
		||||
	function weather.show(seconds)
 | 
			
		||||
		weather.hide()
 | 
			
		||||
 | 
			
		||||
        if followtag then
 | 
			
		||||
            notification_preset.screen = focused()
 | 
			
		||||
        end
 | 
			
		||||
		if followtag then
 | 
			
		||||
			notification_preset.screen = focused()
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
        if not weather.notification_text then
 | 
			
		||||
            weather.update()
 | 
			
		||||
            weather.forecast_update()
 | 
			
		||||
        end
 | 
			
		||||
		if not weather.notification_text then
 | 
			
		||||
			weather.update()
 | 
			
		||||
			weather.forecast_update()
 | 
			
		||||
		end
 | 
			
		||||
 | 
			
		||||
        weather.notification = naughty.notify {
 | 
			
		||||
            preset  = notification_preset,
 | 
			
		||||
            text    = weather.notification_text,
 | 
			
		||||
            icon    = weather.icon_path,
 | 
			
		||||
            timeout = type(seconds) == "number" and seconds or notification_preset.timeout
 | 
			
		||||
        }
 | 
			
		||||
    end
 | 
			
		||||
		weather.notification = naughty.notify({
 | 
			
		||||
			preset = notification_preset,
 | 
			
		||||
			text = weather.notification_text,
 | 
			
		||||
			icon = weather.icon_path,
 | 
			
		||||
			timeout = type(seconds) == 'number' and seconds or notification_preset.timeout,
 | 
			
		||||
		})
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    function weather.hide()
 | 
			
		||||
        if weather.notification then
 | 
			
		||||
            naughty.destroy(weather.notification)
 | 
			
		||||
            weather.notification = nil
 | 
			
		||||
        end
 | 
			
		||||
    end
 | 
			
		||||
	function weather.hide()
 | 
			
		||||
		if weather.notification then
 | 
			
		||||
			naughty.destroy(weather.notification)
 | 
			
		||||
			weather.notification = nil
 | 
			
		||||
		end
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    function weather.attach(obj)
 | 
			
		||||
        obj:connect_signal("mouse::enter", function()
 | 
			
		||||
            weather.show(0)
 | 
			
		||||
        end)
 | 
			
		||||
        obj:connect_signal("mouse::leave", function()
 | 
			
		||||
            weather.hide()
 | 
			
		||||
        end)
 | 
			
		||||
    end
 | 
			
		||||
	function weather.attach(obj)
 | 
			
		||||
		obj:connect_signal('mouse::enter', function()
 | 
			
		||||
			weather.show(0)
 | 
			
		||||
		end)
 | 
			
		||||
		obj:connect_signal('mouse::leave', function()
 | 
			
		||||
			weather.hide()
 | 
			
		||||
		end)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    function weather.forecast_update()
 | 
			
		||||
        local cmd = string.format(forecast_call, city_id, units, lang, APPID)
 | 
			
		||||
        helpers.async(cmd, function(f)
 | 
			
		||||
            local err
 | 
			
		||||
            weather_now, _, err = json.decode(f, 1, nil)
 | 
			
		||||
	function weather.forecast_update()
 | 
			
		||||
		local cmd = string.format(forecast_call, city_id, units, lang, APPID)
 | 
			
		||||
		helpers.async(cmd, function(f)
 | 
			
		||||
			local err
 | 
			
		||||
			weather_now, _, err = json.decode(f, 1, nil)
 | 
			
		||||
 | 
			
		||||
            if not err and type(weather_now) == "table" and tonumber(weather_now["cod"]) == 200 then
 | 
			
		||||
                weather.notification_text = ""
 | 
			
		||||
                for i = 1, weather_now["cnt"], math.floor(weather_now["cnt"] / cnt) do
 | 
			
		||||
                    weather.notification_text = weather.notification_text ..
 | 
			
		||||
                                                notification_text_fun(weather_now["list"][i])
 | 
			
		||||
                    if i < weather_now["cnt"] then
 | 
			
		||||
                        weather.notification_text = weather.notification_text .. "\n"
 | 
			
		||||
                    end
 | 
			
		||||
                end
 | 
			
		||||
            end
 | 
			
		||||
        end)
 | 
			
		||||
    end
 | 
			
		||||
			if not err and type(weather_now) == 'table' and tonumber(weather_now['cod']) == 200 then
 | 
			
		||||
				weather.notification_text = ''
 | 
			
		||||
				for i = 1, weather_now['cnt'], math.floor(weather_now['cnt'] / cnt) do
 | 
			
		||||
					weather.notification_text = weather.notification_text
 | 
			
		||||
						.. notification_text_fun(weather_now['list'][i])
 | 
			
		||||
					if i < weather_now['cnt'] then
 | 
			
		||||
						weather.notification_text = weather.notification_text .. '\n'
 | 
			
		||||
					end
 | 
			
		||||
				end
 | 
			
		||||
			end
 | 
			
		||||
		end)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    function weather.update()
 | 
			
		||||
        local cmd = string.format(current_call, city_id, units, lang, APPID)
 | 
			
		||||
        helpers.async(cmd, function(f)
 | 
			
		||||
            local err
 | 
			
		||||
            weather_now, _, err = json.decode(f, 1, nil)
 | 
			
		||||
	function weather.update()
 | 
			
		||||
		local cmd = string.format(current_call, city_id, units, lang, APPID)
 | 
			
		||||
		helpers.async(cmd, function(f)
 | 
			
		||||
			local err
 | 
			
		||||
			weather_now, _, err = json.decode(f, 1, nil)
 | 
			
		||||
 | 
			
		||||
            if not err and type(weather_now) == "table" and tonumber(weather_now["cod"]) == 200 then
 | 
			
		||||
                local sunrise = tonumber(weather_now["sys"]["sunrise"])
 | 
			
		||||
                local sunset  = tonumber(weather_now["sys"]["sunset"])
 | 
			
		||||
                local icon    = weather_now["weather"][1]["icon"]
 | 
			
		||||
                local loc_now = os.time()
 | 
			
		||||
			if not err and type(weather_now) == 'table' and tonumber(weather_now['cod']) == 200 then
 | 
			
		||||
				local sunrise = tonumber(weather_now['sys']['sunrise'])
 | 
			
		||||
				local sunset = tonumber(weather_now['sys']['sunset'])
 | 
			
		||||
				local icon = weather_now['weather'][1]['icon']
 | 
			
		||||
				local loc_now = os.time()
 | 
			
		||||
 | 
			
		||||
                if sunrise <= loc_now and loc_now <= sunset then
 | 
			
		||||
                    icon = string.gsub(icon, "n", "d")
 | 
			
		||||
                else
 | 
			
		||||
                    icon = string.gsub(icon, "d", "n")
 | 
			
		||||
                end
 | 
			
		||||
				if sunrise <= loc_now and loc_now <= sunset then
 | 
			
		||||
					icon = string.gsub(icon, 'n', 'd')
 | 
			
		||||
				else
 | 
			
		||||
					icon = string.gsub(icon, 'd', 'n')
 | 
			
		||||
				end
 | 
			
		||||
 | 
			
		||||
                weather.icon_path = icons_path .. icon .. ".png"
 | 
			
		||||
                widget = weather.widget
 | 
			
		||||
                settings()
 | 
			
		||||
            else
 | 
			
		||||
                weather.icon_path = icons_path .. "na.png"
 | 
			
		||||
                weather.widget:set_markup(weather_na_markup)
 | 
			
		||||
            end
 | 
			
		||||
				weather.icon_path = icons_path .. icon .. '.png'
 | 
			
		||||
				widget = weather.widget
 | 
			
		||||
				settings()
 | 
			
		||||
			else
 | 
			
		||||
				weather.icon_path = icons_path .. 'na.png'
 | 
			
		||||
				weather.widget:set_markup(weather_na_markup)
 | 
			
		||||
			end
 | 
			
		||||
 | 
			
		||||
            weather.icon:set_image(weather.icon_path)
 | 
			
		||||
        end)
 | 
			
		||||
    end
 | 
			
		||||
			weather.icon:set_image(weather.icon_path)
 | 
			
		||||
		end)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    if showpopup == "on" then weather.attach(weather.widget) end
 | 
			
		||||
	if showpopup == 'on' then
 | 
			
		||||
		weather.attach(weather.widget)
 | 
			
		||||
	end
 | 
			
		||||
 | 
			
		||||
    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)
 | 
			
		||||
	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 weather
 | 
			
		||||
	return weather
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
return factory
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user