awesome/my.lua
author Fabien Ninoles <fabien@tzone.org>
Sat, 16 Apr 2016 13:41:51 -0400
changeset 17 171cba5d3e3a
parent 15 73df43769340
child 18 f9712f62d202
permissions -rw-r--r--
Add disk I/O widget.

local os = require("os")
local io = require("io")
local debug = require("debug")
local table = require("table")
local awesome = require("awesome")
local naughty = require("naughty")
local awful = require("awful")
local vicious = require("vicious")
local wicked = require("wicked")
local beautiful = require("beautiful")
local timer = require("timer")
local wibox = require("wibox")

local ipairs = ipairs
local pairs = pairs

local capi = {
   keygrabber = require("keygrabber"),
   client = require("client"),
   string = require("string"),
}

module("my")

--- Stolen from shifty: Shows a popup and logs to a file
-- @param message The text message.
-- @param log_level 1 = INFO, 2 = WARN, 3 = ERROR, if nothing is provided 1 is used.
function log(message, log_level)
  if log_level == nil then
    log_level = 1
  end

  local log_table = {
    { level = "INFO", bg_colour = "#18F92C", fg_colour  = "#0E0E0E" },
    { level = "WARN", bg_colour = "#9E731F", fg_colour  = "#0E0E0E" },
    { level = "ERROR", bg_colour = "#FF0015", fg_colour  = "#000000" }
  }
  -- %c eg: Wed Jan 30 14:25:13 2013
  local time = os.date("%c")
  message = time .. " - " .. log_table[log_level].level .. " - " .. message ..  "\n"
  tb = debug.traceback()

  local home = os.getenv("HOME")
  local log_file = io.open(home .. "/.awesome.log", "a+")
  log_file:write(message .. tb .. "\n")
  log_file:close()

  naughty.notify({ preset = naughty.config.presets.critical, text = message, bg = log_table[log_level].bg_colour, fg = log_table[log_level].fg_colour})
end

local in_error = false
function mk_notify_error_dialog(level)
   return function(msg)
      -- Make sure we don't go into an endless error loop
      if in_error then return end
      in_error = true

      log(msg, level)
      
      in_error = false
   end
end
   
notify_error = mk_notify_error_dialog(3)

local function get_gradient_colors(c1, c2)
   return { type = "linear", from = { 0, 0 }, to = { 0, 20 }, stops = { { 0, c2 }, { 1, c1 } }}
end

function mkspawn(p)
   return function () awful.util.spawn(p) end
end

function setxkbmap(kb)
   XKBDIR="$HOME/.xkb"
   -- Strange bug on xkbcomp: pushing directly the output to the display
   -- lead to an error; We need to convert it first to .xkb and then feed
   -- xkbcomp again for setting the x display
   p = 'setxkbmap "' .. kb .. " -print | xkbcomp -xkb -a -I" .. XKBDIR .. " - - | kxbcomp - $DISPLAY"
   awful.util.spawn_with_shell(p)
end

function kill_all(rule)
   for c in awful.client.iterate(rule) do
      c:kill()
   end
end

function quit()
   kill_all(function (c) return true end)
   awesome.quit()
end

local function widgets_cpu()
   local w = awful.widget.graph()
   -- Graph properties
   w:set_width(30)
   w:set_background_color(beautiful.bg_normal)
   w:set_color(get_gradient_colors(beautiful.bg_focus, beautiful.bg_urgent))
-- Register widget
   vicious.register(w, vicious.widgets.cpu, "$1", 1)
   return w
end

local function widgets_mdp()
   -- Initialize widget
   local w = wibox.widget.textbox()
   -- Register widget
   vicious.register(w, vicious.widgets.mpd,
                    function (widget, args)
                       if args["{state}"] == "Stop" then 
                          return " - "
                       else 
                          return args["{Artist}"]..' - '.. args["{Title}"]
                       end
                    end, 10)
   return w
end

local function widgets_mem(monitor)
-- Initialize widget
   local w = awful.widget.progressbar()
-- Progressbar properties
   w:set_width(10)
   w:set_vertical(true)
   w:set_background_color(beautiful.bg_normal)
   w:set_color(get_gradient_colors(beautiful.bg_focus, beautiful.bg_urgent))
-- Register widget
   vicious.register(w, vicious.widgets.mem, "$1", 5)
   return w
end

local function widgets_net(device)
   local w = wibox.widget.textbox()
   -- w:set_vertical(true)
   w.width = 150
   w:set_align('center')
   vicious.register(w, wicked.widgets.net,
                    '<tt>${' .. device .. ' up} ▴ ${' .. device .. ' down} ▾</tt>',
                   1)
   return w
end

local function widgets_dio(devices)
   local w = wibox.widget.textbox()
   -- w:set_vertical(true)
   w.width = 150
   w:set_align('center')
   local format = '<tt> '
   for _, device in ipairs(devices) do
      format = format .. device .. ': ${' .. device .. ' total_mb} '
   end
   format = format .. '</tt>'

   vicious.register(w, vicious.widgets.dio, format, 1)
   return w
end

local function widgets_mode()
   local bg = wibox.widget.background()
   local w = wibox.widget.textbox()
   bg:set_widget(w)
   widgets.mode_widget = w
   widgets.mode_widget_bg = bg
   return bg
end

local function widgets_decorated(w, onclick)
   local wg = w
   if w.widget then
      wg = w.widget
   end
   wg:buttons(awful.util.table.join(
                awful.button({}, 1, onclick)))
   return w
end

widgets = {
   cpu = widgets_cpu,
   mem = widgets_mem,
   net = widgets_net,
   dio = widgets_dio,
   mdp = widgets_mdp,
   mode = widgets_mode,
   decorated = widgets_decorated,

}

function split_modifiers(key)
   local modifiers = {}
   local pattern = capi.string.format("([^+]+)", sep)
   key:gsub(pattern, function(mod) table.insert(modifiers, mod) end)
   key = table.remove(modifiers)
   return modifiers, key
end

function normalize_key(k)
   mods, key = split_modifiers(k)
   if #mods > 0 then
      table.sort(mods)
      k = table.concat(mods, "+") .. "+" .. key
   end
   return k
end
   
function make_key(k, n, f)
   k = normalize_key(k)
   return { key = k, name = n, func = f}
end

function make_single_key(k, n, f)
   if f == nil then
      log("name: " .. n .. " is nil", 3)
   end
   return make_key(k, n, function (c) f(c); return false ; end)
end

function make_interactive_key(k, n, f)
   return make_key(k, n, function (c) f(c); return true; end)
end

function show_kt(keytable, title)
   -- todo: replace with a menu ?
   text = ""
   for _, k in ipairs(keytable) do
      text = text .. "\'" .. k.key .. "\'\t" .. k.name .. "\n"
   end
   naughty.notify({text = text, 
                   title = title, 
                   position = "top_left",
   })
end

local keynames = {
   [" "] = "space",
   ["\t"] = "tab",
}

local ignored_mods = {
   ["Mod2"] = true,
}

local function translate_key(mod, key)
   local skey = ""
   for m,v in pairs(mod) do
      if not ignored_mods[v] then 
         skey = skey .. v .. "+"
      end
   end
   skey = skey .. (keynames[key] or key)
   return normalize_key(skey)
end

local function kt_handler(keytable, mod, key, event)
   if event == "release" then 
      return true 
   end
   widgets.mode_widget_bg:set_bg(beautiful.bg_focus)
   if key == "Escape" then   
      return false
   end
   local skey = capi.string.lower(translate_key(mod, key))
   if skey == "control+h" or skey == "shift+?" or skey == "f1" then
      show_kt(keytable, "Current binding")
      return true
   end
   for _,k in ipairs(keytable) do
      if skey == capi.string.lower(k.key) then
         return k.func(c)
      end
   end
   -- notify_error("Nothing for " .. skey)
   widgets.mode_widget_bg:set_bg(beautiful.bg_urgent)
   return true
end

local function stop_kt()
   capi.keygrabber.stop()
   widgets.mode_widget:set_text("")
end

function mkinteractive(f)
  return function (c)
     stop_kt()
     return f(c)
  end
end


-- local is_in_run_kt = false
function run_kt(c, keytable, title)
   widgets.mode_widget:set_text("Mode: " .. title)
   -- widgets.mode_widget:show()
   widgets.mode_widget_bg:set_bg(beautiful.bg_focus)
   widgets.mode_widget:buttons(
      awful.util.table.join(
         awful.button({}, 1, function () show_kt(keytable, "Binding") end),
         awful.button({}, 3, stop_kt)))
   capi.keygrabber.run(function (mod, key, event)
         if not kt_handler(keytable, mod, key, event) then
            stop_kt()
            return false
         end
         return true
   end)
end

function make_kt(keytable, title)
   return function (c) 
      -- if is_in_run_kt then
      --    -- notify_error("Already in run_kt:\n" .. debug.traceback())
      --    is_in_run_kt = false -- reset
      --    return false
      -- end
      return run_kt(c, keytable, title)
   end
end

function make_focus_bydirection(direction)
   return function ()
      awful.client.focus.bydirection(direction)
      if capi.client.focus then capi.client.focus:raise() end
      return true
   end
end
      
function make_globalkeys(modifiers, keytable)
   local t = {}
   for _,k in ipairs(keytable) do
      local mods, key = split_modifiers(k.key)
      mods = awful.util.table.join(modifiers, mods)
      -- log("name=" .. k.name .. "; modifiers=" .. table.concat(mods, ":") .. "; key=" .. key)
      if key == "Space" then
         key = capi.string.lower(key)
      end
      t = awful.util.table.join(t, awful.key(mods, key, k.func))
   end
   return t
end

-- Install a sloppy-like focus with a timeout.

function sloppy_toggle()
   sloppy = not sloppy
end

function sloppy_mouse_enter(c)
   if sloppy and
      c ~= sloppy_last_client and
      awful.client.focus.filter(c) and
      awful.layout.get(c.screen) ~= awful.layout.suit.magnifier
   then
      if sloppy_timer.started
      then
         sloppy_timer:again()
      else
         sloppy_timer:start()
      end
      sloppy_last_client = c
   end
end

function sloppy_on_timeout()
   if sloppy
   then
      local c = awful.mouse.client_under_pointer()
      if c and
         c == sloppy_last_client
      then
         capi.client.focus = c
         sloppy_timer:stop()
      end
    end
end

sloppy = true
sloppy_last_client = nil
sloppy_timer = nil

function sloppy_install(delay)
   sloppy_timer = timer({ timeout = delay })
   sloppy_timer:connect_signal("timeout", sloppy_on_timeout)
   sloppy_timer:start()
   capi.client.connect_signal("manage", function (c)
                             c:connect_signal("mouse::enter", sloppy_mouse_enter)
   end)
   capi.client.connect_signal("focus", function ()
                                 if sloppy_timer.started then
                                    sloppy_timer:stop()
                                 end
   end)
end

-- theme menu
function theme_load(theme)
   local cfg_path = awful.util.getdir("config")
   awful.util.spawn("ln -sfn " .. cfg_path .. "/themes/" .. theme .. " " .. cfg_path .. "/current_theme")
   awesome.restart()
end

function theme_menu()
   -- List your theme files and feed the menu table
   local cmd = "ls -1 " .. awful.util.getdir("config") .. "/themes/"
   local f = io.popen(cmd)

   local themes = {}
   for l in f:lines() do
	  local item = { l, function () theme_load(l) end }
	  table.insert(themes, item)
   end

   f:close()
   return themes
end

function set_wallpaper(wallpaper)
   local cfg_path = awful.util.getdir("config")
   local source_path = cfg_path .. "/wallpapers/" .. wallpaper
   local default_path = cfg_path .. "/default"
   awful.util.spawn("ln -sfn " .. source_path .. " " .. default_path )
   awful.util.spawn("fbsetbg -a " .. default_path)
end

function wallpaper_menu()
   local cmd = "ls " .. awful.util.getdir("config") .. "/wallpapers/"
   local f = io.popen(cmd)
   local wallpapers = {}
   for l in f:lines() do
      local name, ext = capi.string.match(l, "(.-)(%.[^%.]+)$")
      local item = { name,  function () set_wallpaper(l) end }
      table.insert(wallpapers, item)
   end

   f:close()
   return wallpapers
end