awesome/my.lua
author Fabien Ninoles <fabien@tzone.org>
Sat, 03 Jan 2015 11:21:36 -0500
changeset 9 56e80afa3e1c
parent 8 0eae48be1d62
child 11 7226709dc4c4
permissions -rw-r--r--
Only start sloppy timer on a mouse_enter. This avoid a bug when the focus is stolen by another window.

require("naughty")
require("awful")
require("debug")
require("vicious")
require("wicked")
require("beautiful")
require("timer")

local ipairs = ipairs
local pairs = pairs
local naughty = naughty
local awful = awful
local debug = debug
local vicious = vicious
local wicked = wicked
local beautiful = beautiful
local timer = timer

local capi = {
   keygrabber = keygrabber,
   client = client,
   string = string,
   widget = widget,
}

module("my")

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

   naughty.notify({ preset = naughty.config.presets.critical,
                    title = "Oops, an error happened!",
                    text = err })
   in_error = false
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(awful.rules.any)
   awesome.quit()
end

function show_clients()
   if instance then
      instance:hide()
      instance = nil
   else
      instance = awful.menu.clients({width=250})
   end
end

local function widgets_cpu()
   local w = awful.widget.graph()
   -- Graph properties
   w:set_width(30)
   w:set_background_color("#729fcf")
   w:set_gradient_colors({ "#729fcf", "LightBlue", "white" })
-- Register widget
   vicious.register(w, vicious.widgets.cpu, "$1", 1)
   return w
end

local function widgets_mdp()
   -- Initialize widget
   local w = capi.widget({ type = "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("#729fcf")
   w:set_gradient_colors({ "#729fcf", "LightBlue", "white" })
-- Register widget
   vicious.register(w, vicious.widgets.mem, "$1", 5)
   return w
end

local function widgets_net(device)
   local w = capi.widget({ type = "textbox"})
   -- w:set_vertical(true)
   w.width = 150
   w.align = 'center'
   vicious.register(w, wicked.widgets.net,
                    '${' .. device .. ' up} / ${' .. device .. ' down}',
                   1)
   return w
end

local function widgets_mode()
   local w = capi.widget({ type = "textbox"})
   widgets.mode_widget = w
   return w
end

local function widgets_keyboard()
   local w = capi.widget({ type = "textbox"})
   widgets.keyboard_widget = w
   return w
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,
   mdp = widgets_mdp,
   mode = widgets_mode,
   keyboard = widgets_keyboard,
   decorated = widgets_decorated,
}

function make_key(k, n, f)
   return { key = k, name = n, func = f}
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 skey
end

local function kt_handler(keytable, mod, key, event)
   if event == "release" then 
      return true 
   end
   local theme = beautiful.get()
   widgets.mode_widget.bg = theme.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+?" 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 = theme.bg_urgent
   return true
end

local function stop_kt()
   capi.keygrabber.stop()
   widgets.mode_widget.visible = false
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.text = "Mode: " .. title
   widgets.mode_widget.visible = true
   widgets.mode_widget.bg = "LightBlue"
   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
      t = awful.util.table.join(t, awful.key(modifiers, k.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:add_signal("timeout", sloppy_on_timeout)
   sloppy_timer:start()
   capi.client.add_signal("manage", function (c)
                             c:add_signal("mouse::enter", sloppy_mouse_enter)
   end)
   capi.client.add_signal("focus", function () sloppy_timer:stop() end)
end