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.widgets.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.widgets.abase({
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.widgets.abase({
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
485 {description = "maximize", group = "client"})
488 -- Bind all key numbers to tags.
489 -- Be careful: we use keycodes to make it works on any keyboard layout.
490 -- This should map on the top row of your keyboard, usually 1 to 9.
492 globalkeys = awful.util.table.join(globalkeys,
494 awful.key({ modkey }, "#" .. i + 9,
496 local screen = awful.screen.focused()
497 local tag = screen.tags[i]
502 {description = "view tag #"..i, group = "tag"}),
504 awful.key({ modkey, "Control" }, "#" .. i + 9,
506 local screen = awful.screen.focused()
507 local tag = screen.tags[i]
509 awful.tag.viewtoggle(tag)
512 {description = "toggle tag #" .. i, group = "tag"}),
513 -- Move client to tag.
514 awful.key({ modkey, "Shift" }, "#" .. i + 9,
517 local tag = client.focus.screen.tags[i]
519 client.focus:move_to_tag(tag)
523 {description = "move focused client to tag #"..i, group = "tag"}),
524 -- Toggle tag on focused client.
525 awful.key({ modkey, "Control", "Shift" }, "#" .. i + 9,
528 local tag = client.focus.screen.tags[i]
530 client.focus:toggle_tag(tag)
534 {description = "toggle focused client on tag #" .. i, group = "tag"})
538 clientbuttons = awful.util.table.join(
539 awful.button({ }, 1, function (c) client.focus = c; c:raise() end),
540 awful.button({ modkey }, 1, awful.mouse.client.move),
541 awful.button({ modkey }, 3, awful.mouse.client.resize))
544 globalkeys = awful.util.table.join(globalkeys,
545 awful.key({ cmdkey }, "n", function () awful.spawn("firefox") end),
546 awful.key({ cmdkey }, "m", function () awful.spawn("chromium --enable-remote-extensions") end),
547 awful.key({ cmdkey }, "y", function () awful.spawn(terminal .. " -e python") end),
548 awful.key({ cmdkey }, "c", function () awful.spawn("thunderbird") end),
549 awful.key({ cmdkey }, "r", function () mypromptbox[mouse.screen]:run() end),
550 awful.key({ cmdkey }, "g", function () awful.spawn("gscan2pdf") end),
551 awful.key({ cmdkey }, "v", function () awful.spawn("virt-manager") end),
552 awful.key({ cmdkey }, "l", function () awful.spawn("libreoffice") end),
553 awful.key({ cmdkey }, "f", function () awful.spawn("thunar") end),
554 awful.key({ cmdkey }, "i", function () awful.spawn(terminal .. " -title irc -name irc -e env MOSH_TITLE_NOPREFIX=true mosh -4 -- irc-host tmux new -DAs irc irssi") end),
555 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),
556 awful.key({ cmdkey, "Shift" }, "x", function () awful.spawn("xscreensaver-command -exit") end),
559 awful.key(nil, "XF86ScreenSaver", function () awful.spawn("xset dpms force off") end),
560 awful.key(nil, "XF86AudioMute", function () awful.spawn("pactl set-sink-mute 0 toggle") end),
561 awful.key({ cmdkey }, "End", function () awful.spawn("pactl set-sink-mute 0 toggle") end),
562 awful.key(nil, "XF86AudioLowerVolume", function () awful.spawn("pactl set-sink-volume 0 -2%") end),
563 awful.key({ cmdkey }, "Next", function () awful.spawn("pactl set-sink-volume 0 -2%") end),
564 awful.key(nil, "XF86AudioRaiseVolume", function () awful.spawn("pactl set-sink-volume 0 +2%") end),
565 awful.key({ cmdkey }, "Prior", function () awful.spawn("pactl set-sink-volume 0 +2%") end),
566 awful.key(nil, "XF86AudioMicMute", function () awful.spawn("pactl set-source-mute 1 toggle") end),
567 awful.key({ cmdkey }, "Home", function () awful.spawn("pactl set-source-mute 1 toggle") end),
568 awful.key(nil, "XF86MonBrightnessDown", function () awful.spawn("xbacklight -dec 5%") end),
569 awful.key(nil, "XF86MonBrightnessUp", function () awful.spawn("xbacklight -inc 5%") end),
570 awful.key(nil, "XF86Display", function () awful.spawn("") end),
571 awful.key(nil, "XF86WLAN", function () awful.spawn("") end),
572 awful.key(nil, "XF86Tools", function () awful.spawn("") end),
573 awful.key(nil, "XF86Search", function () awful.spawn("") end),
574 awful.key(nil, "XF86LaunchA", function () awful.spawn("") end),
575 awful.key(nil, "XF86Explorer", function () awful.spawn("") end)
579 root.keys(globalkeys)
583 -- Rules to apply to new clients (through the "manage" signal).
585 local function move_to_tag(s, t)
587 c:move_to_tag(tags[s][t])
591 awful.rules.rules = {
592 -- All clients will match this rule.
594 properties = { border_width = beautiful.border_width,
595 border_color = beautiful.border_normal,
596 focus = awful.client.focus.filter,
599 buttons = clientbuttons,
600 screen = awful.screen.preferred,
601 placement = awful.placement.no_overlap+awful.placement.no_offscreen,
606 -- Add titlebars to normal clients and dialogs
607 --DISABLED-- { rule_any = {type = { "normal", "dialog" }
608 --DISABLED-- }, properties = { titlebars_enabled = true }
611 { rule = { type = "dialog" },
612 properties = { placement = awful.placement.centered }},
614 { rule = { class = "URxvt" },
617 size_hints_honor = false
619 { rule = { class = "URxvt", instance = "irc" },
623 callback = move_to_tag(screen.count(), screen.count() == 1 and 2 or 1)
625 { rule = { class = "Firefox", instance = "Navigator" },
629 callback = move_to_tag(screen.count() == 1 and 1 or 2, 9)
631 { rule = { class = "Thunderbird", instance = "Mail" },
635 callback = move_to_tag(screen.count() == 1 and 1 or 2, 8)
637 { rule = { class = "Chromium", instance = "chromium" },
641 callback = move_to_tag(screen.count() == 1 and 1 or 2, 9)
643 { rule = { class = "Gscan2pdf" },
647 callback = move_to_tag(1, 5)
649 { rule = { name = "gscan2pdf .*" },
654 { rule = { class = "Thunar", type = "normal" },
663 -- Signal function to execute when a new client appears.
664 client.connect_signal("manage", function (c)
665 -- Set the windows at the slave,
666 -- i.e. put it at the end of others instead of setting it master.
667 -- if not awesome.startup then awful.client.setslave(c) end
669 if awesome.startup and
670 not c.size_hints.user_position
671 and not c.size_hints.program_position then
672 -- Prevent clients from being unreachable after screen count changes.
673 awful.placement.no_offscreen(c)
677 -- Add a titlebar if titlebars_enabled is set to true in the rules.
678 client.connect_signal("request::titlebars", function(c)
679 -- buttons for the titlebar
680 local buttons = awful.util.table.join(
681 awful.button({ }, 1, function()
684 awful.mouse.client.move(c)
686 awful.button({ }, 3, function()
689 awful.mouse.client.resize(c)
693 awful.titlebar(c) : setup {
695 awful.titlebar.widget.iconwidget(c),
697 layout = wibox.layout.fixed.horizontal
702 widget = awful.titlebar.widget.titlewidget(c)
705 layout = wibox.layout.flex.horizontal
708 awful.titlebar.widget.floatingbutton (c),
709 awful.titlebar.widget.maximizedbutton(c),
710 awful.titlebar.widget.stickybutton (c),
711 awful.titlebar.widget.ontopbutton (c),
712 awful.titlebar.widget.closebutton (c),
713 layout = wibox.layout.fixed.horizontal()
715 layout = wibox.layout.align.horizontal
719 -- Enable sloppy focus
720 client.connect_signal("mouse::enter", function(c)
721 if awful.layout.get(c.screen) ~= awful.layout.suit.magnifier
722 and awful.client.focus.filter(c) then
727 client.connect_signal("focus", function(c) c.border_color = beautiful.border_focus end)
728 client.connect_signal("unfocus", function(c) c.border_color = beautiful.border_normal end)
730 awful.ewmh.add_activate_filter(function(c, context, hints)
731 if context == "ewmh" and c.class == "Firefox" then return false end
734 -- vim:ft=lua:sw=4:sts=4:ts=4:et