All patches and comments are welcome. Please squash your changes to logical
commits before using git-format-patch and git-send-email to
patches@git.madduck.net.
If you'd read over the Git project's submission guidelines and adhered to them,
I'd be especially grateful.
2 -- Standard awesome library
3 local gears = require("gears")
4 local awful = require("awful")
5 require("awful.autofocus")
6 -- Widget and layout library
7 local wibox = require("wibox")
8 -- Theme handling library
9 local beautiful = require("beautiful")
10 -- Notification library
11 local naughty = require("naughty")
12 local menubar = require("menubar")
13 local hotkeys_popup = require("awful.hotkeys_popup").widget
14 local lain = require("lain")
15 local dkjson = require("lain.util").dkjson
16 local math = require("math")
20 -- Check if awesome encountered an error during startup and fell back to
21 -- another config (This code will only ever execute for the fallback config)
22 if awesome.startup_errors then
23 naughty.notify({ preset = naughty.config.presets.critical,
24 title = "Oops, there were errors during startup!",
25 text = awesome.startup_errors })
28 -- Handle runtime errors after startup
30 local in_error = false
31 awesome.connect_signal("debug::error", function (err)
32 -- Make sure we don't go into an endless error loop
33 if in_error then return end
36 naughty.notify({ preset = naughty.config.presets.critical,
37 title = "Oops, an error happened!",
38 text = tostring(err) })
44 -- {{{ Variable definitions
45 -- Themes define colours, icons, and wallpapers
46 beautiful.init(awful.util.get_themes_dir() .. "default/theme.lua")
48 -- This is used later as the default terminal and editor to run.
49 terminal = "x-terminal-emulator"
50 editor = "sensible-editor"
51 editor_cmd = terminal .. " -e " .. editor
54 -- Usually, Mod4 is the key with a logo between Control and Alt.
55 -- If you do not like this or do not have such a key,
56 -- I suggest you to remap Mod4 to another key using xmodmap or other tools.
57 -- However, you can use another modifier like Mod1, but it may interact with others.
61 -- Table of layouts to cover with awful.layout.inc, order matters.
62 awful.layout.layouts = {
63 awful.layout.suit.fair,
64 awful.layout.suit.tile,
65 -- awful.layout.suit.tile.left,
66 -- awful.layout.suit.tile.bottom,
67 awful.layout.suit.tile.top,
68 -- awful.layout.suit.spiral,
69 -- awful.layout.suit.spiral.dwindle,
70 awful.layout.suit.max,
71 -- awful.layout.suit.max.fullscreen,
72 -- awful.layout.suit.magnifier,
73 -- awful.layout.suit.corner.nw,
74 -- awful.layout.suit.corner.ne,
75 -- awful.layout.suit.corner.sw,
76 -- awful.layout.suit.corner.se,
77 awful.layout.suit.floating,
80 layout_default = awful.layout.layouts[1]
81 layout_tiled = awful.layout.layouts[2]
82 layout_maximised = awful.layout.layouts[4]
83 layout_floating = awful.layout.layouts[5]
86 -- {{{ Helper functions
87 local function client_menu_toggle_fn()
91 if instance and instance.wibox.visible then
95 instance = awful.menu.clients({ theme = { width = 250 } })
100 local function sorted_pairs(t, f)
102 for n in pairs(t) do table.insert(a, n) end
104 local i = 0 -- iterator variable
105 local iter = function () -- iterator function
107 if a[i] == nil then return nil
108 else return a[i], t[a[i]]
114 local function print_table(tbl, indent)
115 if not indent then indent = 0 end
116 for k, v in pairs(tbl) do
117 formatting = string.rep(" ", indent) .. k .. ": "
118 if type(v) == "table" then
120 print_table(v, indent+1)
122 print(formatting .. tostring(v))
127 local lain_bat = lain.widget.bat({
128 batteries = {"BAT0", "BAT1"},
129 settings = function()
131 if bat_now.status == "Charging" then delim = "↑"
132 elseif bat_now.status == "Unknown" then delim = "٭" end
133 widget:set_text(bat_now.perc .. "% " .. delim .. " " .. bat_now.time)
137 local function poloniex_price(output, pair, prec)
138 local xc, pos, err = dkjson.decode(output, 1, nil)
139 if not prec then prec = 4 end
140 val = (xc and xc[pair]["last"]) or 0
141 val = math.floor(val*10^prec+0.5)/10^prec
142 return (not err and val) or "n/a"
145 local eth_widget = lain.widget.watch({
146 cmd = "curl -m5 -s 'https://poloniex.com/public?command=returnTicker'",
148 settings = function()
149 widget:set_text(poloniex_price(output, 'BTC_ETH') .. " Ƀ/Ξ")
153 local function coindesk_price(output, base, prec)
154 local xc, pos, err = dkjson.decode(output, 1, nil)
155 if not prec then prec = 4 end
156 val = (xc and xc["bpi"][base]["rate_float"]) or 0
157 val = math.floor(val*10^prec+0.5)/10^prec
158 return (not err and val) or "n/a"
161 local btc_widget = lain.widget.watch({
162 cmd = "curl -m5 -Ls 'https://api.coindesk.com/v1/bpi/currentprice/EUR.json'",
164 settings = function()
165 widget:set_text(coindesk_price(output, "EUR", 2) .. " €/Ƀ")
172 -- Create a launcher widget and a main menu
174 { "hotkeys", function() return false, hotkeys_popup.show_help end},
175 { "manual", terminal .. " -e man awesome" },
176 { "edit config", editor_cmd .. " " .. awesome.conffile },
177 { "restart", awesome.restart },
178 { "quit", awesome.quit }
181 mymainmenu = awful.menu({ items = { { "awesome", myawesomemenu, beautiful.awesome_icon },
182 { "open terminal", terminal }
186 mylauncher = awful.widget.launcher({ image = beautiful.awesome_icon,
190 -- {{{ Menubar configuration
191 menubar.utils.terminal = terminal -- Set the terminal for applications that require it
195 local spacer = wibox.widget.textbox()
196 spacer:set_text(' | ')
197 -- Create a textclock widget
198 mytextclock = wibox.widget.textclock("%a %d %b %H:%M:%S", 1)
200 -- Create a wibox for each screen and add it
205 mytaglist.buttons = awful.util.table.join(
206 awful.button({ }, 1, function(t) t:view_only() end),
207 awful.button({ modkey }, 1, function(t)
209 client.focus:move_to_tag(t)
212 awful.button({ }, 3, awful.tag.viewtoggle),
213 awful.button({ modkey }, 3, function(t)
215 client.focus:toggle_tag(t)
218 awful.button({ }, 4, function(t) awful.tag.viewnext(t.screen) end),
219 awful.button({ }, 5, function(t) awful.tag.viewprev(t.screen) end)
223 mytasklist.buttons = awful.util.table.join(
224 awful.button({ }, 1, function (c)
225 if c == client.focus then
226 -- I don't like click-minimising
227 -- c.minimized = true
229 -- Without this, the following
230 -- :isvisible() makes no sense
232 if not c:isvisible() and c.first_tag then
233 c.first_tag:view_only()
235 -- This will also un-minimize
236 -- the client, if needed
241 awful.button({ }, 3, client_menu_toggle_fn()),
242 awful.button({ }, 4, function ()
243 awful.client.focus.byidx(1)
245 awful.button({ }, 5, function ()
246 awful.client.focus.byidx(-1)
253 tags.config["main"] = {
254 t1 = { layout = layout_default, selected = true },
255 t2 = { layout = layout_default },
256 t3 = { layout = layout_tiled },
257 t4 = { layout = layout_tiled },
258 t5 = { layout = layout_tiled },
259 t6 = { layout = layout_floating },
260 t7 = { layout = layout_maximised },
261 t8 = { layout = layout_maximised },
262 t9 = { layout = layout_maximised },
264 tags.config["aux"] = {
265 t1 = { layout = layout_default, selected = true },
266 t2 = { layout = layout_default },
267 t3 = { layout = layout_tiled },
268 t4 = { layout = layout_floating },
269 t5 = { layout = layout_floating },
270 t6 = { layout = layout_floating },
271 t7 = { layout = layout_floating },
272 t8 = { layout = layout_floating },
273 t9 = { layout = layout_maximised },
277 screentags[1] = tags.config["main"]
278 if screen.count() == 2 then -- aux screen is on the right
279 screentags[2] = tags.config["aux"]
280 elseif screen.count() == 3 then -- main screen is still #1 in the middle
281 screentags[2] = tags.config["aux"]
282 screentags[3] = tags.config["aux"]
285 awful.screen.connect_for_each_screen(function(s)
287 --DISABLED--if beautiful.wallpaper then
288 --DISABLED-- local wallpaper = beautiful.wallpaper
289 --DISABLED-- -- If wallpaper is a function, call it with the screen
290 --DISABLED-- if type(wallpaper) == "function" then
291 --DISABLED-- wallpaper = wallpaper(s)
293 --DISABLED-- gears.wallpaper.maximized(wallpaper, s, true)
296 if not tags[s.index] then
299 for n,p in sorted_pairs(screentags[s.index]) do
301 n = string.sub(n, 2) -- remove leading 't' needed for syntax in table
302 table.insert(tags[s.index], awful.tag.add(n, p))
305 -- Create a promptbox for each screen
306 mypromptbox[s] = awful.widget.prompt()
307 -- Create an imagebox widget which will contains an icon indicating which layout we're using.
308 -- We need one layoutbox per screen.
309 mylayoutbox[s] = awful.widget.layoutbox(s)
310 mylayoutbox[s]:buttons(awful.util.table.join(
311 awful.button({ }, 1, function () awful.layout.inc( 1) end),
312 awful.button({ }, 3, function () awful.layout.inc(-1) end),
313 awful.button({ }, 4, function () awful.layout.inc( 1) end),
314 awful.button({ }, 5, function () awful.layout.inc(-1) end)))
315 -- Create a taglist widget
316 mytaglist[s] = awful.widget.taglist(s, awful.widget.taglist.filter.all, mytaglist.buttons)
318 -- Create a tasklist widget
319 mytasklist[s] = awful.widget.tasklist(s, awful.widget.tasklist.filter.currenttags, mytasklist.buttons)
322 mywibox[s] = awful.wibar({ position = "top", screen = s })
324 -- Add widgets to the wibox
326 layout = wibox.layout.align.horizontal,
328 layout = wibox.layout.fixed.horizontal,
333 mytasklist[s], -- Middle widget
335 layout = wibox.layout.fixed.horizontal,
337 wibox.widget.systray(),
349 -- {{{ Mouse bindings
350 root.buttons(awful.util.table.join(
351 awful.button({ }, 3, function () mymainmenu:toggle() end),
352 awful.button({ }, 4, awful.tag.viewnext),
353 awful.button({ }, 5, awful.tag.viewprev)
358 globalkeys = awful.util.table.join(
359 awful.key({ modkey, }, "s", hotkeys_popup.show_help,
360 {description="show help", group="awesome"}),
361 awful.key({ modkey, }, "Left", awful.tag.viewprev,
362 {description = "view previous", group = "tag"}),
363 awful.key({ modkey, }, "Right", awful.tag.viewnext,
364 {description = "view next", group = "tag"}),
365 awful.key({ modkey, }, "Escape", awful.tag.history.restore,
366 {description = "go back", group = "tag"}),
368 awful.key({ modkey, }, "k",
370 awful.client.focus.byidx( 1)
372 {description = "focus next by index", group = "client"}
374 awful.key({ modkey, }, "j",
376 awful.client.focus.byidx(-1)
378 {description = "focus previous by index", group = "client"}
380 awful.key({ modkey, }, "w", function () mymainmenu:show() end,
381 {description = "show main menu", group = "awesome"}),
383 -- Layout manipulation
384 awful.key({ modkey, "Shift" }, "k", function () awful.client.swap.byidx( 1) end,
385 {description = "swap with next client by index", group = "client"}),
386 awful.key({ modkey, "Shift" }, "j", function () awful.client.swap.byidx( -1) end,
387 {description = "swap with previous client by index", group = "client"}),
388 awful.key({ modkey, "Control" }, "k", function () awful.screen.focus_relative( 1) end,
389 {description = "focus the next screen", group = "screen"}),
390 awful.key({ modkey, "Control" }, "j", function () awful.screen.focus_relative(-1) end,
391 {description = "focus the previous screen", group = "screen"}),
392 awful.key({ modkey, }, "u", awful.client.urgent.jumpto,
393 {description = "jump to urgent client", group = "client"}),
394 awful.key({ modkey, }, "Tab",
396 awful.client.focus.history.previous()
401 {description = "go back", group = "client"}),
404 awful.key({ modkey, }, "Return", function () awful.spawn(terminal) end,
405 {description = "open a terminal", group = "launcher"}),
406 awful.key({ modkey, "Control" }, "r", awesome.restart,
407 {description = "reload awesome", group = "awesome"}),
408 awful.key({ modkey, "Shift" }, "q", awesome.quit,
409 {description = "quit awesome", group = "awesome"}),
411 awful.key({ modkey, }, "l", function () awful.tag.incmwfact( 0.05) end,
412 {description = "increase master width factor", group = "layout"}),
413 awful.key({ modkey, }, "h", function () awful.tag.incmwfact(-0.05) end,
414 {description = "decrease master width factor", group = "layout"}),
415 awful.key({ modkey, "Shift" }, "h", function () awful.tag.incnmaster( 1, nil, true) end,
416 {description = "increase the number of master clients", group = "layout"}),
417 awful.key({ modkey, "Shift" }, "l", function () awful.tag.incnmaster(-1, nil, true) end,
418 {description = "decrease the number of master clients", group = "layout"}),
419 awful.key({ modkey, "Control" }, "h", function () awful.tag.incncol( 1, nil, true) end,
420 {description = "increase the number of columns", group = "layout"}),
421 awful.key({ modkey, "Control" }, "l", function () awful.tag.incncol(-1, nil, true) end,
422 {description = "decrease the number of columns", group = "layout"}),
423 awful.key({ modkey, }, "space", function () awful.layout.inc( 1) end,
424 {description = "select next", group = "layout"}),
425 awful.key({ modkey, "Shift" }, "space", function () awful.layout.inc(-1) end,
426 {description = "select previous", group = "layout"}),
428 awful.key({ modkey, "Control" }, "n",
430 local c = awful.client.restore()
431 -- Focus restored client
437 {description = "restore minimized", group = "client"}),
440 awful.key({ modkey }, "r", function () mypromptbox[awful.screen.focused()]:run() end,
441 {description = "run prompt", group = "launcher"}),
443 awful.key({ modkey }, "x",
445 awful.prompt.run({ prompt = "Run Lua code: " },
446 mypromptbox[awful.screen.focused()].widget,
447 awful.util.eval, nil,
448 awful.util.get_cache_dir() .. "/history_eval")
450 {description = "lua execute prompt", group = "awesome"}),
452 awful.key({ modkey }, "p", function() menubar.show() end,
453 {description = "show the menubar", group = "launcher"})
456 clientkeys = awful.util.table.join(
457 awful.key({ modkey, }, "f",
459 c.fullscreen = not c.fullscreen
462 {description = "toggle fullscreen", group = "client"}),
463 awful.key({ modkey, "Shift" }, "c", function (c) c:kill() end,
464 {description = "close", group = "client"}),
465 awful.key({ modkey, "Control" }, "space", awful.client.floating.toggle ,
466 {description = "toggle floating", group = "client"}),
467 awful.key({ modkey, "Control" }, "Return", function (c) c:swap(awful.client.getmaster()) end,
468 {description = "move to master", group = "client"}),
469 awful.key({ modkey, }, "o", function (c) c:move_to_screen() end,
470 {description = "move to screen", group = "client"}),
471 awful.key({ modkey, }, "t", function (c) c.ontop = not c.ontop end,
472 {description = "toggle keep on top", group = "client"}),
473 awful.key({ modkey, }, "n",
475 -- The client currently has the input focus, so it cannot be
476 -- minimized, since minimized clients can't have the focus.
479 {description = "minimize", group = "client"}),
480 awful.key({ modkey, }, "m",
482 c.maximized = not c.maximized
483 c.maximized_horizontal = false
484 c.maximized_vertical = false
487 {description = "maximize", group = "client"})
490 -- Bind all key numbers to tags.
491 -- Be careful: we use keycodes to make it works on any keyboard layout.
492 -- This should map on the top row of your keyboard, usually 1 to 9.
494 globalkeys = awful.util.table.join(globalkeys,
496 awful.key({ modkey }, "#" .. i + 9,
498 local screen = awful.screen.focused()
499 local tag = screen.tags[i]
504 {description = "view tag #"..i, group = "tag"}),
506 awful.key({ modkey, "Control" }, "#" .. i + 9,
508 local screen = awful.screen.focused()
509 local tag = screen.tags[i]
511 awful.tag.viewtoggle(tag)
514 {description = "toggle tag #" .. i, group = "tag"}),
515 -- Move client to tag.
516 awful.key({ modkey, "Shift" }, "#" .. i + 9,
519 local tag = client.focus.screen.tags[i]
521 client.focus:move_to_tag(tag)
525 {description = "move focused client to tag #"..i, group = "tag"}),
526 -- Toggle tag on focused client.
527 awful.key({ modkey, "Control", "Shift" }, "#" .. i + 9,
530 local tag = client.focus.screen.tags[i]
532 client.focus:toggle_tag(tag)
536 {description = "toggle focused client on tag #" .. i, group = "tag"})
540 clientbuttons = awful.util.table.join(
541 awful.button({ }, 1, function (c) client.focus = c; c:raise() end),
542 awful.button({ modkey }, 1, awful.mouse.client.move),
543 awful.button({ modkey }, 3, awful.mouse.client.resize))
546 globalkeys = awful.util.table.join(globalkeys,
547 awful.key({ cmdkey }, "n", function () awful.spawn("firefox") end),
548 awful.key({ cmdkey }, "m", function () awful.spawn("chromium --enable-remote-extensions") end),
549 awful.key({ cmdkey }, "y", function () awful.spawn(terminal .. " -e python") end),
550 awful.key({ cmdkey }, "c", function () awful.spawn("thunderbird") end),
551 awful.key({ cmdkey }, "r", function () mypromptbox[mouse.screen]:run() end),
552 awful.key({ cmdkey }, "g", function () awful.spawn("gscan2pdf") end),
553 awful.key({ cmdkey }, "v", function () awful.spawn("virt-manager") end),
554 awful.key({ cmdkey }, "l", function () awful.spawn("libreoffice") end),
555 awful.key({ cmdkey }, "f", function () awful.spawn("thunar") end),
556 awful.key({ cmdkey }, "i", function () awful.spawn(terminal .. " -name irc -e env MOSH_TITLE_NOPREFIX=true mosh -4 -- irc-host tmux new -As irc irssi") end),
557 awful.key({ cmdkey }, "x", function () awful.spawn.with_shell("/sbin/start-stop-daemon --start --background --exec /usr/bin/xscreensaver -- -no-capture-stderr; sleep 2; xscreensaver-command -lock") end),
558 awful.key({ cmdkey, "Shift" }, "x", function () awful.spawn("xscreensaver-command -exit") end),
561 awful.key(nil, "XF86ScreenSaver", function () awful.spawn("xset dpms force off") end),
562 awful.key(nil, "XF86AudioMute", function () awful.spawn("pactl set-sink-mute 0 toggle") end),
563 awful.key({ cmdkey }, "End", function () awful.spawn("pactl set-sink-mute 0 toggle") end),
564 awful.key(nil, "XF86AudioLowerVolume", function () awful.spawn("pactl set-sink-volume 0 -2%") end),
565 awful.key({ cmdkey }, "Next", function () awful.spawn("pactl set-sink-volume 0 -2%") end),
566 awful.key(nil, "XF86AudioRaiseVolume", function () awful.spawn("pactl set-sink-volume 0 +2%") end),
567 awful.key({ cmdkey }, "Prior", function () awful.spawn("pactl set-sink-volume 0 +2%") end),
568 awful.key(nil, "XF86AudioMicMute", function () awful.spawn("pactl set-source-mute 1 toggle") end),
569 awful.key({ cmdkey }, "Home", function () awful.spawn("pactl set-source-mute 1 toggle") end),
570 awful.key(nil, "XF86MonBrightnessDown", function () awful.spawn("xbacklight -dec 5%") end),
571 awful.key(nil, "XF86MonBrightnessUp", function () awful.spawn("xbacklight -inc 5%") end),
572 awful.key(nil, "XF86Display", function () awful.spawn("") end),
573 awful.key(nil, "XF86WLAN", function () awful.spawn("") end),
574 awful.key(nil, "XF86Tools", function () awful.spawn("") end),
575 awful.key(nil, "XF86Search", function () awful.spawn("") end),
576 awful.key(nil, "XF86LaunchA", function () awful.spawn("") end),
577 awful.key(nil, "XF86Explorer", function () awful.spawn("") end)
581 root.keys(globalkeys)
585 -- Rules to apply to new clients (through the "manage" signal).
587 local function move_to_tag(s, t)
589 c:move_to_tag(tags[s][t])
593 awful.rules.rules = {
594 -- All clients will match this rule.
596 properties = { border_width = beautiful.border_width,
597 border_color = beautiful.border_normal,
598 focus = awful.client.focus.filter,
601 buttons = clientbuttons,
602 screen = awful.screen.preferred,
603 placement = awful.placement.no_overlap+awful.placement.no_offscreen,
608 -- Add titlebars to normal clients and dialogs
609 --DISABLED-- { rule_any = {type = { "normal", "dialog" }
610 --DISABLED-- }, properties = { titlebars_enabled = true }
613 { rule = { type = "dialog" },
614 properties = { floating = true,
615 placement = awful.placement.centered
619 { rule = { class = "URxvt" },
622 size_hints_honor = false
624 { rule = { class = "URxvt", instance = "irc" },
628 callback = move_to_tag(screen.count(), screen.count() == 1 and 2 or 1)
630 { rule = { class = "Firefox", instance = "Navigator" },
634 callback = move_to_tag(screen.count() == 1 and 1 or 2, 9)
636 { rule = { class = "Firefox-esr", instance = "Navigator" },
640 callback = move_to_tag(screen.count() == 1 and 1 or 2, 9)
642 { rule = { class = "Thunderbird", instance = "Mail" },
646 callback = move_to_tag(screen.count() == 1 and 1 or 2, 8)
648 { rule = { class = "Chromium", instance = "chromium" },
652 callback = move_to_tag(screen.count() == 1 and 1 or 2, 9)
654 { rule = { class = "Gscan2pdf" },
658 callback = move_to_tag(1, 5)
660 { rule = { name = "gscan2pdf .*" },
665 { rule = { class = "Thunar", type = "normal" },
670 { rule = { class = "MuPDF", instance = "mupdf" },
675 { rule = { class = "Pinentry", instance = "pinentry" },
684 -- Signal function to execute when a new client appears.
685 client.connect_signal("manage", function (c)
686 -- Set the windows at the slave,
687 -- i.e. put it at the end of others instead of setting it master.
688 -- if not awesome.startup then awful.client.setslave(c) end
690 if awesome.startup and
691 not c.size_hints.user_position
692 and not c.size_hints.program_position then
693 -- Prevent clients from being unreachable after screen count changes.
694 awful.placement.no_offscreen(c)
697 c.maximized_horizontal = false
698 c.maximized_vertical = false
701 -- Add a titlebar if titlebars_enabled is set to true in the rules.
702 client.connect_signal("request::titlebars", function(c)
703 -- buttons for the titlebar
704 local buttons = awful.util.table.join(
705 awful.button({ }, 1, function()
708 awful.mouse.client.move(c)
710 awful.button({ }, 3, function()
713 awful.mouse.client.resize(c)
717 awful.titlebar(c) : setup {
719 awful.titlebar.widget.iconwidget(c),
721 layout = wibox.layout.fixed.horizontal
726 widget = awful.titlebar.widget.titlewidget(c)
729 layout = wibox.layout.flex.horizontal
732 awful.titlebar.widget.floatingbutton (c),
733 awful.titlebar.widget.maximizedbutton(c),
734 awful.titlebar.widget.stickybutton (c),
735 awful.titlebar.widget.ontopbutton (c),
736 awful.titlebar.widget.closebutton (c),
737 layout = wibox.layout.fixed.horizontal()
739 layout = wibox.layout.align.horizontal
743 -- Enable sloppy focus
744 client.connect_signal("mouse::enter", function(c)
745 if awful.layout.get(c.screen) ~= awful.layout.suit.magnifier
746 and awful.client.focus.filter(c) then
751 client.connect_signal("focus", function(c) c.border_color = beautiful.border_focus end)
752 client.connect_signal("unfocus", function(c) c.border_color = beautiful.border_normal end)
754 awful.ewmh.add_activate_filter(function(c, context, hints)
755 if context == "ewmh" and (c.class == "Firefox-esr" or c.class == "Firefox") then return false end
758 -- vim:ft=lua:sw=4:sts=4:ts=4:et