]> git.madduck.net Git - etc/awesome.git/blob - .config/awesome/rc.lua

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:

afbcb7d233705a245ce5368af86146000313301b
[etc/awesome.git] / .config / awesome / rc.lua
1 -- {{{ Imports
2
3 -- Standard awesome library
4 local gears = require("gears")
5 local awful = require("awful")
6 require("awful.autofocus")
7 -- Widget and layout library
8 local wibox = require("wibox")
9 -- Tyrannical tab handling
10 --local tyrannical = require("tyrannical")
11 -- Theme handling library
12 local beautiful = require("beautiful")
13 local xrdb = beautiful.xresources
14 -- Notification library
15 local naughty = require("naughty")
16 local menubar = require("menubar")
17 local hotkeys_popup = require("awful.hotkeys_popup").widget
18 -- Enable hotkeys help widget for VIM and other apps
19 -- when client with a matching name is opened:
20 require("awful.hotkeys_popup.keys")
21
22 -- Load Debian menu entries
23 local debian = require("debian.menu")
24 local has_fdo, freedesktop = pcall(require, "freedesktop")
25 -- Other libraries
26 local lain = require("lain")
27 local ccwidgets = require("cryptocoin_widgets")
28 --local fxwidgets = require("forex_widgets")
29 local clocksarray = require("clocksarray")
30 local dbg = require("debugfunc")
31 local th = require("taghelpers")
32 -- }}}
33
34 -- {{{ Error handling
35 -- Check if awesome encountered an error during startup and fell back to
36 -- another config (This code will only ever execute for the fallback config)
37 if awesome.startup_errors then
38     naughty.notify({ preset = naughty.config.presets.critical,
39                      title = "Oops, there were errors during startup!",
40                      text = awesome.startup_errors })
41 end
42
43 -- Handle runtime errors after startup
44 do
45     local in_error = false
46     awesome.connect_signal("debug::error", function (err)
47         -- Make sure we don't go into an endless error loop
48         if in_error then return end
49         in_error = true
50
51         naughty.notify({ preset = naughty.config.presets.critical,
52                          title = "Oops, an error happened!",
53                          text = tostring(err) })
54         in_error = false
55     end)
56 end
57 -- }}}
58
59 -- {{{ Variable definitions
60 --xrdb.set_dpi(95, screen[1])
61 --xrdb.set_dpi(120, screen[2])
62
63 -- Themes define colours, icons, font and wallpapers.
64 beautiful.init(gears.filesystem.get_configuration_dir () .. "theme/theme.lua")
65
66 -- This is used later as the default terminal and editor to run.
67 terminal = "rxvt-unicode"
68 editor = os.getenv("EDITOR") or "editor"
69 editor_cmd = terminal .. " -e " .. editor
70
71 -- Default modkey.
72 -- Usually, Mod4 is the key with a logo between Control and Alt.
73 -- If you do not like this or do not have such a key,
74 -- I suggest you to remap Mod4 to another key using xmodmap or other tools.
75 -- However, you can use another modifier like Mod1, but it may interact with others.
76 modkey = "Mod4"
77 cmdkey = "Mod3"
78
79 -- Table of layouts to cover with awful.layout.inc, order matters.
80 local layouts = {
81     default = awful.layout.suit.fair,
82     default_horiz = awful.layout.suit.fair.horizontal,
83     tiled = awful.layout.suit.tile,
84     tiled_horiz = awful.layout.suit.tile.top,
85     floating = awful.layout.suit.floating,
86     maximised = awful.layout.suit.max
87 }
88 awful.layout.layouts = {
89     layouts.default,
90     layouts.tiled,
91     layouts.maximised,
92     layouts.floating,
93     layouts.default_horiz,
94     layouts.tiled_horiz,
95 }
96 -- }}}
97
98 -- {{{ Helper functions
99 local function client_menu_toggle_fn()
100     local instance = nil
101
102     return function ()
103         if instance and instance.wibox.visible then
104             instance:hide()
105             instance = nil
106         else
107             instance = awful.menu.clients({ theme = { width = 250 } })
108         end
109     end
110 end
111
112 local function set_wallpaper(s)
113     -- Wallpaper
114     if beautiful.wallpaper then
115         local wallpaper = beautiful.wallpaper
116         -- If wallpaper is a function, call it with the screen
117         if type(wallpaper) == "function" then
118             wallpaper = wallpaper(s)
119         end
120         gears.wallpaper.maximized(wallpaper, s, true)
121     end
122 end
123
124 local function move_mouse_to_area(a)
125     local coords = mouse.coords()
126     if (coords.x < a.x or
127         coords.x > (a.x+a.width) or
128         coords.y < a.y or
129         coords.y > (a.y+a.height)) then
130
131         mouse.coords({
132             x = a.x + a.width/2,
133             y = a.y + a.height/2,
134         }, true)
135     end
136 end
137
138 -- }}}
139
140 -- {{{ Menu
141 -- Create a launcher widget and a main menu
142 myawesomemenu = {
143    { "hotkeys", function() return false, hotkeys_popup.show_help end},
144    { "manual", terminal .. " -e man awesome" },
145    { "edit config", editor_cmd .. " " .. awesome.conffile },
146    { "restart", awesome.restart },
147    { "quit", function() awesome.quit() end}
148 }
149
150 local menu_awesome = { "awesome", myawesomemenu, beautiful.awesome_icon }
151 local menu_terminal = { "open terminal", terminal }
152
153 if has_fdo then
154     mymainmenu = freedesktop.menu.build({
155         before = { menu_awesome },
156         after =  { menu_terminal }
157     })
158 else
159     mymainmenu = awful.menu({
160         items = {
161                   menu_awesome,
162                   { "Debian", debian.menu.Debian_menu.Debian },
163                   menu_terminal,
164                 }
165     })
166 end
167
168
169 mylauncher = awful.widget.launcher({ image = beautiful.awesome_icon,
170                                      menu = mymainmenu })
171
172 -- Menubar configuration
173 menubar.utils.terminal = terminal -- Set the terminal for applications that require it
174 -- }}}
175
176 -- {{{ Wibar
177 --local spacer = wibox.widget {
178 --    color = beautiful.bg_minimize,
179 --    forced_width = 4,
180 --    widget = wibox.widget.separator
181 --}
182 local function make_spacer(text)
183     local spacer = wibox.widget.textbox()
184     spacer:set_text(text or " │ ")
185     return spacer
186 end
187
188 -- Keyboard map indicator and switcher
189 mykeyboardlayout = awful.widget.keyboardlayout()
190
191 local lain_bat = lain.widget.bat({
192     batteries = {"BAT0", "BAT1"},
193     settings = function()
194         local delim = "↓"
195         if bat_now.status == "Charging" then delim = "↑"
196         elseif bat_now.status == "Unknown" then delim = "٭" end
197         widget:set_text(bat_now.perc .. "% " .. delim .. " " .. bat_now.time)
198     end,
199 })
200
201 -- Create a textclock widget
202 clocksarray = clocksarray.get_clocksarray("%a %d %b %H:%M:%S %Z", {
203 --        ["NZ"] = "Pacific/Auckland",
204         ["DE"] = "Europe/Berlin"
205     }, make_spacer())
206
207 -- Create a wibox for each screen and add it
208 local taglist_buttons = gears.table.join(
209                     awful.button({ }, 1, function(t) t:view_only() end),
210                     awful.button({ modkey }, 1, function(t)
211                                               if client.focus then
212                                                   client.focus:move_to_tag(t)
213                                               end
214                                           end),
215                     awful.button({ }, 3, awful.tag.viewtoggle),
216                     awful.button({ modkey }, 3, function(t)
217                                               if client.focus then
218                                                   client.focus:toggle_tag(t)
219                                               end
220                                           end),
221                     awful.button({ }, 4, function(t) awful.tag.viewnext(t.screen) end),
222                     awful.button({ }, 5, function(t) awful.tag.viewprev(t.screen) end)
223                 )
224
225 local tasklist_buttons = gears.table.join(
226                      awful.button({ }, 1, function (c)
227                                               if c == client.focus then
228                                                   -- I don't like click-minimising
229                                                   -- c.minimized = true
230                                               else
231                                                   -- Without this, the following
232                                                   -- :isvisible() makes no sense
233                                                   c.minimized = false
234                                                   if not c:isvisible() and c.first_tag then
235                                                       c.first_tag:view_only()
236                                                   end
237                                                   -- This will also un-minimize
238                                                   -- the client, if needed
239                                                   client.focus = c
240                                                   c:raise()
241                                               end
242                                           end),
243                      awful.button({ }, 3, client_menu_toggle_fn()),
244                      awful.button({ }, 4, function ()
245                                               awful.client.focus.byidx(1)
246                                           end),
247                      awful.button({ }, 5, function ()
248                                               awful.client.focus.byidx(-1)
249                                           end))
250 -- }}}
251
252 -- {{{ Screens
253
254 -- Re-set wallpaper when a screen's geometry changes (e.g. different resolution)
255 screen.connect_signal("property::geometry", set_wallpaper)
256
257 -- {{{ Basic setup for screens
258 local function screen_set_profile(s, profile)
259     s.profile = profile
260     s.outputstr = table.concat(gears.table.keys(s.outputs), "+")
261     s.name = s.profile .. "/" .. s.outputstr
262 end
263
264 awful.screen.connect_for_each_screen(function(s)
265
266     s.set_profile = screen_set_profile
267
268     -- Wallpaper
269     set_wallpaper(s)
270
271     -- Create a text widget to display screen name
272     s.namebox = wibox.container.background(wibox.widget.textbox(s.name),
273       beautiful.bg_minimize)
274
275     -- Create a promptbox for each screen
276     s.mypromptbox = awful.widget.prompt()
277     -- Create an imagebox widget which will contains an icon indicating which layout we're using.
278     -- We need one layoutbox per screen.
279     s.mylayoutbox = awful.widget.layoutbox(s)
280     s.mylayoutbox:buttons(awful.util.table.join(
281                            awful.button({ }, 1, function () awful.layout.inc( 1) end),
282                            awful.button({ }, 3, function () awful.layout.inc(-1) end),
283                            awful.button({ }, 4, function () awful.layout.inc( 1) end),
284                            awful.button({ }, 5, function () awful.layout.inc(-1) end)))
285     -- Create a taglist widget
286     s.mytaglist = awful.widget.taglist(s, awful.widget.taglist.filter.all, taglist_buttons)
287
288     -- Create a tasklist widget
289     s.mytasklist = awful.widget.tasklist(s, awful.widget.tasklist.filter.currenttags, tasklist_buttons)
290
291     -- Create the wibox, but only if there isn't one yet
292     if not s.mywibox then
293         s.mywibox = awful.wibar({ position = "top", screen = s })
294     end
295
296     -- Add widgets to the wibox
297     local right_widgets = gears.table.join(clocksarray, {
298         make_spacer(" "),
299         wibox.widget.systray(),
300         s.mylayoutbox,
301         layout = wibox.layout.fixed.horizontal,
302     })
303
304 --    if s == screen.primary then
305         right_widgets = gears.table.join({
306             make_spacer(" "),
307             ccwidgets.btc_widget,
308             make_spacer(),
309             --ccwidgets.eth_widget,
310             --make_spacer(),
311             --fxwidgets.ecb_widget,
312             --make_spacer(),
313             lain_bat.widget,
314             make_spacer(),
315         }, right_widgets)
316 --    end
317
318     s.mywibox:setup {
319         layout = wibox.layout.align.horizontal,
320         { -- Left widgets
321             layout = wibox.layout.fixed.horizontal,
322             --s.namebox,
323             s.mytaglist,
324             make_spacer(" "),
325             s.mypromptbox,
326         },
327         s.mytasklist, -- Middle widget
328         right_widgets,
329     }
330 end) -- }}}
331
332 -- {{{ autorandr integration
333 local function find_screen_by_pattern(pattern)
334     for s in screen do
335         print(s.name .. " :: " .. pattern)
336         if s.name:match(pattern) then
337             return s
338         end
339     end
340 end
341
342 local function get_target_screen_for_tag(tag)
343     local function primary_screen(reason)
344         local s = screen.primary
345         local msg = "  → primary screen \"" .. s.name .. "\""
346         if reason then msg = msg .. " (" .. reason .. ")" end
347         print(msg)
348         return s
349     end
350
351     print("Figuring out target screen for tag " .. tag.name .. "…")
352     if tag.targets then
353         if type(tag.targets) == "table" then
354             for _,target in ipairs(tag.targets) do
355                 local s = find_screen_by_pattern(target:gsub('%-', '%%-'))
356                 if s then
357                     print("  → screen " .. s.name)
358                     return s
359                 end
360             end
361         elseif tag.targets == "primary" then
362             return primary_screen("explicit request")
363         end
364         return primary_screen("no matching target in " .. table.concat(tag.targets, ","))
365     else
366         return primary_screen("no targets specified")
367     end
368 end
369
370 local function move_tag_to_target_screen(tag)
371     tag.screen = get_target_screen_for_tag(tag)
372 end
373
374 local function move_tags_to_target_screens()
375     for _,tag in ipairs(root.tags()) do
376         move_tag_to_target_screen(tag)
377     end
378 end
379
380 tag.connect_signal("request::screen", function(t)
381     -- throw the tag onto any other screen, it'll get reassigned later when
382     -- a new profile has been processed.
383     for s in screen do
384         if s ~= t.screen then
385             t.screen = s
386             t.selected = false
387             break
388         end
389     end
390     naughty.notify({
391         title = "Screen removed",
392         text = "Salvaged tab " .. t.name,
393     })
394 end)
395
396 function handle_new_autorandr_profile(newprofile)
397     -- The main idea here is that autorandr invokes this via awesome-client
398     -- after switching to a new profile. Awesome will have already set up all
399     -- the screens long before this function is called. Therefore, we just do
400     -- the necessary modifications to the existing screens, and move tags
401     -- around.
402
403     if not newprofile then
404         error("Missing new profile name")
405     end
406
407     naughty.notify({
408         preset = naughty.config.presets.low,
409         title = "New autorandr profile",
410         text = "Reconfiguring for profile <b>" .. newprofile .. "</b>",
411     })
412
413     for s in screen do
414         s:set_profile(newprofile)
415     end
416     move_tags_to_target_screens()
417 end
418
419 local function initialise_to_autorandr_profile()
420     local profile
421     profile = nil
422
423     local function process_line(line)
424         if profile then return end
425         local match = string.match(line, "^([^%s]+) %(detected%)")
426         if match then
427             profile = match
428         end
429     end
430
431     local function output_done()
432         if not profile then
433             error("autorandr detected no profile")
434             profile = "awesome"
435         end
436         handle_new_autorandr_profile(profile)
437     end
438
439     local function handle_exit(reason, code)
440         if not (reason == "exit" and code == 0) then
441             error("autorandr error: " .. reason .. ": " .. tostring(code))
442         end
443     end
444
445     awful.spawn.with_line_callback('autorandr', {
446         stdout = process_line,
447         output_done = output_done,
448         exit = handle_exit
449     })
450 end
451 awesome.connect_signal("startup", initialise_to_autorandr_profile)
452 -- }}}
453
454 -- }}}
455
456 -- {{{ Tags
457
458 local default_tag = {
459     name        = nil,
460     init        = true,
461     layout      = layouts.default,
462     fallback    = true,
463     targets     = "primary",
464 }
465 local default_tags = {}
466 for i = 1, 9 do
467     default_tags[i] = {}
468     for k,v in pairs(default_tag) do
469         default_tags[i][k] = v
470     end
471     default_tags[i].name = tostring(i)
472 end
473 default_tags[1].selected = true
474
475 default_tags = gears.table.join(default_tags, {
476   {
477     name        = "vrt",
478     init        = true,
479     exclusive   = true,
480     layout      = layouts.maximised,
481     selected    = false,
482     exec_once   = { "remmina" },
483     instance    = { "Remmina" },
484     targets     = {
485                     "gern/DP-?[12]-1",
486                     "gauting/eDP-?1",
487                     "toni/eDP-?1",
488                     "krafftwerk/DP-?1-1-5",
489                     "krafftwerk/DisplayPort-2",
490                     "cafe/eDP-?1"
491                 },
492   },
493   {
494     name        = "irc",
495     init        = true,
496     exclusive   = true,
497     layout      = layouts.tiled,
498     selected    = false,
499     exec_once   = { terminal .. " -name irc -e env MOSH_TITLE_NOPREFIX=true mosh --family=all -- irc-host tmux new -As irc irssi" },
500     instance    = { "irc" },
501     targets     = {
502                     "gern/DP-?[12]-1",
503                     "gauting/eDP-?1",
504                     "toni/eDP-?1",
505                     "krafftwerk/DP-?1-1-5",
506                     "krafftwerk/DisplayPort-2",
507                     "cafe/eDP-?1"
508                 },
509   },
510   {
511     name        = "[]",
512     init        = true,
513     exclusive   = true,
514     master_count = 0,
515     column_count = 4,
516     layout      = layouts.tiled,
517     selected    = false,
518     targets     = {
519                     "gern/DP-?[12]-1",
520                     "gauting/eDP-?1",
521                     "toni/eDP-?1",
522                     "krafftwerk/DP-?1-1-5",
523                     "krafftwerk/DisplayPort-2",
524                     "cafe/eDP-?1"
525                 },
526   },
527   {
528     name        = "dflt",
529     init        = false,
530     fallback    = true,
531     layout      = layouts.floating,
532     volatile    = true,
533     selected    = true,
534   },
535   {
536     name        = "cal",
537     init        = true,
538     exclusive   = true,
539     layout      = layouts.default,
540     exec_once   = { "thunderbird" },
541     class       = { "thunderbird" },
542     targets     = {
543                     "gern/DP-?[12]-1",
544                     "gauting/eDP-?1",
545                     "toni/eDP-?1",
546                     "krafftwerk/DP-?1-1-6",
547                     "krafftwerk/DisplayPort-0",
548                     "cafe/eDP-?1"
549                 },
550   },
551   {
552     name        = "chr",
553     init        = true,
554     exclusive   = true,
555     layout      = layouts.default,
556     exec_once   = { "chromium" },
557     class       = { "Chromium" },
558     targets     = {
559                     "gern/DP-?[12]-1",
560                     "gauting/eDP-?1",
561                     "toni/DP-?2-2",
562                     "krafftwerk/DP-?1-1-6",
563                     "krafftwerk/DisplayPort-0",
564                     "present/HDMI.*",
565                     "cafe/eDP-?1"
566                 },
567   },
568   {
569     name        = "ffx",
570     init        = true,
571     exclusive   = true,
572     layout      = layouts.default,
573     exec_once   = { "firefox" },
574     class       = { "Firefox" },
575     targets     = {
576                     "gern/DP-?[12]-1",
577                     "gauting/eDP-?1",
578                     "toni/DP-?2-2",
579                     "krafftwerk/DP-?1-1-6",
580                     "krafftwerk/DisplayPort-0",
581                     "present/HDMI.*",
582                     "cafe/eDP-?1"
583                 },
584   },
585 })
586
587 if not tyrannical then
588
589 for _,t in ipairs(default_tags) do
590     if t.init then
591         t.screen = t.screen or screen.primary
592         t.layout = t.layout or layouts.default
593         local newt = th.add_tag(t.name, t, false)
594     end
595 end
596
597 else -- {{{ tyrannical is loaded
598 tyrannical.settings.default_layout = layouts.default
599 tyrannical.settings.master_width_factor = 0.5
600 tyrannical.settings.block_children_focus_stealing = true
601 tyrannical.settings.group_children = true
602
603 tyrannical.tags = default_tags
604
605 tyrannical.properties.size_hints_honor = { URxvt = false }
606
607 --XX---- Ignore the tag "exclusive" property for the following clients (matched by classes)
608 --XX--tyrannical.properties.intrusive = {
609 --XX--  "ksnapshot"     , "pinentry"       , "gtksu"     , "kcalc"        , "xcalc"               ,
610 --XX--  "feh"           , "Gradient editor", "About KDE" , "Paste Special", "Background color"    ,
611 --XX--  "kcolorchooser" , "plasmoidviewer" , "Xephyr"    , "kruler"       , "plasmaengineexplorer",
612 --XX--}
613 --XX--
614 --XX---- Ignore the tiled layout for the matching clients
615 --XX--tyrannical.properties.floating = {
616 --XX--  "MPlayer"      , "pinentry"        , "ksnapshot"  , "pinentry"     , "gtksu"          ,
617 --XX--  "xine"         , "feh"             , "kmix"       , "kcalc"        , "xcalc"          ,
618 --XX--  "yakuake"      , "Select Color$"   , "kruler"     , "kcolorchooser", "Paste Special"  ,
619 --XX--  "New Form"     , "Insert Picture"  , "kcharselect", "mythfrontend" , "plasmoidviewer"
620 --XX--}
621 --XX--
622 --XX---- Make the matching clients (by classes) on top of the default layout
623 --XX--tyrannical.properties.ontop = {
624 --XX--  "Xephyr"       , "ksnapshot"       , "kruler"
625 --XX--}
626 --XX--
627 --XX---- Force the matching clients (by classes) to be centered on the screen on init
628 --XX--tyrannical.properties.centered = {
629 --XX--  "kcalc"
630 --XX--}
631 end -- }}}
632
633 -- }}}
634
635 -- {{{ Mouse bindings
636 root.buttons(gears.table.join(
637     awful.button({ }, 3, function () mymainmenu:toggle() end),
638     awful.button({ }, 4, awful.tag.viewnext),
639     awful.button({ }, 5, awful.tag.viewprev)
640 ))
641 -- }}}
642
643 -- {{{ Key bindings
644
645 local function toggle_tag_by_name(tagname, exclusive)
646     return function()
647         local t = awful.tag.find_by_name(nil, tagname)
648         if t then
649             if exclusive then
650                 t:view_only()
651             else
652                 awful.tag.viewtoggle(t)
653             end
654             cf = awful.client.getmaster(t.screen)
655             if cf then
656                 cf:jump_to()
657             end
658         end
659     end
660 end
661
662 local function move_tags_to_screen_relative(direction)
663     local s = awful.screen.focused()
664     for _,tag in ipairs(s.selected_tags) do
665         print("index: " .. s.index .. " count: " .. screen:count())
666         tag.screen = screen[(s.index + screen:count() + direction) % screen.count()]
667     end
668 end
669
670 globalkeys = gears.table.join(
671     awful.key({ modkey,           }, "s",      hotkeys_popup.show_help,
672               {description="show help", group="awesome"}),
673     awful.key({ modkey,           }, "Left",   awful.tag.viewprev,
674               {description = "view previous", group = "tag"}),
675     awful.key({ modkey,           }, "Right",  awful.tag.viewnext,
676               {description = "view next", group = "tag"}),
677     awful.key({ modkey,           }, "Escape", awful.tag.history.restore,
678               {description = "go back", group = "tag"}),
679
680     awful.key({ modkey,           }, "k",
681         function ()
682             awful.client.focus.byidx( 1)
683         end,
684         {description = "focus next by index", group = "client"}
685     ),
686     awful.key({ modkey,           }, "j",
687         function ()
688             awful.client.focus.byidx(-1)
689         end,
690         {description = "focus previous by index", group = "client"}
691     ),
692
693     -- Layout manipulation
694     awful.key({ modkey, "Shift"   }, "k", function () awful.client.swap.byidx( 1)    end,
695               {description = "swap with next client by index", group = "client"}),
696     awful.key({ modkey, "Shift"   }, "j", function () awful.client.swap.byidx(-1)    end,
697               {description = "swap with previous client by index", group = "client"}),
698     awful.key({ modkey, "Control" }, "k", function () awful.screen.focus_relative( 1) end,
699               {description = "focus the next screen", group = "screen"}),
700     awful.key({ modkey, "Control" }, "j", function () awful.screen.focus_relative(-1) end,
701               {description = "focus the previous screen", group = "screen"}),
702     awful.key({ modkey, "Shift", "Control" }, "k", function () move_tags_to_screen_relative( 1) end,
703               {description = "move tags to the next screen", group = "screen"}),
704     awful.key({ modkey, "Shift", "Control" }, "j", function () move_tags_to_screen_relative(-1) end,
705               {description = "move tags to the previous screen", group = "screen"}),
706     awful.key({ modkey, "Shift"   }, "Return", awful.client.urgent.jumpto,
707               {description = "jump to urgent client", group = "client"}),
708     awful.key({ modkey,           }, "Tab",
709         function ()
710             awful.client.focus.history.previous()
711             if client.focus then
712                 client.focus:raise()
713             end
714         end,
715         {description = "go back", group = "client"}),
716
717     -- Standard program
718     awful.key({ modkey,           }, "Return", function () awful.spawn(terminal) end,
719               {description = "open a terminal", group = "launcher"}),
720     awful.key({ modkey,           }, "r", function()
721         package.loaded.rc = nil
722         require("rc")
723     end,
724               {description = "reload rc.lua", group = "awesome"}),
725     awful.key({ modkey, "Control" }, "r", awesome.restart,
726               {description = "reload awesome", group = "awesome"}),
727     awful.key({ modkey, "Shift"   }, "q", awesome.quit,
728               {description = "quit awesome", group = "awesome"}),
729
730     awful.key({ modkey,           }, "l",     function () awful.tag.incmwfact( 0.05)          end,
731               {description = "increase master width factor", group = "layout"}),
732     awful.key({ modkey,           }, "h",     function () awful.tag.incmwfact(-0.05)          end,
733               {description = "decrease master width factor", group = "layout"}),
734     awful.key({ modkey, "Shift"   }, "h",     function () awful.tag.incnmaster( 1, nil, true) end,
735               {description = "increase the number of master clients", group = "layout"}),
736     awful.key({ modkey, "Shift"   }, "l",     function () awful.tag.incnmaster(-1, nil, true) end,
737               {description = "decrease the number of master clients", group = "layout"}),
738     awful.key({ modkey, "Control" }, "h",     function () awful.tag.incncol( 1, nil, true)    end,
739               {description = "increase the number of columns", group = "layout"}),
740     awful.key({ modkey, "Control" }, "l",     function () awful.tag.incncol(-1, nil, true)    end,
741               {description = "decrease the number of columns", group = "layout"}),
742     awful.key({ modkey,           }, "space", function () awful.layout.inc( 1)                end,
743               {description = "select next", group = "layout"}),
744     awful.key({ modkey, "Shift"   }, "space", function () awful.layout.inc(-1)                end,
745               {description = "select previous", group = "layout"}),
746
747     awful.key({ modkey, "Control" }, "n",
748               function ()
749                   local c = awful.client.restore()
750                   -- Focus restored client
751                   if c then
752                       client.focus = c
753                       c:raise()
754                   end
755               end,
756               {description = "restore minimized", group = "client"}),
757
758     -- Prompt
759     awful.key({ cmdkey },            "r",
760               function ()
761                   local widget = awful.screen.focused().mypromptbox.widget
762                   local function spawn(command, args)
763                       gears.debug.dump(args)
764                       awful.spawn(command, args)
765                   end
766
767                   awful.prompt.run {
768                     prompt       = "Exec: ",
769                     bg_cursor    = '#ff0000',
770                     textbox      = widget,
771                     history_path = awful.util.get_cache_dir() .. "/history",
772                     completion_callback = awful.completion.shell,
773                     hooks = {
774                         -- Replace the 'normal' Return with a custom one
775                         {{         }, 'Return', function(command)
776                             spawn(command)
777                         end},
778                         -- Spawn method to spawn in the current tag
779                         {{'Mod1'   }, 'Return', function(command)
780                             spawn(command,{
781                                 intrusive = true,
782                                 tag       = mouse.screen.selected_tag
783                             })
784                         end},
785                         -- Spawn in the current tag as floating and on top
786                         {{'Shift'  }, 'Return', function(command)
787                             spawn(command,{
788                                 ontop     = true,
789                                 floating  = true,
790                                 tag       = mouse.screen.selected_tag
791                             })
792                         end},
793                         -- Spawn in a new tag
794                         {{'Control'}, 'Return', function(command)
795                             spawn(command,{
796                                 new_tag = true,
797                                 layout = layouts.default,
798                                 volatile = true,
799                             })
800                         end},
801                         -- Cancel
802                         {{         }, 'Escape', function(_) return end},
803                     },
804                 }
805         end,
806               {description = "run prompt", group = "launcher"}),
807
808     awful.key({ modkey }, "x",
809               function ()
810                   awful.prompt.run {
811                     prompt       = "Eval: ",
812                     bg_cursor    = '#ff0000',
813                     textbox      = awful.screen.focused().mypromptbox.widget,
814                     exe_callback = awful.util.eval,
815                     history_path = awful.util.get_cache_dir() .. "/history_eval"
816                   }
817               end,
818               {description = "lua execute prompt", group = "awesome"}),
819     -- Menubar
820     awful.key({ modkey }, "w", function() menubar.show() end,
821               {description = "show the menubar", group = "launcher"}),
822
823     -- Tag helpers
824     awful.key({ modkey,           }, "a", function()
825         th.add_tag(nil, {layout=layouts.default} ,true)
826     end,
827     {description = "add a tag", group = "tag"}),
828     awful.key({ modkey,           }, "d", th.delete_tag,
829               {description = "delete the current tag", group = "tag"}),
830     awful.key({ modkey, "Shift",           }, "a", function()
831         th.move_to_new_tag(nil, nil, { layout = layouts.maximised },true,true,true)
832     end,
833               {description = "add a volatile tag with the focused client", group = "tag"}),
834     awful.key({ modkey, "Shift", "Control" }, "a", function()
835         th.move_to_new_tag(nil, nil, { layout = layouts.maximised },false,true,true)
836     end,
837               {description = "add a permanent tag with the focused client", group = "tag"}),
838     awful.key({ modkey, "Mod1"   }, "a", th.copy_tag,
839               {description = "create a copy of the current tag", group = "tag"}),
840     awful.key({ modkey, "Control"   }, "a", th.rename_tag,
841               {description = "rename the current tag", group = "tag"}),
842     awful.key({ modkey, "Control", "Shift", "Mod1" }, "a", th.collect_orphan_clients_to_tag,
843               {description = "collect all orphaned clients", group = "client"}),
844
845     awful.key({ modkey }, "t", toggle_tag_by_name("vrt", true),
846               {description = "view tag 'vrt'", group = "tag"}),
847     awful.key({ modkey, "Control" }, "t", toggle_tag_by_name("vrt"),
848               {description = "toggle tag 'vrt'", group = "tag"}),
849     awful.key({ modkey }, "y", toggle_tag_by_name("irc", true),
850               {description = "view tag 'irc'", group = "tag"}),
851     awful.key({ modkey, "Control" }, "y", toggle_tag_by_name("irc"),
852               {description = "toggle tag 'irc'", group = "tag"}),
853     awful.key({ modkey }, "u", toggle_tag_by_name("[]", true),
854               {description = "view tag '[]'", group = "tag"}),
855     awful.key({ modkey, "Control" }, "u", toggle_tag_by_name("[]"),
856               {description = "toggle tag '[]'", group = "tag"}),
857     awful.key({ modkey }, "i", toggle_tag_by_name("cal", true),
858               {description = "view tag 'cal'", group = "tag"}),
859     awful.key({ modkey, "Control" }, "i", toggle_tag_by_name("cal"),
860               {description = "toggle tag 'cal'", group = "tag"}),
861     awful.key({ modkey }, "o", toggle_tag_by_name("chr", true),
862               {description = "view tag 'chr'", group = "tag"}),
863     awful.key({ modkey, "Control" }, "o", toggle_tag_by_name("chr"),
864               {description = "toggle tag 'chr'", group = "tag"}),
865     awful.key({ modkey }, "p", toggle_tag_by_name("ffx", true),
866               {description = "view tag 'ff'", group = "tag"}),
867     awful.key({ modkey, "Control" }, "p", toggle_tag_by_name("ffx"),
868               {description = "toggle tag 'ff'", group = "tag"}),
869 {})
870
871 clientkeys = gears.table.join(
872     awful.key({ modkey,           }, "f",
873         function (c)
874             c.fullscreen = not c.fullscreen
875             c:raise()
876         end,
877         {description = "toggle fullscreen", group = "client"}),
878     awful.key({ modkey, "Shift"   }, "c",      function (c) c:kill()                         end,
879               {description = "close", group = "client"}),
880     awful.key({ modkey, "Control" }, "space",  awful.client.floating.toggle                     ,
881               {description = "toggle floating", group = "client"}),
882     awful.key({ modkey, "Control" }, "Return", function (c) c:swap(awful.client.getmaster()) end,
883               {description = "move to master", group = "client"}),
884     awful.key({ modkey,           }, "z",      function (c) c:move_to_screen() end,
885               {description = "move to screen", group = "client"}),
886     --awful.key({ modkey,           }, "t",      function (c) c.ontop = not c.ontop            end,
887     --          {description = "toggle keep on top", group = "client"}),
888     awful.key({ modkey,           }, "n",
889         function (c)
890             -- The client currently has the input focus, so it cannot be
891             -- minimized, since minimized clients can't have the focus.
892             c.minimized = true
893         end ,
894         {description = "minimize", group = "client"}),
895     awful.key({ modkey,           }, "m",
896         function (c)
897             c.maximized = not c.maximized
898             c.maximized_horizontal = false
899             c.maximized_vertical = false
900             c:raise()
901         end ,
902         {description = "(un)maximize", group = "client"}),
903     awful.key({ modkey, "Control" }, "m",
904         function (c)
905             c.maximized_vertical = not c.maximized_vertical
906             c:raise()
907         end ,
908         {description = "(un)maximize vertically", group = "client"}),
909     awful.key({ modkey, "Shift"   }, "m",
910         function (c)
911             c.maximized_horizontal = not c.maximized_horizontal
912             c:raise()
913         end ,
914         {description = "(un)maximize horizontally", group = "client"})
915 )
916
917 -- Bind all key numbers to tags.
918 -- Be careful: we use keycodes to make it work on any keyboard layout.
919 -- This should map on the top row of your keyboard, usually 1 to 9.
920 for i = 1, 9 do
921     globalkeys = gears.table.join(globalkeys,
922         -- View tag only.
923         awful.key({ modkey }, "#" .. i + 9, toggle_tag_by_name(tostring(i), true),
924                   {description = "view tag #"..i, group = "tag"}),
925         -- Toggle tag display.
926         awful.key({ modkey, "Control" }, "#" .. i + 9, toggle_tag_by_name(tostring(i)),
927                   {description = "toggle tag #" .. i, group = "tag"}),
928         -- Move client to tag.
929         awful.key({ modkey, "Shift" }, "#" .. i + 9,
930                   function ()
931                       if client.focus then
932                           local tag = awful.tag.find_by_name(screen.primary, tostring(i))
933                           if tag then
934                               client.focus:move_to_tag(tag)
935                           end
936                      end
937                   end,
938                   {description = "move focused client to tag #"..i, group = "tag"}),
939         -- Toggle tag on focused client.
940         awful.key({ modkey, "Control", "Shift" }, "#" .. i + 9,
941                   function ()
942                       if client.focus then
943                           local tag = awful.tag.find_by_name(screen.primary, tostring(i))
944                           if tag then
945                               client.focus:toggle_tag(tag)
946                           end
947                       end
948                   end,
949                   {description = "toggle focused client on tag #" .. i, group = "tag"})
950     )
951 end
952
953 clientbuttons = gears.table.join(
954     awful.button({ }, 1, function (c) client.focus = c; c:raise() end),
955     awful.button({ modkey }, 1, awful.mouse.client.move),
956     awful.button({ modkey }, 3, awful.mouse.client.resize))
957
958 -- misc apps
959 globalkeys = awful.util.table.join(globalkeys,
960 awful.key({ cmdkey }, "n", function () awful.spawn("firefox") end),
961 awful.key({ cmdkey }, "c", function () awful.spawn("chromium --enable-remote-extensions") end),
962 awful.key({ cmdkey }, "y", function () awful.spawn(terminal .. " -e ipython3") end),
963 awful.key({ cmdkey }, "m", function () awful.spawn(terminal .. " -name mutt -e mutt") end),
964 awful.key({ cmdkey }, "t", function () awful.spawn("thunderbird") end),
965 awful.key({ cmdkey }, "g", function () awful.spawn("gscan2pdf") end),
966 awful.key({ cmdkey, "Shift" }, "v", function () awful.spawn("virt-manager") end),
967 awful.key({ cmdkey, "Shift" }, "r", function () awful.spawn("remmina") end),
968 awful.key({ cmdkey }, "l", function () awful.spawn("libreoffice") end),
969 awful.key({ cmdkey }, "v", function () awful.spawn("virt-viewer") end),
970 awful.key({ cmdkey }, "p", function () awful.spawn("pavucontrol") end),
971 awful.key({ cmdkey }, "i", function () awful.spawn(terminal .. " -name irc -e env MOSH_TITLE_NOPREFIX=true mosh --family=all -- irc-host tmux new -As irc irssi") end),
972 awful.key({ cmdkey }, "x", function ()
973     run_output_notify("sh -c '. ~/.xsession.d/10-xautolock; echo \"Locking…\"; sleep 1; xautolock -locknow'", "Enabling xautolock")
974 end),
975 awful.key({ cmdkey, "Shift" }, "x", function () run_output_notify("pkill -e xautolock", "Disabling xautolock") end),
976
977 awful.key({ cmdkey }, "BackSpace", function () awful.spawn("pkill -USR1 offlineimap") end),
978
979 -- function keys
980 awful.key(nil, "XF86ScreenSaver", function () awful.spawn("xset dpms force off") end),
981 awful.key(nil, "XF86AudioMute", function () awful.spawn("pactl set-sink-mute @DEFAULT_SINK@ toggle") end),
982 awful.key({ cmdkey }, "End", function () awful.spawn("pactl set-sink-mute @DEFAULT_SINK@ toggle") end),
983 awful.key(nil, "XF86AudioLowerVolume", function () awful.spawn("pactl set-sink-volume @DEFAULT_SINK@ -2%") end),
984 awful.key({ cmdkey }, "Next", function () awful.spawn("pactl set-sink-volume @DEFAULT_SINK@ -2%") end),
985 awful.key(nil, "XF86AudioRaiseVolume", function () awful.spawn("pactl set-sink-volume @DEFAULT_SINK@ +2%") end),
986 awful.key({ cmdkey }, "Prior", function () awful.spawn("pactl set-sink-volume @DEFAULT_SINK@ +2%") end),
987 awful.key(nil, "XF86AudioMicMute", function () awful.spawn("pactl set-source-mute @DEFAULT_SOURCE@ toggle") end),
988 awful.key({ cmdkey }, "Home", function () awful.spawn("pactl set-source-mute @DEFAULT_SOURCE@ toggle") end),
989 awful.key({ cmdkey }, "Insert", function () awful.spawn("pa_cycle_default source") end),
990 awful.key({ cmdkey }, "Delete", function () awful.spawn("pa_cycle_default sink") end),
991 awful.key(nil, "XF86MonBrightnessDown", function () awful.spawn("light -U 10") end),
992 awful.key(nil, "XF86MonBrightnessUp", function () awful.spawn("light -A 10") end),
993 awful.key(nil, "XF86Display", function () awful.spawn("autorandr --change --force"); initialise_to_autorandr_profile() end),
994 awful.key(nil, "XF86AudioStop", function () awful.spawn("autorandr --change --force"); initialise_to_autorandr_profile() end),
995 awful.key(nil, "XF86WLAN", function () awful.spawn("") end),
996 awful.key(nil, "XF86Tools", function () awful.spawn("") end),
997 awful.key(nil, "XF86Search", function () awful.spawn("") end),
998 awful.key(nil, "XF86LaunchA", function () awful.spawn("") end),
999 awful.key(nil, "XF86Explorer", function () awful.spawn("") end),
1000 awful.key(nil, "XF86Calculator", function () awful.spawn(terminal .. " -e ipython3 --profile=calc") end),
1001 awful.key(nil, "XF86Favorites", function () awful.spawn("systemctl suspend") end),
1002
1003 awful.key({ cmdkey }, "Multi_key", function () run_output_notify("flameshot gui", "Output") end),
1004 awful.key({ cmdkey, "Shift" }, "Multi_key", function () run_output_notify("flameshot full --delay 2000 --clipboard", "Output") end),
1005
1006 awful.key({ cmdkey }, "Up", function () awful.spawn("pap prev") end),
1007 awful.key({ cmdkey }, "Left", function () awful.spawn("pap seek -10") end),
1008 awful.key({ cmdkey, "Shift" }, "Left", function () awful.spawn("pap seek -60") end),
1009 awful.key({ cmdkey }, "Down", function () awful.spawn("pap next") end),
1010 awful.key({ cmdkey }, "Right", function () awful.spawn("pap seek +10") end),
1011 awful.key({ cmdkey, "Shift" }, "Right", function () awful.spawn("pap seek +60") end),
1012 awful.key({ cmdkey }, "space", function () awful.spawn("pap pause") end),
1013 awful.key({ cmdkey }, "\\", function () run_output_notify("pap info", "Track info") end),
1014 awful.key({ cmdkey }, "]", function () run_output_notify("pap list", "Playlist") end)
1015 )
1016
1017 function run_output_notify(cmd, title)
1018     awful.spawn.easy_async(cmd, function(stdout, stderr, reason, exit_code)
1019         if #stdout > 1 then
1020             naughty.notify({
1021                 preset = naughty.config.presets.low,
1022                 title = title,
1023                 text = stdout})
1024             end
1025         end)
1026 end
1027
1028 -- Set keys
1029 root.keys(globalkeys)
1030 -- }}}
1031
1032 -- {{{ Rules
1033 -- Rules to apply to new clients (through the "manage" signal).
1034
1035 local function float_client_in_the_middle_with_margins(client, leftright, topbottom)
1036     local wa = client.screen.workarea
1037     if topbottom then
1038         client.y = wa.y + topbottom
1039         client.height = wa.height - 2*topbottom
1040     else
1041         client.y = wa.y + (wa.height - client.height)/2
1042     end
1043     if leftright then
1044         client.x = wa.x + leftright
1045         client.width = wa.width - 2*leftright
1046     else
1047         client.x = wa.x + (wa.width - client.width)/2
1048     end
1049 end
1050
1051 local function move_to_tag_by_name(s, tagname)
1052     return function(c)
1053         local t = awful.tag.find_by_name(s, tagname)
1054         if not t then
1055             error("No tag by the name of " .. tagname)
1056             return
1057         end
1058         c:move_to_tag(t)
1059     end
1060 end
1061
1062 local function move_to_tag_or_create_volatile(s, tagname)
1063     return function(c)
1064         local t = awful.tag.find_by_name(s, tagname)
1065         if t then
1066             c:move_to_tag(t)
1067         else
1068             th.move_to_new_tag(c, tagname, {}, true, true, true)
1069         end
1070     end
1071 end
1072
1073 awful.rules.rules = {
1074     -- All clients will match this rule.
1075     { rule = { },
1076       properties = { border_width = beautiful.border_width,
1077                      border_color = beautiful.border_normal,
1078                      focus = awful.client.focus.filter,
1079                      raise = true,
1080                      keys = clientkeys,
1081                      buttons = clientbuttons,
1082                      screen = awful.screen.preferred,
1083                      placement = awful.placement.no_overlap+awful.placement.no_offscreen,
1084                      floating = false,
1085                      maximized = false,
1086                  },
1087     },
1088     { rule = { type = "dialog" },
1089       properties = { floating = true,
1090                      ontop = true,
1091                      skip_taskbar = true,
1092                      urgent = true,
1093                      --new_tag = true,
1094                      --switchtotag = true,
1095                      placement = awful.placement.centered
1096                    }
1097     },
1098     { rule = { class = "URxvt" },
1099       properties = { size_hints_honor = false, }
1100     },
1101     { rule = { instance = "irc" },
1102       callback = move_to_tag_by_name(nil, "irc"),
1103     },
1104     { rule = { class = "scrcpy" },
1105       callback = move_to_tag_by_name(nil, "[]"),
1106     },
1107     { rule_any = { class = { "Firefox", "firefox" } },
1108       callback = move_to_tag_by_name(nil, "ffx"),
1109     },
1110     { rule_any = { class = { "org.remmina.Remmina",
1111                              "Virt-viewer",
1112                              "Virt-manager"
1113                            },
1114                  },
1115       callback = move_to_tag_by_name(nil, "vrt"),
1116     },
1117     { rule = { class = "Chromium" },
1118       callback = move_to_tag_by_name(nil, "chr"),
1119     },
1120     { rule_any = { class = { "thunderbird", "Thunderbird" } },
1121       callback = move_to_tag_by_name(nil, "cal"),
1122     },
1123     { rule = { instance = "mutt" },
1124       properties = {
1125           new_tag = {
1126               name = "mutt",
1127               layout = awful.layout.suit.fair.horizontal,
1128               volatile = true
1129           },
1130           switchtotag = true,
1131       },
1132     },
1133     { rule_any = { class = { "zoom" } },
1134       callback = move_to_tag_or_create_volatile(nil, "Zoom"),
1135     },
1136     { rule_any = { class = { "Ssvnc.tcl" },
1137                    class = { "Ssvnc" },
1138                    name = { "SSL/SSH VNC Viewer.-" },
1139                  },
1140       callback = move_to_tag_or_create_volatile(nil, "SSVNC"),
1141     },
1142     { rule_any = { class = {
1143         "Gxmessage",
1144         "Pinentry"
1145     }},
1146       properties = { floating = true,
1147                      maximized = false,
1148                      focus = true,
1149                      placement = awful.placement.centered,
1150                    },
1151     },
1152     { rule_any = { instance = {
1153         "tridactyl-edit",
1154         "pdfshuffler",
1155         "vlc",
1156         "pavucontrol"
1157     }},
1158       properties = { floating = true,
1159                      maximized = false,
1160                      focus = true,
1161                      placement = awful.placement.centered,
1162                    },
1163     },
1164     { rule_any = { class = {
1165                         "Gimp",
1166                         "Inkscape",
1167                         "Pitivi",
1168                         "Audacity",
1169                     },
1170                     instance = {
1171                         "libreoffice",
1172                     }
1173                 },
1174       except_any = { type = { "dialog" } },
1175       properties = { new_tag = {
1176                         layout = layouts.maximised,
1177                         volatile = true,
1178                     },
1179                      switchtotag = true,
1180                      focus = true,
1181                    },
1182                },
1183     { rule_any = { class = {
1184                         "Gscan2pdf",
1185                     },
1186                 },
1187       except_any = { type = { "dialog" } },
1188       properties = { new_tag = {
1189                         layout = layouts.default,
1190                         volatile = true,
1191                     },
1192                     floating = true,
1193                     maximized = false,
1194                     focus = true,
1195                     placement = awful.placement.centered,
1196                     switchtotag = true,
1197                     focus = true,
1198                    },
1199                },
1200 --XX--    { rule = { class = "Gscan2pdf" },
1201 --XX--               properties = {
1202 --XX--                   switchtotag = true
1203 --XX--               },
1204 --XX--               callback = move_to_tag(1, 5)
1205 --XX--           },
1206 --XX--    { rule = { name = "gscan2pdf .*" },
1207 --XX--               properties = {
1208 --XX--                   floating = false,
1209 --XX--               },
1210 --XX--           },
1211 --XX--    { rule = { class = "Thunar", type = "normal" },
1212 --XX--               properties = {
1213 --XX--                   floating = false,
1214 --XX--               },
1215 --XX--           },
1216 --XX--    { rule = { class = "Pinentry", instance = "pinentry" },
1217 --XX--               properties = {
1218 --XX--                   floating = true,
1219 --XX--               },
1220 --XX--           },
1221 --XX--    { rule = { class = "Gxmessage" },
1222 --XX--               properties = {
1223 --XX--                   floating = true,
1224 --XX--               },
1225 --XX--           },
1226 --XX--}
1227 }
1228 -- }}}
1229
1230 -- {{{ Signals
1231 -- Signal function to execute when a new client appears.
1232 client.connect_signal("manage", function (c)
1233     -- Set the windows at the slave,
1234     -- i.e. put it at the end of others instead of setting it master.
1235     -- if not awesome.startup then awful.client.setslave(c) end
1236     --if not awesome.startup then
1237     --    local t = awful.screen.focused().selected_tag
1238     --    if t.name == "xmutt" then
1239     --        awful.client.setslave(c)
1240     --    end
1241     --end
1242
1243     if awesome.startup and
1244       not c.size_hints.user_position
1245       and not c.size_hints.program_position then
1246         -- Prevent clients from being unreachable after screen count changes.
1247         awful.placement.no_offscreen(c)
1248     end
1249
1250     c.maximized_horizontal = false
1251     c.maximized_vertical = false
1252 end)
1253
1254 -- Add a titlebar if titlebars_enabled is set to true in the rules.
1255 client.connect_signal("request::titlebars", function(c)
1256     -- buttons for the titlebar
1257     local buttons = gears.table.join(
1258         awful.button({ }, 1, function()
1259             client.focus = c
1260             c:raise()
1261             awful.mouse.client.move(c)
1262         end),
1263         awful.button({ }, 3, function()
1264             client.focus = c
1265             c:raise()
1266             awful.mouse.client.resize(c)
1267         end)
1268     )
1269
1270     awful.titlebar(c) : setup {
1271         { -- Left
1272             awful.titlebar.widget.iconwidget(c),
1273             buttons = buttons,
1274             layout  = wibox.layout.fixed.horizontal
1275         },
1276         { -- Middle
1277             { -- Title
1278                 align  = "center",
1279                 widget = awful.titlebar.widget.titlewidget(c)
1280             },
1281             buttons = buttons,
1282             layout  = wibox.layout.flex.horizontal
1283         },
1284         { -- Right
1285             awful.titlebar.widget.floatingbutton (c),
1286             awful.titlebar.widget.maximizedbutton(c),
1287             awful.titlebar.widget.stickybutton   (c),
1288             awful.titlebar.widget.ontopbutton    (c),
1289             awful.titlebar.widget.closebutton    (c),
1290             layout = wibox.layout.fixed.horizontal()
1291         },
1292         layout = wibox.layout.align.horizontal
1293     }
1294 end)
1295
1296 -- Enable sloppy focus, so that focus follows mouse.
1297 client.connect_signal("mouse::enter", function(c)
1298     if awful.layout.get(c.screen) ~= awful.layout.suit.magnifier
1299         and awful.client.focus.filter(c) then
1300         client.focus = c
1301     end
1302 --17 18:03 < psychon> madduck: yes. In the default config at the very end there is code that actives a client on mouse::enter. Just add if c.class == "whatever virt-viewer uses" then return end to that, or 
1303 --                    something like this
1304 end)
1305
1306 client.connect_signal("focus", function(c)
1307     c.border_color = beautiful.border_focus
1308 end)
1309 client.connect_signal("unfocus", function(c)
1310     c.border_color = beautiful.border_normal
1311 end)
1312
1313 awful.ewmh.add_activate_filter(function(c, context, hints)
1314     if context == "ewmh" then
1315         if (c.class == "Firefox-esr" or c.class == "Firefox") then
1316             return false
1317         end
1318     end
1319 end)
1320
1321 client.connect_signal("request::activate", function(c, context, hints)
1322     if gears.table.hasitem({
1323         "client.focus.byidx",
1324         "client.jumpto",
1325         "autofocus.check_focus",
1326         "rules",
1327         "ewmh",
1328     }, context) then
1329         gears.timer.delayed_call(function()
1330             -- we need a delayed call so that we execute *after layout changes
1331             if hints.raise and c == client.focus and client.focus:isvisible() then
1332                 move_mouse_to_area(client.focus)
1333             end
1334         end)
1335     end
1336 end)
1337
1338 -- vim:ft=lua:sw=4:sts=4:ts=4:et