Improve awesome conf
[~madcoder/dotfiles.git] / config / awesome / rc.lua
index 5a838ca..a27550b 100644 (file)
@@ -2,11 +2,12 @@
 
 -- Include awesome library, with lots of useful function!
 require("awful")
-require("eminent")
+require("awful.autofocus")
+require("awful.rules")
 require("vicious")
 require("beautiful")
 
-terminal = "x-terminal-emulator"
+terminal = "urxvtcd"
 lock     = 'xscreensaver-command -lock'
 beautiful.init(awful.util.getdir("config").."/theme")
 
@@ -16,19 +17,19 @@ shift = "Shift"
 alt = "Mod1"
 control = "Control"
 
-k_n  = {}
-k_m  = {modkey}
-k_ms = {modkey, shift}
-k_ma = {modkey, alt}
-k_mc = {modkey, control}
-k_a  = {alt}
-k_ac = {alt, control}
-k_c  = {control}
-k_cs = {control, shift}
-k_s  = {shift}
+k_n   = {}
+k_m   = {modkey}
+k_ms  = {modkey, shift}
+k_ma  = {modkey, alt}
+k_mc  = {modkey, control}
+k_mcs = {modkey, control, shift}
+k_a   = {alt}
+k_ac  = {alt, control}
+k_c   = {control}
+k_cs  = {control, shift}
+k_s   = {shift}
 
 -- }}}
-
 -- {{{ Markup helper functions
 -- Inline markup is a tad ugly, so use these functions
 -- to dynamically create markup.
@@ -36,39 +37,180 @@ function fg(color, text)
     return '<span color="'..color..'">'..text..'</span>'
 end
 
-function heading(text)
-    return fg(beautiful.fg_focus, text)
-end
-
 -- }}}
--- {{{ Functions
--- Toggle whether we're viewing a tag
-function tag_toggleview(tag)
-    tag:view(not tag:isselected())
+
+-- {{{ Tags
+
+tags = {}
+for s = 1, screen.count() do
+    -- Each screen has its own tag table.
+    local p = {}
+    for i = 1, 10 do
+        table.insert(p, "work-"..i)
+    end
+    tags[s] = awful.tag(p, s, awful.layout.suit.tile)
 end
 
 -- Get the screen number we're on
-function getscreen()
-    local sel = client.focus
-    return (sel and sel.screen) or mouse.screen
+local mtag = { }
+function mtag.getn(idx, s)
+    return tags[s or mouse.screen][idx]
 end
+function mtag.viewonly (idx, s)
+    local t = mtag.getn(idx, s)
+    if t then awful.tag.viewonly(t) end
+end
+function mtag.viewtoggle (idx, s)
+    local t = mtag.getn(idx, s)
+    if t then awful.tag.viewtoggle(t) end
+end
+function mtag.isoccupied(s, t)
+    local clients = (t and t:clients()) or {}
+    return #clients > 0
+end
+function mtag.occupied(s)
+    local p = {}
 
--- Move current client to a specific screen
-function client_movetoscreen(i)
-    client.focus.screen = i
+    if not s then s = mouse.screen end
+    for t in pairs(tags[s]) do
+        t = tags[s][t]
+        if mtag.isoccupied(s, t) then table.insert(p, t) end
+    end
+    return p
 end
+function mtag.getnext(s)
+    if s == nil then s = mouse.screen end
 
--- }}}
--- {{{ Set tag names
+    local p = mtag.occupied(s)
+    local curtag = awful.tag.selected()
 
-for s = 1, screen.count() do
-    eminent.newtag(s, 5)
-    for i = 1, 10 do
-        eminent.tag.name(i, s, 'work-'..i)
+    local t = 0
+
+    -- Get tag #
+    if mtag.isoccupied(s, curtag) then
+        for x in pairs(p) do
+            if curtag == p[x] then t = x end
+        end
+    end
+
+    local lasto = 1
+    local o = 0
+
+    for x in pairs(tags[s]) do
+        if curtag == tags[s][x] then o = x end
+        if mtag.isoccupied(s, tags[s][x]) then lasto = x end
+    end
+
+    -- Now: t is # in non-empty, o is # in all
+    if o == table.maxn(tags[s]) then
+        -- We're the last tag, create a new one
+        if t == 0 then
+            -- We're empty, go to first
+            return tags[s][1]
+        else
+            -- We're occupied, create new
+            return newtag()
+        end
+    else
+        if t == 0 then
+            -- We're empty, check if we're last
+            if o > lasto then
+                -- We're also later than the last non-empty
+                -- Wrap to first
+                return tags[s][1]
+            else
+                -- Nevermind, get the next
+                return tags[s][o+1]
+            end
+        else
+            -- Return next tag
+            return tags[s][o+1]
+        end
+    end
+end
+function mtag.next(s)
+    awful.tag.viewonly(mtag.getnext(s))
+end
+function mtag.movetonext(s)
+    local t = mtag.getnext(s)
+    awful.client.movetotag(t)
+    awful.tag.viewonly(t)
+end
+function mtag.getprev(s)
+    if s == nil then s = mouse.screen end
+
+    local p = mtag.occupied(s)
+    local curtag = awful.tag.selected()
+
+    local t = 0
+
+    -- Get tag #
+    if mtag.isoccupied(s, curtag) then
+        for x in pairs(p) do
+            if curtag == p[x] then t = x end
+        end
+    end
+
+    local lasto = 1
+    local o = 0
+
+    for x in pairs(tags[s]) do
+        if curtag == tags[s][x] then o = x end
+        if mtag.isoccupied(s, tags[s][x]) then lasto = x end
     end
+
+    -- Now: t is # in non-empty, o is # in all
+    if o == 1 then
+        -- We're the very first tag, wrap around
+        return p[ table.maxn(p) ]
+    else
+        -- We're not first, just go prev
+        return tags[s][o-1]
+    end
+end
+function mtag.prev(s)
+    awful.tag.viewonly(mtag.getprev(s))
 end
+function mtag.movetoprev(s)
+    local t = mtag.getprev(s)
+    awful.client.movetotag(t)
+    awful.tag.viewonly(t)
+end
+
 
 -- }}}
+-- {{{ Widgets
+
+local icondir = awful.util.getdir("config").."/icons/"
+
+function make_icon(fname, left, right, h, bg)
+    local icon = image(icondir..fname)
+    local ib   = widget { type = 'imagebox' }
+    local w    = left + icon.width + right
+    local i    = image.argb32(w, h, nil)
+
+    i:draw_rectangle(0, 0, w, h, true, beautiful.bg_normal)
+    i:insert(icon, left, math.floor((h - icon.height) / 2))
+
+    ib.image = i
+    return ib
+end
+
+function draw_dashes_h(img, x, y, on, off, len, color)
+    for i = 0, len, on + off do
+        img:draw_line(x + i, y, x + i + on - 1, y, color)
+    end
+end
+
+function draw_dashes_v(img, x, y, on, off, len, color)
+    for i = 0, len, on + off do
+        img:draw_line(x, y + i, x, y + i + on - 1, color)
+    end
+end
+
+
+local mywidgets = {}
+
 -- {{{ Taglist
 
 maintaglist = {}
@@ -76,51 +218,66 @@ maintaglist.buttons = awful.util.table.join(
     awful.button(k_n, 1, awful.tag.viewonly),
     awful.button(k_s, 1, awful.client.toggletag)
 )
+maintaglist.label = awful.widget.taglist.label.noempty
 
 -- }}}
--- {{{ Widgets
+-- {{{ Prompt box
 
--- {{{ Load Average Widget
-
-loadwidget = widget({ type = 'textbox' })
-load_update = function()
-    -- Use /proc/loadavg to get the average system load on 1, 5 and 15 minute intervals
-    local f = io.open('/proc/loadavg')
-    local n = f:read()
-    f:close()
+mypromptbox = awful.widget.prompt{
+    layout = awful.widget.layout.horizontal.leftright
+}
 
-    -- Find the third space
-    local pos = n:find(' ', n:find(' ', n:find(' ')+1)+1)
+-- }}}
+-- {{{ CPU Widgets
+
+local cpuwidgets = {}
+for i = 1, io.popen("grep -c ^cpu /proc/stat"):read("*n") do
+    local cpu = {}
+
+    cpu.widget = widget{ type = 'imagebox' }
+    cpu.update = function(cpu, value)
+        local color
+
+        if value < 25 then
+            color = beautiful.fg_focus
+        elseif value < 50 then
+            color = 'yellow'
+        elseif value < 75 then
+            color = 'orange'
+        else
+            color = 'red'
+        end
 
-    loadwidget.text = heading('Load')..': '..n:sub(1,pos-1)
+        if cpu.color == color then return end
+        local img = image.argb32(11, 16, nil)
+        draw_dashes_h(img, 0,  4, 2, 1, 7, color)
+        draw_dashes_h(img, 0, 11, 2, 1, 7, color)
+        draw_dashes_v(img, 0,  4, 2, 1, 7, color)
+        draw_dashes_v(img, 7,  4, 2, 1, 7, color)
+        img:draw_rectangle(2, 6, 4, 4, true, color)
+        cpu.widget.image = img
+        cpu.percent = value
+    end
+    table.insert(mywidgets, cpu.widget)
+    cpuwidgets[i] = cpu
 end
-load_update()
 
-t = timer { timeout = 10 }
-t:add_signal("timeout", load_update)
-t:start()
-
--- }}}
--- {{{ CPU Usage Widget
-
-cputextwidget = widget({ type = 'textbox' })
-cpugraphwidget = awful.widget.graph{
-    width  = 40, height = 16,
-    layout = awful.widget.layout.horizontal.rightleft
-}
-cpugraphwidget:set_background_color('#333333')
-cpugraphwidget:set_border_color('#0a0a0a')
-cpugraphwidget:set_gradient_colors({ '#285577', '#285577', '#AEC6D8' })
+cputextwidget = widget{ type = 'textbox' }
 
 vicious.register(cputextwidget, vicious.widgets.cpu,
 function (widget, args)
     local r = tonumber(args[1])
     local percent = args[1]..'%'
+
+    for i = 1, #args do
+        cpuwidgets[i]:update(args[i])
+    end
+
     if r < 10 then
         percent = '0'..percent
     end
     if r < 25 then
-        percent = fg('green', percent)
+        percent = fg(beautiful.fg_focus, percent)
     elseif r < 50 then
         percent = fg('yellow', percent)
     elseif r < 75 then
@@ -128,19 +285,18 @@ function (widget, args)
     else
         percent = fg('red', percent)
     end
-    cpugraphwidget:add_value(r / 100)
-    return heading('CPU')..': '..percent..' '
+    return percent..' '
 end, 2)
 
--- }}}
--- {{{ CPU Graph Widget
+table.insert(mywidgets, cputextwidget)
 
 -- }}}
 -- {{{ Memory Usage Widget
 
-memtextwidget = widget({ type = 'textbox' })
+table.insert(mywidgets, make_icon('mem.png', 16, 4, 16))
+
+memtextwidget = widget{ type = 'textbox' }
 
-memtextwidget.text = heading('MEM')..': '
 vicious.register(memtextwidget, vicious.widgets.mem, function (widget, args)
     -- Add extra preceding zeroes when needed
     local r = tonumber(args[1])
@@ -149,89 +305,70 @@ vicious.register(memtextwidget, vicious.widgets.mem, function (widget, args)
         percent = '0'..percent
     end
     if r < 50 then
-        percent = fg('green', percent)
+        percent = fg(beautiful.fg_focus, percent)
     elseif r < 80 then
         percent = fg('orange', percent)
     else
         percent = fg('red', percent)
     end
-    return heading('MEM')..': '..percent..' '..args[2]..'M'
+    return percent..' '..args[2]..'M'
 end, 2)
 
--- }}}
--- {{{ spacers
-
-rspacer = widget({ type = 'textbox' })
-rspacer.text = " │ "
+table.insert(mywidgets, memtextwidget)
 
 -- }}}
 -- {{{ Clock
 
-clockwidget = widget({ type = "textbox" })
-vicious.register(clockwidget, vicious.widgets.date, fg("#dddddd", "%a %d %b - %H:%M"), 10)
+table.insert(mywidgets, make_icon('clock.png', 16, 4, 16))
 
--- }}}
+clockwidget = widget{ type = "textbox" }
+vicious.register(clockwidget, vicious.widgets.date,
+                 fg(theme.fg_focus, "%H:%M") .. " %a %d %b  ", 10)
 
-mymenubox = widget{ type = "textbox" }
+table.insert(mywidgets, clockwidget)
 
+-- }}}
 -- {{{ Statusbar
 
-mainstatusbar = {}
-
 for s = 1, screen.count() do
-    mainstatusbar[s] = awful.wibox{ position = "top", height = 18, screen = s }
-
-    mainstatusbar[s].widgets = {
+    local tab = {
         {
-            awful.widget.taglist(s, awful.widget.taglist.label.noempty, maintaglist.buttons),
-            maintaglist,
-            awful.widget.prompt({ layout = awful.widget.layout.horizontal.leftright }),
+            awful.widget.taglist(s, maintaglist.label, maintaglist.buttons),
+            mypromptbox.widget,
             layout = awful.widget.layout.horizontal.leftright
         },
-        clockwidget, rspacer,
-        memtextwidget, rspacer,
-        cpugraphwidget, cputextwidget, rspacer,
-        loadwidget, rspacer,
-        layout = awful.widget.layout.horizontal.rightleft
     }
+
+    if (s == 1) then
+        table.insert(tab, widget{ type = 'systray' })
+    end
+    for i = #mywidgets, 1, -1 do
+        table.insert(tab, mywidgets[i])
+    end
+    tab["layout"] = awful.widget.layout.horizontal.rightleft
+    awful.wibox{ position = "top", height = 16, screen = s }.widgets = tab
 end
 
 -- }}}
 -- }}}
-
 -- {{{ Keys
 ---- {{{ Global keys
 
-local hist = os.getenv("HOME") .. "/.cache/awesome/history"
 globalkeys = awful.util.table.join(
     -- Mod+{A/S}: Switch to prev/next tag
-    awful.key(k_m, "Left",  eminent.tag.prev),
-    awful.key(k_m, "Right", eminent.tag.next),
+    awful.key(k_m, "Left",  mtag.prev),
+    awful.key(k_m, "Right", mtag.next),
 
     -- Mod+Shift+{A/S}: Move window to Prev/Next tag
-    awful.key(k_ms, "Left", function()
-        awful.client.movetotag(eminent.tag.getprev())
-        eminent.tag.prev()
-    end),
-    awful.key(k_ms, "Right", function()
-        awful.client.movetotag(eminent.tag.getnext())
-        eminent.tag.next()
-    end),
+    awful.key(k_ms, "Left",  mtag.movetoprev),
+    awful.key(k_ms, "Right", mtag.movetonext),
 
     -- Mod+Shift_{E/D}: move window to next/prev screen
     awful.key(k_mc, "Right", function()
-       local s = getscreen() + 1
-       while s > screen.count() do
-           s = s-screen.count()
-       end
-       client_movetoscreen(s)
+       client.focus.screen = awful.util.cycle(screen.count(), mouse.screen + 1)
     end),
     awful.key(k_mc, "Left", function()
-       local s = getscreen() - 1
-       while s < 1 do
-           s = s+screen.count()
-       end
-       client_movetoscreen(s)
+       client.focus.screen = awful.util.cycle(screen.count(), mouse.screen - 1)
     end),
 
 
@@ -252,8 +389,8 @@ globalkeys = awful.util.table.join(
     awful.key(k_ms, "k", function () awful.client.swap.byidx(-1) end),
 
     -- Mod+{E/D}: Switch to next/previous screen
-    awful.key(k_m, "Tab",  function () awful.screen.focus(1) end),
-    awful.key(k_ms, "Tab", function () awful.screen.focus(-1) end),
+    awful.key(k_m, "Tab",  function () awful.screen.focus_relative(1) end),
+    awful.key(k_ms, "Tab", function () awful.screen.focus_relative(-1) end),
 
     -- Mod+Enter: Launch a new terminal
     awful.key(k_m,  "e",      function() awful.util.spawn("firefox") end),
@@ -271,28 +408,20 @@ globalkeys = awful.util.table.join(
     awful.key(k_mc, "l", function () awful.tag.incncol(-1) end),
 
     -- Menu
-    awful.key(k_m, "r",
-        function ()
-            awful.prompt.run({ prompt = "Run: " },
-                             mymenubox,
-                             awful.util.spawn,
-                             awful.completion.shell,
-                             awful.util.getdir("cache").."/commands")
-        end),
+    awful.key(k_m, "r", function () mypromptbox:run() end),
     awful.key(k_m, "F4",
         function ()
             awful.prompt.run({ prompt = "Run Lua code: " },
-                             mymenubox,
-                             awful.util.eval,
-                             awful.prompt.shell,
+                             mypromptbox.widget,
+                             awful.util.eval, nil,
                              awful.util.getdir("cache").."/lua_commands")
     end),
 
-    awful.key({}, "#192", function() eminent.tag.goto(1, nil, true) end),
-    awful.key({}, "#193", function() eminent.tag.goto(2, nil, true) end),
-    awful.key({}, "#194", function() eminent.tag.goto(3, nil, true) end),
-    awful.key({}, "#195", function() eminent.tag.goto(4, nil, true) end),
-    awful.key({}, "#196", function() eminent.tag.goto(5, nil, true) end)
+    awful.key({}, "#192", function() mtag.viewonly(1) end),
+    awful.key({}, "#193", function() mtag.viewonly(2) end),
+    awful.key({}, "#194", function() mtag.viewonly(3) end),
+    awful.key({}, "#195", function() mtag.viewonly(4) end),
+    awful.key({}, "#196", function() mtag.viewonly(5) end)
 )
 
 -- Mod+#: Switch to tag
@@ -303,53 +432,89 @@ globalkeys = awful.util.table.join(
 for i = 1, 10 do
     globalkeys = awful.util.table.join(
         globalkeys,
-        awful.key(k_m, i % 10,
-            function()
-                eminent.tag.goto(i, nil, true)
-            end),
-
-        awful.key(k_ms, i % 10,
-            function ()
-                local t = eminent.tag.getn(i, nil, true)
-                if t ~= nil then t.selected = not t.selected end
-            end),
+        awful.key(k_m,  i % 10, function() mtag.viewonly(i)   end),
+        awful.key(k_ms, i % 10, function() mtag.viewtoggle(i) end),
         awful.key(k_mc, i % 10,
-            function ()
-                local t = eminent.tag.getn(i, nil, true)
-                if t ~= nil then awful.client.movetotag(t) end
-            end)
+                  function ()
+                      if client.focus and tags[client.focus.screen][i] then
+                          awful.client.movetotag(tags[client.focus.screen][i])
+                      end
+                  end),
+        awful.key(k_mcs, i % 10,
+                  function ()
+                      if client.focus and tags[client.focus.screen][i] then
+                          awful.client.toggletag(tags[client.focus.screen][i])
+                      end
+                  end)
     )
 end
 
 ---- }}}
----- {{{ Client hotkeys
+---- {{{ Client hotkeys / buttons
 
-clientkeys = awful.util.table.join(
+local clientkeys = awful.util.table.join(
     awful.key(k_m, "i", function (c)
-        if mymenubox.text then
-            mymenubox.text = ""
+        if mypromptbox.widget.text then
+            mypromptbox.widget.text = ""
         else
-            mymenubox.text = "Class: " .. c.class .. " Instance: ".. c.instance
+            mypromptbox.widget.text = "Class: " .. c.class .. " Instance: ".. c.instance
         end
     end),
 
     -- Client manipulation
     awful.key(k_m, "c", function (c) c:kill() end),
     awful.key(k_m, "o", awful.client.floating.toggle),
-    awful.key(k_m, "t", awful.client.togglemarked),
     awful.key(k_m, "F11", function (c) c.fullscreen = not c.fullscreen end)
 )
 
+local clientbuttons = awful.util.table.join(
+    awful.button({ }, 1, function (c) client.focus = c; c:raise() end),
+    awful.button(k_a, 1, awful.mouse.client.move),
+    awful.button(k_a, 3, awful.mouse.client.resize)
+)
+
 ---- }}}
 
 root.keys(globalkeys)
 -- }}}
--- {{{ signals
+-- {{{ Rules
+
+awful.rules.rules = {
+    -- All clients will match this rule.
+    {
+        rule = { },
+        properties = {
+            border_width = beautiful.border_width,
+            border_color = beautiful.border_normal,
+            focus   = true,
+            keys    = clientkeys,
+            buttons = clientbuttons,
+            size_hints_honor = false,
+        }
+    },
+    { rule = { class = "MPlayer" },  properties = { floating = true } },
+    { rule = { class = "pinentry" }, properties = { floating = true } },
+    -- Set Firefox to always map on tags number 2 of screen 1.
+    -- { rule = { class = "Firefox" },
+    --   properties = { tag = tags[1][2] } },
+}
+
+-- }}}
+-- {{{ Signals
+
+function warp_mouse(c)
+    local o = mouse.object_under_pointer()
+    if not o or (o ~= c and type(o) == "client") then
+        local g = c:geometry()
+        mouse.coords({ x = g.x + 5, y = g.y + 5 }, true)
+    end
+end
 
 client.add_signal("focus", function (c)
     if not awful.client.ismarked(c) then
         c.border_color = beautiful.border_focus
     end
+    warp_mouse(c)
 end)
 
 client.add_signal("unfocus", function (c)
@@ -358,69 +523,19 @@ client.add_signal("unfocus", function (c)
     end
 end)
 
-client.add_signal("marked", function (c)
-    c.border_color = beautiful.border_marked
-end)
-
-client.add_signal("unmarked", function (c)
-    c.border_color = beautiful.border_focus
-end)
-
 client.add_signal("manage", function (c, startup)
-    if not startup and awful.client.focus.filter(c) then
-        c.screen = mouse.screen
-    end
-
-    -- Add mouse bindings
-    c:buttons(awful.util.table.join(
-        awful.button({ }, 1, function (c) client.focus = c; c:raise() end),
-        awful.button(k_a, 1, awful.mouse.client.move),
-        awful.button(k_a, 3, awful.mouse.client.resize)
-    ))
-
-    -- Enable sloppy focus
-    c:add_signal("mouse::enter", function(c)
-        client.focus = c
-    end)
-
-    -- Create border
-    c.border_width = beautiful.border_width
-    c.border_color = beautiful.border_normal
-
-    -- Make certain windows floating
-    local class = c.class:lower()
-    if class:find('pinentry')
-    or class:find('kcalc')
-    or class:find('gajim')
-    then
-        c.floating = true
-    end
-
-    -- Focus new clients
-    client.focus = c
-    c:keys(clientkeys)
-    c.size_hints_honor = false
+    c:add_signal("mouse::enter", function(c) client.focus = c end)
 end)
 
--- Hook function to execute when arranging the screen
--- (tag switch, new client, etc)
-client.add_signal("arrange", function (screen)
-    local sel = client.focus
-
-    if not sel then
-        sel = awful.client.focus.history.get(screen, 0)
-        if not sel then return end
-        client.focus = sel
-    end
-
-    local o = mouse.object_under_pointer()
-    if not o or (type(o) == "client" and o ~= sel) then
-        local g = sel:geometry()
-
-        mouse.coords { x = g.x + 5, y = g.y + 5 }
-    end
-end)
+for s = 1, screen.count() do
+    screen[s]:add_signal("arrange", function ()
+        if client.focus and client.focus.screen == s then
+            warp_mouse(client.focus)
+        end
+    end)
+end
 
 -- }}}
 
 awful.util.spawn("xkbcomp -w 0 -R/usr/share/X11/xkb /home/madcoder/.Xkeyboard :0")
+awful.util.spawn("xsetroot -cursor_name left_ptr")