diff --git a/README.rst b/README.rst index f1a2841..2b77f9a 100644 --- a/README.rst +++ b/README.rst @@ -13,7 +13,7 @@ Layouts, widgets and utilities for Awesome WM 4.x Description ----------- -Successor of awesome-vain_, this module provides alternative layouts, asynchronous widgets and utility functions for Awesome_. Read the wiki_ for all the info. +Successor of awesome-vain_, this module provides alternative layouts, asynchronous widgets and utility functions for Awesome_. Contributions ------------- @@ -35,5 +35,4 @@ Contributed widgets have to be put in ``widget/contrib``. .. _GNU-GPL2: http://www.gnu.org/licenses/gpl-2.0.html .. _awesome-vain: https://github.com/vain/awesome-vain .. _Awesome: https://github.com/awesomeWM/awesome -.. _wiki: https://github.com/lcpz/lain/wiki .. _lain.helpers: https://github.com/lcpz/lain/blob/master/helpers.lua diff --git a/helpers.lua b/helpers.lua index d3fa259..45f090f 100644 --- a/helpers.lua +++ b/helpers.lua @@ -25,9 +25,8 @@ helpers.scripts_dir = helpers.lain_dir .. 'scripts/' -- {{{ Modules loader -function helpers.wrequire(table, key) - local module = rawget(table, key) - return module or require(table._NAME .. '.' .. key) +function helpers.wrequire(t, k) + return rawget(t, k) or require(t._NAME .. '.' .. k) end -- }}} @@ -109,7 +108,7 @@ end -- @return cmd PID function helpers.async(cmd, callback) return spawn.easy_async(cmd, - function (stdout, stderr, reason, exit_code) + function (stdout, _, _, exit_code) callback(stdout, exit_code) end) end @@ -117,7 +116,7 @@ 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, stderr, reason, exit_code) + function (stdout, _, _, exit_code) callback(stdout, exit_code) end) end @@ -187,7 +186,7 @@ function helpers.trivial_partition_set(set) return ss end --- creates the powerset of a given set +-- create the powerset of a given set function helpers.powerset(s) if not s then return {} end local t = {{}} diff --git a/layout/centerwork.lua b/layout/centerwork.lua index 2b38a69..142ccbe 100644 --- a/layout/centerwork.lua +++ b/layout/centerwork.lua @@ -24,7 +24,7 @@ local function arrange(p, layout) if #cls == 0 then return end - local c, g = cls[1], {} + local g = {} -- Main column, fixed width and height local mwfact = t.master_width_factor @@ -64,12 +64,12 @@ local function arrange(p, layout) g.width = max(g.width, 1) g.height = max(g.height, 1) - p.geometries[c] = g + p.geometries[cls[1]] = g -- Auxiliary clients if #cls <= 1 then return end for i = 2, #cls do - local c, g = cls[i], {} + g = {} local idxChecker, dimToAssign local rowIndex = floor(i/2) @@ -121,11 +121,11 @@ local function arrange(p, layout) g.width = max(g.width, 1) g.height = max(g.height, 1) - p.geometries[c] = g + p.geometries[cls[i]] = g end end -local function mouse_resize_handler(c, corner, x, y, orientation) +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() @@ -191,4 +191,77 @@ function centerwork.horizontal.mouse_resize_handler(c, corner, x, y) 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 function compare_position(a, b) + 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 = client.focus.first_tag:clients() + 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 + + 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" +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 +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 +end + return centerwork diff --git a/layout/termfair.lua b/layout/termfair.lua index e33894e..cf018ef 100644 --- a/layout/termfair.lua +++ b/layout/termfair.lua @@ -14,6 +14,7 @@ local tonumber = tonumber 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 @@ -22,33 +23,32 @@ local function do_fair(p, orientation) 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) + if orientation == "west" then -- Layout with fixed number of vertical columns (read from nmaster). - -- New windows align from left to right. When a row is full, a now + -- 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 | | | -> | 2 | 1 | | -> | 3 | 2 | 1 | -> + -- | 1 | | | -> | 1 | 2 | | -> | 1 | 2 | 3 | -> -- | | | | | | | | | | | | -- +---+---+---+ +---+---+---+ +---+---+---+ -- (4) (5) (6) -- +---+---+---+ +---+---+---+ +---+---+---+ - -- | 4 | | | | 5 | 4 | | | 6 | 5 | 4 | + -- | 1 | | | | 1 | 2 | | | 1 | 2 | 3 | -- +---+---+---+ -> +---+---+---+ -> +---+---+---+ - -- | 3 | 2 | 1 | | 3 | 2 | 1 | | 3 | 2 | 1 | + -- | 2 | 3 | 4 | | 3 | 4 | 5 | | 4 | 5 | 6 | -- +---+---+---+ +---+---+---+ +---+---+---+ - -- How many vertical columns? Read from nmaster on the tag. - local num_x = tonumber(termfair.nmaster) or t.master_count - local ncol = tonumber(termfair.ncol) or t.column_count - - if num_x <= 2 then num_x = 2 end - if ncol <= 1 then ncol = 1 end - local width = math.floor(wa.width/num_x) - local num_y = math.max(math.ceil(#cls / num_x), ncol) local height = math.floor(wa.height/num_y) local cur_num_x = num_x @@ -107,6 +107,56 @@ local function do_fair(p, orientation) 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 | -> + -- | | | | | | | | | | | | + -- +---+---+---+ +---+---+---+ +---+---+---+ + + -- (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) + + 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 + + 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 + + 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 @@ -128,15 +178,6 @@ local function do_fair(p, orientation) -- | | | 4 | | | 3 | 5 | -- +---+---+---+ +---+---+---+ - -- How many vertical columns? Read from nmaster on the tag. - local num_x = tonumber(termfair.center.nmaster) or t.master_count - local ncol = tonumber(termfair.center.ncol) or t.column_count - - if num_x <= 2 then num_x = 2 end - if ncol <= 1 then ncol = 1 end - - local width = math.floor(wa.width / num_x) - if #cls < num_x then -- Less clients than the number of columns, let's center it! local offset_x = wa.x + (wa.width - #cls*width) / 2 @@ -199,8 +240,8 @@ local function do_fair(p, orientation) for i = 1, (num_x-1) do local height = math.floor(wa.height / num_y[i]) local wy = wa.y - for j = 0, (num_y[i]-2) do - local g = {} + for _ = 0, (num_y[i]-2) do + g = {} g.x = wx g.y = wy g.height = height @@ -211,7 +252,7 @@ local function do_fair(p, orientation) nclient = nclient + 1 wy = wy + height end - local g = {} + g = {} g.x = wx g.y = wy g.height = wa.height - (num_y[i] - 1)*height @@ -230,6 +271,10 @@ function termfair.center.arrange(p) return do_fair(p, "center") end +function termfair.stable.arrange(p) + return do_fair(p, "stable") +end + function termfair.arrange(p) return do_fair(p, "west") end diff --git a/util/dkjson.lua b/util/dkjson.lua index 89aa2e1..fb6cedd 100644 --- a/util/dkjson.lua +++ b/util/dkjson.lua @@ -42,8 +42,8 @@ SOFTWARE. --]==] -- global dependencies: -local pairs, type, tostring, tonumber, getmetatable, setmetatable, rawset = - pairs, type, tostring, tonumber, getmetatable, setmetatable, rawset +local pairs, type, tostring, tonumber, getmetatable, setmetatable = + pairs, type, tostring, tonumber, getmetatable, setmetatable local error, require, pcall, select = error, require, pcall, select local floor, huge = math.floor, math.huge local strrep, gsub, strsub, strbyte, strchar, strfind, strlen, strformat = @@ -246,7 +246,7 @@ local function exception(reason, value, state, buffer, buflen, defaultmessage) end end -function json.encodeexception(reason, value, state, defaultmessage) +function json.encodeexception(_, _, _, defaultmessage) return quotestring("<" .. defaultmessage .. ">") end @@ -321,7 +321,7 @@ encode2 = function (value, indent, level, buffer, buflen, tables, globalorder, s local v = value[k] if v then used[k] = true - buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state) + buflen, _ = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state) prev = true -- add a seperator before the next element end end @@ -504,7 +504,6 @@ end local scanvalue -- forward declaration local function scantable (what, closechar, str, startpos, nullval, objectmeta, arraymeta) - local len = strlen (str) local tbl, n = {}, 0 local pos = startpos + 1 if what == 'object' then @@ -626,7 +625,7 @@ function json.use_lpeg () local PlainChar = 1 - S"\"\\\n\r" local EscapeSequence = (P"\\" * g.C (S"\"\\/bfnrt" + Err "unsupported escape sequence")) / escapechars local HexDigit = R("09", "af", "AF") - local function UTF16Surrogate (match, pos, high, low) + local function UTF16Surrogate (_, _, high, low) high, low = tonumber (high, 16), tonumber (low, 16) if 0xD800 <= high and high <= 0xDBff and 0xDC00 <= low and low <= 0xDFFF then return true, unichar ((high - 0xD800) * 0x400 + (low - 0xDC00) + 0x10000) diff --git a/util/init.lua b/util/init.lua index 55bfa26..f7d3088 100644 --- a/util/init.lua +++ b/util/init.lua @@ -39,7 +39,7 @@ function util.menu_clients_current_tags(menu, args) local t = cls_tags[i] local cls = t:clients() - for k, c in pairs(cls) do + for _, c in pairs(cls) do cls_t[#cls_t + 1] = { awful.util.escape(c.name) or "", function () c.minimized = false @@ -99,7 +99,7 @@ end function util.tag_view_nonempty(direction, sc) local s = sc or awful.screen.focused() - for i = 1, #s.tags do + for _ = 1, #s.tags do awful.tag.viewidx(direction, s) if #s.clients > 0 then return diff --git a/util/menu_iterator.lua b/util/menu_iterator.lua index 9959b25..d457473 100644 --- a/util/menu_iterator.lua +++ b/util/menu_iterator.lua @@ -12,7 +12,6 @@ local naughty = require("naughty") local helpers = require("lain.helpers") -local util = require("lain.util") local atable = require("awful.util").table local assert = assert local pairs = pairs @@ -42,8 +41,8 @@ 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) - local timeout = timeout or 4 -- default timeout for each menu entry - local 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 @@ -104,7 +103,7 @@ local function menu(args) local ch_combinations = args.combination == "powerset" and helpers.powerset(choices) or helpers.trivial_partition_set(choices) - for _,c in pairs(extra_choices) do + for _, c in pairs(extra_choices) do ch_combinations = atable.join(ch_combinations, {{c[1]}}) end diff --git a/util/quake.lua b/util/quake.lua index 01891b0..8bc68a7 100644 --- a/util/quake.lua +++ b/util/quake.lua @@ -50,7 +50,7 @@ function quake:display() if not client then -- The client does not exist, we spawn it - cmd = string.format("%s %s %s", self.app, + 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 @@ -60,6 +60,8 @@ function quake:display() 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 @@ -74,15 +76,21 @@ function quake:display() -- 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 + else + self.maximized = maximized + self.fullscreen = fullscreen + client.maximized = false + client.fullscreen = false client.hidden = true local ctags = client:tags() - for i, t in pairs(ctags) do - ctags[i] = nil + for j, _ in pairs(ctags) do + ctags[j] = nil end client:tags(ctags) end @@ -114,8 +122,22 @@ function quake:compute_size() return self.geometry[self.screen.index] end -function quake:new(config) - local conf = config or {} +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 +end + +function quake.new(conf) + conf = conf or {} conf.app = conf.app or "xterm" -- application to spawn conf.name = conf.name or "QuakeDD" -- window name @@ -135,6 +157,9 @@ function quake:new(config) conf.horiz = conf.horiz or "left" -- left, right or center conf.geometry = {} -- internal use + conf.maximized = false + conf.fullscreen = false + local dropdown = setmetatable(conf, { __index = quake }) capi.client.connect_signal("manage", function(c) @@ -151,18 +176,4 @@ function quake:new(config) return dropdown 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 -end - -return setmetatable(quake, { __call = function(_, ...) return quake:new(...) end }) +return setmetatable(quake, { __call = function(_, ...) return quake.new(...) end }) diff --git a/util/separators.lua b/util/separators.lua index 465132d..ae384ec 100644 --- a/util/separators.lua +++ b/util/separators.lua @@ -21,19 +21,19 @@ function separators.arrow_right(col1, col2) widget.col1 = col1 widget.col2 = col2 - widget.fit = function(m, w, h) + widget.fit = function(_, _, _) return separators.width, separators.height end - widget.update = function(col1, col2) + widget.update = function(_, _) widget.col1 = col1 widget.col2 = col2 widget:emit_signal("widget::redraw_needed") end - widget.draw = function(mycross, wibox, cr, width, height) + widget.draw = function(_, _, cr, width, height) if widget.col2 ~= "alpha" then - cr:set_source_rgb(gears.color.parse_color(widget.col2)) + cr:set_source_rgba(gears.color.parse_color(widget.col2)) cr:new_path() cr:move_to(0, 0) cr:line_to(width, height/2) @@ -50,7 +50,7 @@ function separators.arrow_right(col1, col2) end if widget.col1 ~= "alpha" then - cr:set_source_rgb(gears.color.parse_color(widget.col1)) + cr:set_source_rgba(gears.color.parse_color(widget.col1)) cr:new_path() cr:move_to(0, 0) cr:line_to(width, height/2) @@ -69,19 +69,19 @@ function separators.arrow_left(col1, col2) widget.col1 = col1 widget.col2 = col2 - widget.fit = function(m, w, h) + widget.fit = function(_, _, _) return separators.width, separators.height end - widget.update = function(col1, col2) - widget.col1 = col1 - widget.col2 = col2 + widget.update = function(c1, c2) + widget.col1 = c1 + widget.col2 = c2 widget:emit_signal("widget::redraw_needed") end - widget.draw = function(mycross, wibox, cr, width, height) + widget.draw = function(_, _, cr, width, height) if widget.col1 ~= "alpha" then - cr:set_source_rgb(gears.color.parse_color(widget.col1)) + cr:set_source_rgba(gears.color.parse_color(widget.col1)) cr:new_path() cr:move_to(width, 0) cr:line_to(0, height/2) @@ -104,7 +104,7 @@ function separators.arrow_left(col1, col2) cr:line_to(width, height) cr:close_path() - cr:set_source_rgb(gears.color.parse_color(widget.col2)) + cr:set_source_rgba(gears.color.parse_color(widget.col2)) cr:fill() end end diff --git a/widget/alsa.lua b/widget/alsa.lua index 3b6c6d6..cd095e2 100644 --- a/widget/alsa.lua +++ b/widget/alsa.lua @@ -15,8 +15,8 @@ local string = string -- lain.widget.alsa local function factory(args) - local alsa = { widget = wibox.widget.textbox() } - local args = args or {} + 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 diff --git a/widget/alsabar.lua b/widget/alsabar.lua index 9b9f4b9..8e8cd3a 100644 --- a/widget/alsabar.lua +++ b/widget/alsabar.lua @@ -30,15 +30,19 @@ local function factory(args) _playback = "off" } - local 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 " " alsabar.cmd = args.cmd or "amixer" alsabar.channel = args.channel or "Master" @@ -48,8 +52,7 @@ local function factory(args) alsabar.notification_preset = args.notification_preset if not alsabar.notification_preset then - alsabar.notification_preset = {} - alsabar.notification_preset.font = "Monospace 10" + alsabar.notification_preset = { font = "Monospace 10" } end local format_cmd = string.format("%s get %s", alsabar.cmd, alsabar.channel) @@ -115,22 +118,32 @@ local function factory(args) 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 + local tot = alsabar.notification_preset.max_ticks - -- 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 + 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 = wib.height + tot = 20 end end - int = math.modf((alsabar._current_level / 100) * tot) - preset.text = string.format("[%s%s]", string.rep("|", int), - string.rep(" ", tot - int)) + 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 diff --git a/widget/bat.lua b/widget/bat.lua index 3cb801c..77324de 100644 --- a/widget/bat.lua +++ b/widget/bat.lua @@ -26,8 +26,9 @@ local function factory(args) return end - local bat = { widget = wibox.widget.textbox() } - local 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 @@ -42,7 +43,7 @@ local function factory(args) if bstr then batteries[#batteries + 1] = bstr else - ac = string.match(line, "A%w+") or "AC0" + ac = string.match(line, "A%w+") or ac end end) end @@ -136,7 +137,7 @@ local function factory(args) -- "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] + 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 diff --git a/widget/cal.lua b/widget/cal.lua index 928a7bd..91f9b47 100644 --- a/widget/cal.lua +++ b/widget/cal.lua @@ -126,14 +126,14 @@ local function factory(args) end function cal.show(seconds, month, year, scr) - cal.notification_preset.text = tconcat(cal.build(month, year)) + 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) - cal.notification_preset.text = string.format("%s\n\n%s\n\n%s", - tconcat(cal.build(prev_month, prev_year)), cal.notification_preset.text, + 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 @@ -143,13 +143,14 @@ local function factory(args) 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 + 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) - local offset = offset or 0 + offset = offset or 0 cal.month, cal.year = cal.getdate(cal.month, cal.year, offset) cal.show(0, cal.month, cal.year) end diff --git a/widget/contrib/moc.lua b/widget/contrib/moc.lua index f429c77..ad6452e 100644 --- a/widget/contrib/moc.lua +++ b/widget/contrib/moc.lua @@ -18,8 +18,9 @@ local string = string -- lain.widget.contrib.moc local function factory(args) - local moc = { widget = wibox.widget.textbox() } - local 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)$" diff --git a/widget/contrib/redshift.lua b/widget/contrib/redshift.lua index d0e5eed..d91d941 100644 --- a/widget/contrib/redshift.lua +++ b/widget/contrib/redshift.lua @@ -15,7 +15,7 @@ local type = type -- lain.widget.contrib.redshift local redshift = { active = false, pid = nil } -function redshift:start() +function redshift.start() execute("pkill redshift") awful.spawn.with_shell("redshift -x") -- clear adjustments redshift.pid = awful.spawn.with_shell("redshift") @@ -25,14 +25,14 @@ function redshift:start() end end -function redshift:toggle() +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() + redshift.start() end redshift.update_fun(redshift.active) end) @@ -43,11 +43,11 @@ end -- @param widget: Widget to attach to. -- @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) +function redshift.attach(widget, fun) redshift.update_fun = fun or function() end - if not redshift.pid then redshift:start() 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))) + widget:buttons(awful.util.table.join(awful.button({}, 1, function () redshift.toggle() end))) end end diff --git a/widget/contrib/task.lua b/widget/contrib/task.lua index 536e006..2311996 100644 --- a/widget/contrib/task.lua +++ b/widget/contrib/task.lua @@ -10,7 +10,6 @@ local markup = require("lain.util").markup local awful = require("awful") local naughty = require("naughty") local mouse = mouse -local string = string -- Taskwarrior notification -- lain.widget.contrib.task @@ -69,7 +68,8 @@ function task.prompt() end function task.attach(widget, args) - local 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 diff --git a/widget/contrib/tp_smapi.lua b/widget/contrib/tp_smapi.lua index b8acbe2..87c5e51 100644 --- a/widget/contrib/tp_smapi.lua +++ b/widget/contrib/tp_smapi.lua @@ -8,7 +8,6 @@ local helpers = require("lain.helpers") local focused = require("awful.screen").focused -local gears = require("gears") local naughty = require("naughty") local wibox = require("wibox") local string = string @@ -61,7 +60,7 @@ local function factory(apipath) 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 msg if status and status ~= "idle" then msg = string.format("[%s] %s %s", status, time ~= "N/A" and time or "unknown remaining time", @@ -80,7 +79,8 @@ local function factory(apipath) end function tp_smapi.create_widget(args) - local args = args or {} + 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 @@ -95,7 +95,7 @@ local function factory(apipath) local all_batteries_installed = true - for i, battery in ipairs(batteries) do + for _, battery in ipairs(batteries) do if not tp_smapi.installed(battery) then naughty.notify { preset = naughty.config.critical, diff --git a/widget/cpu.lua b/widget/cpu.lua index 81638c2..c2e28b0 100644 --- a/widget/cpu.lua +++ b/widget/cpu.lua @@ -10,14 +10,14 @@ local helpers = require("lain.helpers") local wibox = require("wibox") local math = math local string = string -local tostring = tostring -- CPU usage -- lain.widget.cpu local function factory(args) - local cpu = { core = {}, widget = wibox.widget.textbox() } - local 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 @@ -25,9 +25,7 @@ local function factory(args) -- Read the amount of time the CPUs have spent performing -- different kinds of work. Read the first line of /proc/stat -- which is the sum of all CPUs. - local times = helpers.lines_match("cpu","/proc/stat") - - for index,time in pairs(times) do + 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 } diff --git a/widget/fs.lua b/widget/fs.lua index 58fbf93..b3a2dad 100644 --- a/widget/fs.lua +++ b/widget/fs.lua @@ -12,11 +12,11 @@ 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 tonumber = tonumber local query_size = Gio.FILE_ATTRIBUTE_FILESYSTEM_SIZE local query_free = Gio.FILE_ATTRIBUTE_FILESYSTEM_FREE local query_used = Gio.FILE_ATTRIBUTE_FILESYSTEM_USED @@ -26,8 +26,10 @@ local query = query_size .. "," .. query_free .. "," .. query_used -- lain.widget.fs local function factory(args) + args = args or {} + local fs = { - widget = wibox.widget.textbox(), + widget = args.widget or wibox.widget.textbox(), units = { [1] = "Kb", [2] = "Mb", [3] = "Gb", [4] = "Tb", [5] = "Pb", [6] = "Eb", @@ -42,15 +44,16 @@ local function factory(args) end function fs.show(seconds, scr) - fs.hide(); fs.update() - 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 - } + 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 args = args or {} local timeout = args.timeout or 600 local partition = args.partition local threshold = args.threshold or 99 @@ -68,12 +71,11 @@ local function factory(args) } end - function fs.update() - local notifytable = { [1] = string.format("%-10s %4s\t%6s\t%6s\t\n", "path", "used", "free", "size") } + local function update_synced() local pathlen = 10 - local maxpathidx = 1 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) @@ -90,19 +92,16 @@ local function factory(args) fs_now[path] = { units = fs.units[units], percentage = math.floor(100 * used / size), -- used percentage - size = size / math.pow(1024, math.floor(units)), - used = used / math.pow(1024, math.floor(units)), - free = free / math.pow(1024, math.floor(units)) + 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 - notifytable[#notifytable+1] = string.format("\n%-10s %3s%%\t%6.2f\t%6.2f\t%s", path, - math.floor(fs_now[path].percentage), fs_now[path].free, fs_now[path].size, - fs_now[path].units) + notifypaths[#notifypaths+1] = path if #path > pathlen then pathlen = #path - maxpathidx = #notifytable end end end @@ -125,19 +124,25 @@ local function factory(args) end end - if pathlen > 10 then -- if are there paths longer than 10 chars, reformat first column accordingly - local pathspaces - for i = 1, #notifytable do - pathspaces = notifytable[i]:match("[ ]+") - if i ~= maxpathidx and pathspaces then - notifytable[i] = notifytable[i]:gsub(pathspaces, pathspaces .. string.rep(" ", pathlen - 10)) - 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 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 + 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) diff --git a/widget/imap.lua b/widget/imap.lua index b3d9dc7..e3f7baa 100644 --- a/widget/imap.lua +++ b/widget/imap.lua @@ -17,8 +17,9 @@ local tonumber = tonumber -- lain.widget.imap local function factory(args) - local imap = { widget = wibox.widget.textbox() } - local 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 diff --git a/widget/mem.lua b/widget/mem.lua index 3dcae2b..0318494 100644 --- a/widget/mem.lua +++ b/widget/mem.lua @@ -14,8 +14,9 @@ local gmatch, lines, floor = string.gmatch, io.lines, math.floor -- lain.widget.mem local function factory(args) - local mem = { widget = wibox.widget.textbox() } - local 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 diff --git a/widget/mpd.lua b/widget/mpd.lua index 01f28e6..143e085 100644 --- a/widget/mpd.lua +++ b/widget/mpd.lua @@ -19,8 +19,9 @@ local string = string -- lain.widget.mpd local function factory(args) - local mpd = { widget = wibox.widget.textbox() } - local 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" diff --git a/widget/net.lua b/widget/net.lua index 805b577..49e60f4 100644 --- a/widget/net.lua +++ b/widget/net.lua @@ -15,8 +15,9 @@ local string = string -- lain.widget.net local function factory(args) - local net = { widget = wibox.widget.textbox(), devices = {} } - local 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" @@ -29,13 +30,14 @@ local function factory(args) net.iface = (args.iface and (type(args.iface) == "string" and {args.iface}) or (type(args.iface) == "table" and args.iface)) or {} - function net.get_device() + 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_device() end + if #net.iface == 0 then net.get_devices() end function net.update() -- These are the totals over all specified interfaces @@ -67,13 +69,19 @@ local function factory(args) 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" and string.match(dev_now.carrier, "1") then + if wifi_state == "on" and helpers.first_line(string.format("/sys/class/net/%s/uevent", dev)) == "DEVTYPE=wlan" then dev_now.wifi = true - dev_now.signal = tonumber(string.match(helpers.lines_from("/proc/net/wireless")[3], "(%-%d+%.)")) or nil + 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" and string.match(dev_now.carrier, "1") then + 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 diff --git a/widget/pulse.lua b/widget/pulse.lua index f63fe55..69f4d70 100644 --- a/widget/pulse.lua +++ b/widget/pulse.lua @@ -15,8 +15,9 @@ local type = type -- lain.widget.pulse local function factory(args) - local pulse = { widget = wibox.widget.textbox(), device = "N/A" } - local 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 diff --git a/widget/pulsebar.lua b/widget/pulsebar.lua index 317468f..19e73b9 100644 --- a/widget/pulsebar.lua +++ b/widget/pulsebar.lua @@ -21,9 +21,10 @@ local tonumber = tonumber local function factory(args) local pulsebar = { colors = { - background = "#000000", - mute = "#EB8F8F", - unmute = "#A4CE8A" + background = "#000000", + mute_background = "#000000", + mute = "#EB8F8F", + unmute = "#A4CE8A" }, _current_level = 0, @@ -31,7 +32,8 @@ local function factory(args) device = "N/A" } - local 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 @@ -40,6 +42,10 @@ local function factory(args) 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 @@ -100,10 +106,12 @@ local function factory(args) 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() @@ -137,9 +145,14 @@ local function factory(args) end end - int = math.modf((pulsebar._current_level / 100) * tot) - preset.text = string.format("[%s%s]", string.rep("|", int), - string.rep(" ", tot - int)) + 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 @@ -154,7 +167,7 @@ local function factory(args) end) end - helpers.newtimer(string.format("pulsebar-%s", pulsebar.sink), timeout, pulsebar.update) + helpers.newtimer(string.format("pulsebar-%s-%s", pulsebar.devicetype, pulsebar.device), timeout, pulsebar.update) return pulsebar end diff --git a/widget/sysload.lua b/widget/sysload.lua index adf3e03..7260524 100644 --- a/widget/sysload.lua +++ b/widget/sysload.lua @@ -14,8 +14,9 @@ local open, match = io.open, string.match -- lain.widget.sysload local function factory(args) - local sysload = { widget = wibox.widget.textbox() } - local 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 diff --git a/widget/temp.lua b/widget/temp.lua index 3c93252..05723f5 100644 --- a/widget/temp.lua +++ b/widget/temp.lua @@ -7,33 +7,37 @@ local helpers = require("lain.helpers") local wibox = require("wibox") -local open = io.open local tonumber = tonumber --- coretemp +-- {thermal,core} temperature info -- lain.widget.temp local function factory(args) - local temp = { widget = wibox.widget.textbox() } - local args = args or {} - local timeout = args.timeout or 2 - local tempfile = args.tempfile or "/sys/class/thermal/thermal_zone0/temp" + 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 settings = args.settings or function() end function temp.update() - local f = open(tempfile) - if f then - coretemp_now = tonumber(f:read("*all")) / 1000 - f:close() - else - coretemp_now = "N/A" - end - - widget = temp.widget - settings() + 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 + coretemp_now = temp_now[tempfile] or "N/A" + widget = temp.widget + settings() + end) end - helpers.newtimer("coretemp", timeout, temp.update) + helpers.newtimer("thermal", timeout, temp.update) return temp end diff --git a/widget/weather.lua b/widget/weather.lua index c998ed9..9cf1ac6 100644 --- a/widget/weather.lua +++ b/widget/weather.lua @@ -21,18 +21,17 @@ local tonumber = tonumber -- lain.widget.weather local function factory(args) - local weather = { widget = wibox.widget.textbox() } - local args = args or {} + args = args or {} + + local weather = { widget = args.widget or wibox.widget.textbox() } local APPID = args.APPID or "3e321f9414eaedbfab34983bda77a66e" -- lain's default local timeout = args.timeout or 60 * 15 -- 15 min - local timeout_forecast = args.timeout or 60 * 60 * 24 -- 24 hrs - local current_call = args.current_call or "curl -s 'http://api.openweathermap.org/data/2.5/weather?id=%s&units=%s&lang=%s&APPID=%s'" - local forecast_call = args.forecast_call or "curl -s 'http://api.openweathermap.org/data/2.5/forecast/daily?id=%s&units=%s&lang=%s&cnt=%s&APPID=%s'" + 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/daily?id=%s&units=%s&lang=%s&cnt=%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 date_cmd = args.date_cmd or "date -u -d @%d +'%%a %%d'" 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 @@ -68,7 +67,7 @@ local function factory(args) preset = notification_preset, text = weather.notification_text, icon = weather.icon_path, - timeout = type(seconds == "number") and seconds or notification_preset.timeout + timeout = type(seconds) == "number" and seconds or notification_preset.timeout } end @@ -91,8 +90,8 @@ local function factory(args) function weather.forecast_update() local cmd = string.format(forecast_call, city_id, units, lang, cnt, APPID) helpers.async(cmd, function(f) - local pos, err - weather_now, pos, err = json.decode(f, 1, nil) + 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 = "" @@ -110,27 +109,14 @@ local function factory(args) function weather.update() local cmd = string.format(current_call, city_id, units, lang, APPID) helpers.async(cmd, function(f) - local pos, err, icon - weather_now, pos, err = json.decode(f, 1, nil) + 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() -- local time - local loc_m = os.time { year = os.date("%Y"), month = os.date("%m"), day = os.date("%d"), hour = 0 } -- local time from midnight - local loc_d = os.date("*t", loc_now) -- table YMDHMS for current local time (for TZ calculation) - local utc_d = os.date("!*t", loc_now) -- table YMDHMS for current UTC time - local utc_now = os.time(utc_d) -- UTC time now - local offdt = (loc_d.isdst and 1 or 0) * 3600 + 100 * (loc_d.min - utc_d.min) / 60 -- DST offset - local offset = os.difftime(loc_now, utc_now) + (loc_d.isdst and 1 or 0) * 3600 + 100 * (loc_d.min - utc_d.min) / 60 -- TZ offset (including DST) - local offday = (offset < 0 and -86400) or 86400 -- 24 hour correction value (+86400 or -86400) - - -- if current UTC time is earlier then local midnight -> positive offset (negative otherwise) - if offset * (loc_m - utc_now + offdt) > 0 then - sunrise = sunrise + offday -- Shift sunset and sunrise times by 24 hours - sunset = sunset + offday - end + local loc_now = os.time() if sunrise <= loc_now and loc_now <= sunset then icon = string.gsub(icon, "n", "d") diff --git a/wiki b/wiki index e5a195c..ddc6aa0 160000 --- a/wiki +++ b/wiki @@ -1 +1 @@ -Subproject commit e5a195cfc013627f21d242fa5110b3added00eb2 +Subproject commit ddc6aa0649d4fd091c1a73784d0507feb86eaf5f