madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

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.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

rewrite tabs creation based on a config table
[etc/awesome.git] / .config / awesome / rc.lua
1 -- {{{ Imports
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 -- }}}
15
16 -- {{{ Error handling
17 -- Check if awesome encountered an error during startup and fell back to
18 -- another config (This code will only ever execute for the fallback config)
19 if awesome.startup_errors then
20     naughty.notify({ preset = naughty.config.presets.critical,
21                      title = "Oops, there were errors during startup!",
22                      text = awesome.startup_errors })
23 end
24
25 -- Handle runtime errors after startup
26 do
27     local in_error = false
28     awesome.connect_signal("debug::error", function (err)
29         -- Make sure we don't go into an endless error loop
30         if in_error then return end
31         in_error = true
32
33         naughty.notify({ preset = naughty.config.presets.critical,
34                          title = "Oops, an error happened!",
35                          text = tostring(err) })
36         in_error = false
37     end)
38 end
39 -- }}}
40
41 -- {{{ Variable definitions
42 -- Themes define colours, icons, and wallpapers
43 beautiful.init(awful.util.get_themes_dir() .. "default/theme.lua")
44
45 -- This is used later as the default terminal and editor to run.
46 terminal = "x-terminal-emulator"
47 editor = "sensible-editor"
48 editor_cmd = terminal .. " -e " .. editor
49
50 -- Default modkey.
51 -- Usually, Mod4 is the key with a logo between Control and Alt.
52 -- If you do not like this or do not have such a key,
53 -- I suggest you to remap Mod4 to another key using xmodmap or other tools.
54 -- However, you can use another modifier like Mod1, but it may interact with others.
55 modkey = "Mod4"
56 cmdkey = "Mod3"
57
58 -- Table of layouts to cover with awful.layout.inc, order matters.
59 awful.layout.layouts = {
60     awful.layout.suit.fair,
61     awful.layout.suit.tile,
62     -- awful.layout.suit.tile.left,
63     -- awful.layout.suit.tile.bottom,
64     awful.layout.suit.tile.top,
65     -- awful.layout.suit.spiral,
66     -- awful.layout.suit.spiral.dwindle,
67     awful.layout.suit.max,
68     awful.layout.suit.max.fullscreen,
69     -- awful.layout.suit.magnifier,
70     -- awful.layout.suit.corner.nw,
71     -- awful.layout.suit.corner.ne,
72     -- awful.layout.suit.corner.sw,
73     -- awful.layout.suit.corner.se,
74     awful.layout.suit.floating,
75 }
76
77 layout_default = awful.layout.layouts[1]
78 layout_tiled = awful.layout.layouts[2]
79 layout_maximised = awful.layout.layouts[4]
80 layout_floating = awful.layout.layouts[5]
81 -- }}}
82
83 -- {{{ Helper functions
84 local function client_menu_toggle_fn()
85     local instance = nil
86
87     return function ()
88         if instance and instance.wibox.visible then
89             instance:hide()
90             instance = nil
91         else
92             instance = awful.menu.clients({ theme = { width = 250 } })
93         end
94     end
95 end
96
97 local function sorted_pairs(t, f)
98     local a = {}
99     for n in pairs(t) do table.insert(a, n) end
100     table.sort(a, f)
101     local i = 0      -- iterator variable
102     local iter = function ()   -- iterator function
103         i = i + 1
104         if a[i] == nil then return nil
105         else return a[i], t[a[i]]
106         end
107     end
108     return iter
109 end
110
111 local function print_table(tbl, indent)
112     if not indent then indent = 0 end
113     for k, v in pairs(tbl) do
114         formatting = string.rep("  ", indent) .. k .. ": "
115         if type(v) == "table" then
116             print(formatting)
117             print_table(v, indent+1)
118         else
119             print(formatting .. tostring(v))
120         end
121     end
122 end
123 -- }}}
124
125 -- {{{ Menu
126 -- Create a launcher widget and a main menu
127 myawesomemenu = {
128    { "hotkeys", function() return false, hotkeys_popup.show_help end},
129    { "manual", terminal .. " -e man awesome" },
130    { "edit config", editor_cmd .. " " .. awesome.conffile },
131    { "restart", awesome.restart },
132    { "quit", awesome.quit }
133 }
134
135 mymainmenu = awful.menu({ items = { { "awesome", myawesomemenu, beautiful.awesome_icon },
136                                     { "open terminal", terminal }
137                                   }
138                         })
139
140 mylauncher = awful.widget.launcher({ image = beautiful.awesome_icon,
141                                      menu = mymainmenu })
142 -- }}}
143
144 -- {{{ Menubar configuration
145 menubar.utils.terminal = terminal -- Set the terminal for applications that require it
146 -- }}}
147
148 -- {{{ Wibox
149 -- Create a textclock widget
150 mytextclock = wibox.widget.textclock("%a %d %b %H:%M:%S", 1)
151
152 -- Keyboard map indicator and switcher
153 mykeyboardlayout = awful.widget.keyboardlayout()
154
155 -- Create a wibox for each screen and add it
156 mywibox = {}
157 mypromptbox = {}
158 mylayoutbox = {}
159 mytaglist = {}
160 mytaglist.buttons = awful.util.table.join(
161                     awful.button({ }, 1, function(t) t:view_only() end),
162                     awful.button({ modkey }, 1, function(t)
163                                               if client.focus then
164                                                   client.focus:move_to_tag(t)
165                                               end
166                                           end),
167                     awful.button({ }, 3, awful.tag.viewtoggle),
168                     awful.button({ modkey }, 3, function(t)
169                                               if client.focus then
170                                                   client.focus:toggle_tag(t)
171                                               end
172                                           end),
173                     awful.button({ }, 4, function(t) awful.tag.viewnext(t.screen) end),
174                     awful.button({ }, 5, function(t) awful.tag.viewprev(t.screen) end)
175                 )
176
177 mytasklist = {}
178 mytasklist.buttons = awful.util.table.join(
179                      awful.button({ }, 1, function (c)
180                                               if c == client.focus then
181                                                   c.minimized = true
182                                               else
183                                                   -- Without this, the following
184                                                   -- :isvisible() makes no sense
185                                                   c.minimized = false
186                                                   if not c:isvisible() and c.first_tag then
187                                                       c.first_tag:view_only()
188                                                   end
189                                                   -- This will also un-minimize
190                                                   -- the client, if needed
191                                                   client.focus = c
192                                                   c:raise()
193                                               end
194                                           end),
195                      awful.button({ }, 3, client_menu_toggle_fn()),
196                      awful.button({ }, 4, function ()
197                                               awful.client.focus.byidx(1)
198                                           end),
199                      awful.button({ }, 5, function ()
200                                               awful.client.focus.byidx(-1)
201                                           end))
202 -- }}}
203
204 -- {{{ Tags
205 tags = {}
206 tags.config = {}
207 tags.config["main"] = {
208     t1 = { layout = layout_default, selected = true },
209     t2 = { layout = layout_default },
210     t3 = { layout = layout_tiled },
211     t4 = { layout = layout_tiled },
212     t5 = { layout = layout_tiled },
213     t6 = { layout = layout_floating },
214     t7 = { layout = layout_maximised },
215     t8 = { layout = layout_maximised },
216     t9 = { layout = layout_maximised },
217 }
218 tags.config["aux"] = {
219     t1 = { layout = layout_default, selected = true },
220     t2 = { layout = layout_default },
221     t3 = { layout = layout_tiled },
222     t4 = { layout = layout_floating },
223     t5 = { layout = layout_floating },
224     t6 = { layout = layout_floating },
225     t7 = { layout = layout_floating },
226     t8 = { layout = layout_floating },
227     t9 = { layout = layout_maximised },
228 }
229
230 screentags = {}
231 screentags[1] = tags.config["main"]
232 if screen.count() == 2 then -- aux screen is on the right
233   screentags[2] = tags.config["aux"]
234 elseif screen.count() == 3 then -- main screen is still #1 in the middle
235   screentags[2] = tags.config["aux"]
236   screentags[3] = tags.config["aux"]
237 end
238
239 awful.screen.connect_for_each_screen(function(s)
240     -- Wallpaper
241     --DISABLED--if beautiful.wallpaper then
242     --DISABLED--    local wallpaper = beautiful.wallpaper
243     --DISABLED--    -- If wallpaper is a function, call it with the screen
244     --DISABLED--    if type(wallpaper) == "function" then
245     --DISABLED--        wallpaper = wallpaper(s)
246     --DISABLED--    end
247     --DISABLED--    gears.wallpaper.maximized(wallpaper, s, true)
248     --DISABLED--end
249
250     if not tags[s.index] then
251         tags[s.index] = {}
252     end
253     for n,p in sorted_pairs(screentags[s.index]) do
254         p["screen"] = s
255         n = string.sub(n, 2) -- remove leading 't' needed for syntax in table
256         table.insert(tags[s.index], awful.tag.add(n, p))
257     end
258
259     -- Create a promptbox for each screen
260     mypromptbox[s] = awful.widget.prompt()
261     -- Create an imagebox widget which will contains an icon indicating which layout we're using.
262     -- We need one layoutbox per screen.
263     mylayoutbox[s] = awful.widget.layoutbox(s)
264     mylayoutbox[s]:buttons(awful.util.table.join(
265                            awful.button({ }, 1, function () awful.layout.inc( 1) end),
266                            awful.button({ }, 3, function () awful.layout.inc(-1) end),
267                            awful.button({ }, 4, function () awful.layout.inc( 1) end),
268                            awful.button({ }, 5, function () awful.layout.inc(-1) end)))
269     -- Create a taglist widget
270     mytaglist[s] = awful.widget.taglist(s, awful.widget.taglist.filter.all, mytaglist.buttons)
271
272     -- Create a tasklist widget
273     mytasklist[s] = awful.widget.tasklist(s, awful.widget.tasklist.filter.currenttags, mytasklist.buttons)
274
275     -- Create the wibox
276     mywibox[s] = awful.wibar({ position = "top", screen = s })
277
278     -- Add widgets to the wibox
279     mywibox[s]:setup {
280         layout = wibox.layout.align.horizontal,
281         { -- Left widgets
282             layout = wibox.layout.fixed.horizontal,
283             -- mylauncher,
284             mytaglist[s],
285             mypromptbox[s],
286         },
287         mytasklist[s], -- Middle widget
288         { -- Right widgets
289             layout = wibox.layout.fixed.horizontal,
290             mykeyboardlayout,
291             wibox.widget.systray(),
292             mytextclock,
293             mylayoutbox[s],
294         },
295     }
296 end)
297 -- }}}
298
299 -- {{{ Mouse bindings
300 root.buttons(awful.util.table.join(
301     awful.button({ }, 3, function () mymainmenu:toggle() end),
302     awful.button({ }, 4, awful.tag.viewnext),
303     awful.button({ }, 5, awful.tag.viewprev)
304 ))
305 -- }}}
306
307 -- {{{ Key bindings
308 globalkeys = awful.util.table.join(
309     awful.key({ modkey,           }, "s",      hotkeys_popup.show_help,
310               {description="show help", group="awesome"}),
311     awful.key({ modkey,           }, "Left",   awful.tag.viewprev,
312               {description = "view previous", group = "tag"}),
313     awful.key({ modkey,           }, "Right",  awful.tag.viewnext,
314               {description = "view next", group = "tag"}),
315     awful.key({ modkey,           }, "Escape", awful.tag.history.restore,
316               {description = "go back", group = "tag"}),
317
318     awful.key({ modkey,           }, "j",
319         function ()
320             awful.client.focus.byidx( 1)
321         end,
322         {description = "focus next by index", group = "client"}
323     ),
324     awful.key({ modkey,           }, "k",
325         function ()
326             awful.client.focus.byidx(-1)
327         end,
328         {description = "focus previous by index", group = "client"}
329     ),
330     awful.key({ modkey,           }, "w", function () mymainmenu:show() end,
331               {description = "show main menu", group = "awesome"}),
332
333     -- Layout manipulation
334     awful.key({ modkey, "Shift"   }, "j", function () awful.client.swap.byidx(  1)    end,
335               {description = "swap with next client by index", group = "client"}),
336     awful.key({ modkey, "Shift"   }, "k", function () awful.client.swap.byidx( -1)    end,
337               {description = "swap with previous client by index", group = "client"}),
338     awful.key({ modkey, "Control" }, "j", function () awful.screen.focus_relative( 1) end,
339               {description = "focus the next screen", group = "screen"}),
340     awful.key({ modkey, "Control" }, "k", function () awful.screen.focus_relative(-1) end,
341               {description = "focus the previous screen", group = "screen"}),
342     awful.key({ modkey,           }, "u", awful.client.urgent.jumpto,
343               {description = "jump to urgent client", group = "client"}),
344     awful.key({ modkey,           }, "Tab",
345         function ()
346             awful.client.focus.history.previous()
347             if client.focus then
348                 client.focus:raise()
349             end
350         end,
351         {description = "go back", group = "client"}),
352
353     -- Standard program
354     awful.key({ modkey,           }, "Return", function () awful.spawn(terminal) end,
355               {description = "open a terminal", group = "launcher"}),
356     awful.key({ modkey, "Control" }, "r", awesome.restart,
357               {description = "reload awesome", group = "awesome"}),
358     awful.key({ modkey, "Shift"   }, "q", awesome.quit,
359               {description = "quit awesome", group = "awesome"}),
360
361     awful.key({ modkey,           }, "l",     function () awful.tag.incmwfact( 0.05)          end,
362               {description = "increase master width factor", group = "layout"}),
363     awful.key({ modkey,           }, "h",     function () awful.tag.incmwfact(-0.05)          end,
364               {description = "decrease master width factor", group = "layout"}),
365     awful.key({ modkey, "Shift"   }, "h",     function () awful.tag.incnmaster( 1, nil, true) end,
366               {description = "increase the number of master clients", group = "layout"}),
367     awful.key({ modkey, "Shift"   }, "l",     function () awful.tag.incnmaster(-1, nil, true) end,
368               {description = "decrease the number of master clients", group = "layout"}),
369     awful.key({ modkey, "Control" }, "h",     function () awful.tag.incncol( 1, nil, true)    end,
370               {description = "increase the number of columns", group = "layout"}),
371     awful.key({ modkey, "Control" }, "l",     function () awful.tag.incncol(-1, nil, true)    end,
372               {description = "decrease the number of columns", group = "layout"}),
373     awful.key({ modkey,           }, "space", function () awful.layout.inc( 1)                end,
374               {description = "select next", group = "layout"}),
375     awful.key({ modkey, "Shift"   }, "space", function () awful.layout.inc(-1)                end,
376               {description = "select previous", group = "layout"}),
377
378     awful.key({ modkey, "Control" }, "n",
379               function ()
380                   local c = awful.client.restore()
381                   -- Focus restored client
382                   if c then
383                       client.focus = c
384                       c:raise()
385                   end
386               end,
387               {description = "restore minimized", group = "client"}),
388
389     -- Prompt
390     awful.key({ modkey },            "r",     function () mypromptbox[awful.screen.focused()]:run() end,
391               {description = "run prompt", group = "launcher"}),
392
393     awful.key({ modkey }, "x",
394               function ()
395                   awful.prompt.run({ prompt = "Run Lua code: " },
396                   mypromptbox[awful.screen.focused()].widget,
397                   awful.util.eval, nil,
398                   awful.util.get_cache_dir() .. "/history_eval")
399               end,
400               {description = "lua execute prompt", group = "awesome"}),
401     -- Menubar
402     awful.key({ modkey }, "p", function() menubar.show() end,
403               {description = "show the menubar", group = "launcher"})
404 )
405
406 clientkeys = awful.util.table.join(
407     awful.key({ modkey,           }, "f",
408         function (c)
409             c.fullscreen = not c.fullscreen
410             c:raise()
411         end,
412         {description = "toggle fullscreen", group = "client"}),
413     awful.key({ modkey, "Shift"   }, "c",      function (c) c:kill()                         end,
414               {description = "close", group = "client"}),
415     awful.key({ modkey, "Control" }, "space",  awful.client.floating.toggle                     ,
416               {description = "toggle floating", group = "client"}),
417     awful.key({ modkey, "Control" }, "Return", function (c) c:swap(awful.client.getmaster()) end,
418               {description = "move to master", group = "client"}),
419     awful.key({ modkey,           }, "o",      function (c) c:move_to_screen()               end,
420               {description = "move to screen", group = "client"}),
421     awful.key({ modkey,           }, "t",      function (c) c.ontop = not c.ontop            end,
422               {description = "toggle keep on top", group = "client"}),
423     awful.key({ modkey,           }, "n",
424         function (c)
425             -- The client currently has the input focus, so it cannot be
426             -- minimized, since minimized clients can't have the focus.
427             c.minimized = true
428         end ,
429         {description = "minimize", group = "client"}),
430     awful.key({ modkey,           }, "m",
431         function (c)
432             c.maximized = not c.maximized
433             c:raise()
434         end ,
435         {description = "maximize", group = "client"})
436 )
437
438 -- Bind all key numbers to tags.
439 -- Be careful: we use keycodes to make it works on any keyboard layout.
440 -- This should map on the top row of your keyboard, usually 1 to 9.
441 for i = 1, 9 do
442     globalkeys = awful.util.table.join(globalkeys,
443         -- View tag only.
444         awful.key({ modkey }, "#" .. i + 9,
445                   function ()
446                         local screen = awful.screen.focused()
447                         local tag = screen.tags[i]
448                         if tag then
449                            tag:view_only()
450                         end
451                   end,
452                   {description = "view tag #"..i, group = "tag"}),
453         -- Toggle tag.
454         awful.key({ modkey, "Control" }, "#" .. i + 9,
455                   function ()
456                       local screen = awful.screen.focused()
457                       local tag = screen.tags[i]
458                       if tag then
459                          awful.tag.viewtoggle(tag)
460                       end
461                   end,
462                   {description = "toggle tag #" .. i, group = "tag"}),
463         -- Move client to tag.
464         awful.key({ modkey, "Shift" }, "#" .. i + 9,
465                   function ()
466                       if client.focus then
467                           local tag = client.focus.screen.tags[i]
468                           if tag then
469                               client.focus:move_to_tag(tag)
470                           end
471                      end
472                   end,
473                   {description = "move focused client to tag #"..i, group = "tag"}),
474         -- Toggle tag on focused client.
475         awful.key({ modkey, "Control", "Shift" }, "#" .. i + 9,
476                   function ()
477                       if client.focus then
478                           local tag = client.focus.screen.tags[i]
479                           if tag then
480                               client.focus:toggle_tag(tag)
481                           end
482                       end
483                   end,
484                   {description = "toggle focused client on tag #" .. i, group = "tag"})
485     )
486 end
487
488 clientbuttons = awful.util.table.join(
489     awful.button({ }, 1, function (c) client.focus = c; c:raise() end),
490     awful.button({ modkey }, 1, awful.mouse.client.move),
491     awful.button({ modkey }, 3, awful.mouse.client.resize))
492
493 -- Set keys
494 root.keys(globalkeys)
495 -- }}}
496
497 -- {{{ Rules
498 -- Rules to apply to new clients (through the "manage" signal).
499 awful.rules.rules = {
500     -- All clients will match this rule.
501     { rule = { },
502       properties = { border_width = beautiful.border_width,
503                      border_color = beautiful.border_normal,
504                      focus = awful.client.focus.filter,
505                      size_hints_honor = false,
506                      raise = true,
507                      keys = clientkeys,
508                      buttons = clientbuttons,
509                      placement = awful.placement.no_overlap+awful.placement.no_offscreen
510      }
511     },
512
513     -- Floating clients.
514     { rule_any = {
515         instance = {
516           "DTA",  -- Firefox addon DownThemAll.
517           "copyq",  -- Includes session name in class.
518         },
519         class = {
520           "Arandr",
521           "Gpick",
522           "Kruler",
523           "MessageWin",  -- kalarm.
524           "Sxiv",
525           "Wpa_gui",
526           "pinentry",
527           "veromix",
528           "xtightvncviewer"},
529
530         name = {
531           "Event Tester",  -- xev.
532         },
533         role = {
534           "AlarmWindow",  -- Thunderbird's calendar.
535           "pop-up",       -- e.g. Google Chrome's (detached) Developer Tools.
536         }
537       }, properties = { floating = true }},
538
539     -- Add titlebars to normal clients and dialogs
540     { rule_any = {type = { "normal", "dialog" }
541       }, properties = { titlebars_enabled = false }
542     },
543
544     -- Set Firefox to always map on the tag named "2" on screen 1.
545     -- { rule = { class = "Firefox" },
546     --   properties = { screen = 1, tag = "2" } },
547 }
548 -- }}}
549
550 -- {{{ Signals
551 -- Signal function to execute when a new client appears.
552 client.connect_signal("manage", function (c)
553     -- Set the windows at the slave,
554     -- i.e. put it at the end of others instead of setting it master.
555     -- if not awesome.startup then awful.client.setslave(c) end
556
557     if awesome.startup and
558       not c.size_hints.user_position
559       and not c.size_hints.program_position then
560         -- Prevent clients from being unreachable after screen count changes.
561         awful.placement.no_offscreen(c)
562     end
563 end)
564
565 -- Add a titlebar if titlebars_enabled is set to true in the rules.
566 client.connect_signal("request::titlebars", function(c)
567     -- buttons for the titlebar
568     local buttons = awful.util.table.join(
569         awful.button({ }, 1, function()
570             client.focus = c
571             c:raise()
572             awful.mouse.client.move(c)
573         end),
574         awful.button({ }, 3, function()
575             client.focus = c
576             c:raise()
577             awful.mouse.client.resize(c)
578         end)
579     )
580
581     awful.titlebar(c) : setup {
582         { -- Left
583             awful.titlebar.widget.iconwidget(c),
584             buttons = buttons,
585             layout  = wibox.layout.fixed.horizontal
586         },
587         { -- Middle
588             { -- Title
589                 align  = "center",
590                 widget = awful.titlebar.widget.titlewidget(c)
591             },
592             buttons = buttons,
593             layout  = wibox.layout.flex.horizontal
594         },
595         { -- Right
596             awful.titlebar.widget.floatingbutton (c),
597             awful.titlebar.widget.maximizedbutton(c),
598             awful.titlebar.widget.stickybutton   (c),
599             awful.titlebar.widget.ontopbutton    (c),
600             awful.titlebar.widget.closebutton    (c),
601             layout = wibox.layout.fixed.horizontal()
602         },
603         layout = wibox.layout.align.horizontal
604     }
605 end)
606
607 -- Enable sloppy focus
608 client.connect_signal("mouse::enter", function(c)
609     if awful.layout.get(c.screen) ~= awful.layout.suit.magnifier
610         and awful.client.focus.filter(c) then
611         client.focus = c
612     end
613 end)
614
615 client.connect_signal("focus", function(c) c.border_color = beautiful.border_focus end)
616 client.connect_signal("unfocus", function(c) c.border_color = beautiful.border_normal end)
617 -- }}}
618
619 -- vim:ft=lua:sw=4:sts=4:ts=4:et