]> 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:

autorandr unification across machines
[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        = "irc",
478     init        = true,
479     exclusive   = true,
480     layout      = layouts.tiled,
481     selected    = true,
482     exec_once   = { terminal .. " -name irc -e env MOSH_TITLE_NOPREFIX=true mosh --family=all -- irc-host tmux new -As irc irssi" },
483     instance    = { "irc" },
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        = "[]",
495     init        = true,
496     exclusive   = true,
497     master_count = 0,
498     column_count = 4,
499     layout      = layouts.tiled,
500     selected    = false,
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        = "dflt",
512     init        = false,
513     fallback    = true,
514     layout      = layouts.floating,
515     volatile    = true,
516     selected    = true,
517   },
518   {
519     name        = "cal",
520     init        = true,
521     exclusive   = true,
522     layout      = layouts.default,
523     exec_once   = { "thunderbird" },
524     class       = { "thunderbird" },
525     targets     = {
526                     "gern/DP-?[12]-1",
527                     "gauting/eDP-?1",
528                     "toni/eDP-?1",
529                     "krafftwerk/DP-?1-1-6",
530                     "krafftwerk/DisplayPort-0",
531                     "cafe/eDP-?1"
532                 },
533   },
534   {
535     name        = "chr",
536     init        = true,
537     exclusive   = true,
538     layout      = layouts.default,
539     exec_once   = { "chromium" },
540     class       = { "Chromium" },
541     targets     = {
542                     "gern/DP-?[12]-1",
543                     "gauting/eDP-?1",
544                     "toni/DP-?2-2",
545                     "krafftwerk/DP-?1-1-6",
546                     "krafftwerk/DisplayPort-0",
547                     "present/HDMI.*",
548                     "cafe/eDP-?1"
549                 },
550   },
551   {
552     name        = "ffx",
553     init        = true,
554     exclusive   = true,
555     layout      = layouts.default,
556     exec_once   = { "firefox" },
557     class       = { "Firefox" },
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
570 if not tyrannical then
571
572 for _,t in ipairs(default_tags) do
573     if t.init then
574         t.screen = t.screen or screen.primary
575         t.layout = t.layout or layouts.default
576         local newt = th.add_tag(t.name, t, false)
577     end
578 end
579
580 else -- {{{ tyrannical is loaded
581 tyrannical.settings.default_layout = layouts.default
582 tyrannical.settings.master_width_factor = 0.5
583 tyrannical.settings.block_children_focus_stealing = true
584 tyrannical.settings.group_children = true
585
586 tyrannical.tags = default_tags
587
588 tyrannical.properties.size_hints_honor = { URxvt = false }
589
590 --XX---- Ignore the tag "exclusive" property for the following clients (matched by classes)
591 --XX--tyrannical.properties.intrusive = {
592 --XX--  "ksnapshot"     , "pinentry"       , "gtksu"     , "kcalc"        , "xcalc"               ,
593 --XX--  "feh"           , "Gradient editor", "About KDE" , "Paste Special", "Background color"    ,
594 --XX--  "kcolorchooser" , "plasmoidviewer" , "Xephyr"    , "kruler"       , "plasmaengineexplorer",
595 --XX--}
596 --XX--
597 --XX---- Ignore the tiled layout for the matching clients
598 --XX--tyrannical.properties.floating = {
599 --XX--  "MPlayer"      , "pinentry"        , "ksnapshot"  , "pinentry"     , "gtksu"          ,
600 --XX--  "xine"         , "feh"             , "kmix"       , "kcalc"        , "xcalc"          ,
601 --XX--  "yakuake"      , "Select Color$"   , "kruler"     , "kcolorchooser", "Paste Special"  ,
602 --XX--  "New Form"     , "Insert Picture"  , "kcharselect", "mythfrontend" , "plasmoidviewer"
603 --XX--}
604 --XX--
605 --XX---- Make the matching clients (by classes) on top of the default layout
606 --XX--tyrannical.properties.ontop = {
607 --XX--  "Xephyr"       , "ksnapshot"       , "kruler"
608 --XX--}
609 --XX--
610 --XX---- Force the matching clients (by classes) to be centered on the screen on init
611 --XX--tyrannical.properties.centered = {
612 --XX--  "kcalc"
613 --XX--}
614 end -- }}}
615
616 -- }}}
617
618 -- {{{ Mouse bindings
619 root.buttons(gears.table.join(
620     awful.button({ }, 3, function () mymainmenu:toggle() end),
621     awful.button({ }, 4, awful.tag.viewnext),
622     awful.button({ }, 5, awful.tag.viewprev)
623 ))
624 -- }}}
625
626 -- {{{ Key bindings
627
628 local function toggle_tag_by_name(tagname, exclusive)
629     return function()
630         local t = awful.tag.find_by_name(nil, tagname)
631         if t then
632             if exclusive then
633                 t:view_only()
634             else
635                 awful.tag.viewtoggle(t)
636             end
637             cf = awful.client.getmaster(t.screen)
638             if cf then
639                 cf:jump_to()
640             end
641         end
642     end
643 end
644
645 local function move_tags_to_screen_relative(direction)
646     local s = awful.screen.focused()
647     for _,tag in ipairs(s.selected_tags) do
648         print("index: " .. s.index .. " count: " .. screen:count())
649         tag.screen = screen[(s.index + screen:count() + direction) % screen.count()]
650     end
651 end
652
653 globalkeys = gears.table.join(
654     awful.key({ modkey,           }, "s",      hotkeys_popup.show_help,
655               {description="show help", group="awesome"}),
656     awful.key({ modkey,           }, "Left",   awful.tag.viewprev,
657               {description = "view previous", group = "tag"}),
658     awful.key({ modkey,           }, "Right",  awful.tag.viewnext,
659               {description = "view next", group = "tag"}),
660     awful.key({ modkey,           }, "Escape", awful.tag.history.restore,
661               {description = "go back", group = "tag"}),
662
663     awful.key({ modkey,           }, "k",
664         function ()
665             awful.client.focus.byidx( 1)
666         end,
667         {description = "focus next by index", group = "client"}
668     ),
669     awful.key({ modkey,           }, "j",
670         function ()
671             awful.client.focus.byidx(-1)
672         end,
673         {description = "focus previous by index", group = "client"}
674     ),
675
676     -- Layout manipulation
677     awful.key({ modkey, "Shift"   }, "k", function () awful.client.swap.byidx( 1)    end,
678               {description = "swap with next client by index", group = "client"}),
679     awful.key({ modkey, "Shift"   }, "j", function () awful.client.swap.byidx(-1)    end,
680               {description = "swap with previous client by index", group = "client"}),
681     awful.key({ modkey, "Control" }, "k", function () awful.screen.focus_relative( 1) end,
682               {description = "focus the next screen", group = "screen"}),
683     awful.key({ modkey, "Control" }, "j", function () awful.screen.focus_relative(-1) end,
684               {description = "focus the previous screen", group = "screen"}),
685     awful.key({ modkey, "Shift", "Control" }, "k", function () move_tags_to_screen_relative( 1) end,
686               {description = "move tags to the next screen", group = "screen"}),
687     awful.key({ modkey, "Shift", "Control" }, "j", function () move_tags_to_screen_relative(-1) end,
688               {description = "move tags to the previous screen", group = "screen"}),
689     awful.key({ modkey, "Shift"   }, "Return", awful.client.urgent.jumpto,
690               {description = "jump to urgent client", group = "client"}),
691     awful.key({ modkey,           }, "Tab",
692         function ()
693             awful.client.focus.history.previous()
694             if client.focus then
695                 client.focus:raise()
696             end
697         end,
698         {description = "go back", group = "client"}),
699
700     -- Standard program
701     awful.key({ modkey,           }, "Return", function () awful.spawn(terminal) end,
702               {description = "open a terminal", group = "launcher"}),
703     awful.key({ modkey,           }, "r", function()
704         package.loaded.rc = nil
705         require("rc")
706     end,
707               {description = "reload rc.lua", group = "awesome"}),
708     awful.key({ modkey, "Control" }, "r", awesome.restart,
709               {description = "reload awesome", group = "awesome"}),
710     awful.key({ modkey, "Shift"   }, "q", awesome.quit,
711               {description = "quit awesome", group = "awesome"}),
712
713     awful.key({ modkey,           }, "l",     function () awful.tag.incmwfact( 0.05)          end,
714               {description = "increase master width factor", group = "layout"}),
715     awful.key({ modkey,           }, "h",     function () awful.tag.incmwfact(-0.05)          end,
716               {description = "decrease master width factor", group = "layout"}),
717     awful.key({ modkey, "Shift"   }, "h",     function () awful.tag.incnmaster( 1, nil, true) end,
718               {description = "increase the number of master clients", group = "layout"}),
719     awful.key({ modkey, "Shift"   }, "l",     function () awful.tag.incnmaster(-1, nil, true) end,
720               {description = "decrease the number of master clients", group = "layout"}),
721     awful.key({ modkey, "Control" }, "h",     function () awful.tag.incncol( 1, nil, true)    end,
722               {description = "increase the number of columns", group = "layout"}),
723     awful.key({ modkey, "Control" }, "l",     function () awful.tag.incncol(-1, nil, true)    end,
724               {description = "decrease the number of columns", group = "layout"}),
725     awful.key({ modkey,           }, "space", function () awful.layout.inc( 1)                end,
726               {description = "select next", group = "layout"}),
727     awful.key({ modkey, "Shift"   }, "space", function () awful.layout.inc(-1)                end,
728               {description = "select previous", group = "layout"}),
729
730     awful.key({ modkey, "Control" }, "n",
731               function ()
732                   local c = awful.client.restore()
733                   -- Focus restored client
734                   if c then
735                       client.focus = c
736                       c:raise()
737                   end
738               end,
739               {description = "restore minimized", group = "client"}),
740
741     -- Prompt
742     awful.key({ cmdkey },            "r",
743               function ()
744                   local widget = awful.screen.focused().mypromptbox.widget
745                   local function spawn(command, args)
746                       gears.debug.dump(args)
747                       awful.spawn(command, args)
748                   end
749
750                   awful.prompt.run {
751                     prompt       = "Exec: ",
752                     bg_cursor    = '#ff0000',
753                     textbox      = widget,
754                     history_path = awful.util.get_cache_dir() .. "/history",
755                     completion_callback = awful.completion.shell,
756                     hooks = {
757                         -- Replace the 'normal' Return with a custom one
758                         {{         }, 'Return', function(command)
759                             spawn(command)
760                         end},
761                         -- Spawn method to spawn in the current tag
762                         {{'Mod1'   }, 'Return', function(command)
763                             spawn(command,{
764                                 intrusive = true,
765                                 tag       = mouse.screen.selected_tag
766                             })
767                         end},
768                         -- Spawn in the current tag as floating and on top
769                         {{'Shift'  }, 'Return', function(command)
770                             spawn(command,{
771                                 ontop     = true,
772                                 floating  = true,
773                                 tag       = mouse.screen.selected_tag
774                             })
775                         end},
776                         -- Spawn in a new tag
777                         {{'Control'}, 'Return', function(command)
778                             spawn(command,{
779                                 new_tag = true,
780                                 layout = layouts.default,
781                                 volatile = true,
782                             })
783                         end},
784                         -- Cancel
785                         {{         }, 'Escape', function(_) return end},
786                     },
787                 }
788         end,
789               {description = "run prompt", group = "launcher"}),
790
791     awful.key({ modkey }, "x",
792               function ()
793                   awful.prompt.run {
794                     prompt       = "Eval: ",
795                     bg_cursor    = '#ff0000',
796                     textbox      = awful.screen.focused().mypromptbox.widget,
797                     exe_callback = awful.util.eval,
798                     history_path = awful.util.get_cache_dir() .. "/history_eval"
799                   }
800               end,
801               {description = "lua execute prompt", group = "awesome"}),
802     -- Menubar
803     awful.key({ modkey }, "w", function() menubar.show() end,
804               {description = "show the menubar", group = "launcher"}),
805
806     -- Tag helpers
807     awful.key({ modkey,           }, "a", function()
808         th.add_tag(nil, {layout=layouts.default} ,true)
809     end,
810     {description = "add a tag", group = "tag"}),
811     awful.key({ modkey,           }, "d", th.delete_tag,
812               {description = "delete the current tag", group = "tag"}),
813     awful.key({ modkey, "Shift",           }, "a", function()
814         th.move_to_new_tag(nil, nil, { layout = layouts.maximised },true,true,true)
815     end,
816               {description = "add a volatile tag with the focused client", group = "tag"}),
817     awful.key({ modkey, "Shift", "Control" }, "a", function()
818         th.move_to_new_tag(nil, nil, { layout = layouts.maximised },false,true,true)
819     end,
820               {description = "add a permanent tag with the focused client", group = "tag"}),
821     awful.key({ modkey, "Mod1"   }, "a", th.copy_tag,
822               {description = "create a copy of the current tag", group = "tag"}),
823     awful.key({ modkey, "Control"   }, "a", th.rename_tag,
824               {description = "rename the current tag", group = "tag"}),
825     awful.key({ modkey, "Control", "Shift", "Mod1" }, "a", th.collect_orphan_clients_to_tag,
826               {description = "collect all orphaned clients", group = "client"}),
827
828     awful.key({ modkey }, "y", toggle_tag_by_name("irc", true),
829               {description = "view tag 'irc'", group = "tag"}),
830     awful.key({ modkey, "Control" }, "y", toggle_tag_by_name("irc"),
831               {description = "toggle tag 'irc'", group = "tag"}),
832     awful.key({ modkey }, "u", toggle_tag_by_name("[]", true),
833               {description = "view tag '[]'", group = "tag"}),
834     awful.key({ modkey, "Control" }, "u", toggle_tag_by_name("[]"),
835               {description = "toggle tag '[]'", group = "tag"}),
836     awful.key({ modkey }, "i", toggle_tag_by_name("cal", true),
837               {description = "view tag 'cal'", group = "tag"}),
838     awful.key({ modkey, "Control" }, "i", toggle_tag_by_name("cal"),
839               {description = "toggle tag 'cal'", group = "tag"}),
840     awful.key({ modkey }, "o", toggle_tag_by_name("chr", true),
841               {description = "view tag 'chr'", group = "tag"}),
842     awful.key({ modkey, "Control" }, "o", toggle_tag_by_name("chr"),
843               {description = "toggle tag 'chr'", group = "tag"}),
844     awful.key({ modkey }, "p", toggle_tag_by_name("ffx", true),
845               {description = "view tag 'ff'", group = "tag"}),
846     awful.key({ modkey, "Control" }, "p", toggle_tag_by_name("ffx"),
847               {description = "toggle tag 'ff'", group = "tag"}),
848 {})
849
850 clientkeys = gears.table.join(
851     awful.key({ modkey,           }, "f",
852         function (c)
853             c.fullscreen = not c.fullscreen
854             c:raise()
855         end,
856         {description = "toggle fullscreen", group = "client"}),
857     awful.key({ modkey, "Shift"   }, "c",      function (c) c:kill()                         end,
858               {description = "close", group = "client"}),
859     awful.key({ modkey, "Control" }, "space",  awful.client.floating.toggle                     ,
860               {description = "toggle floating", group = "client"}),
861     awful.key({ modkey, "Control" }, "Return", function (c) c:swap(awful.client.getmaster()) end,
862               {description = "move to master", group = "client"}),
863     awful.key({ modkey,           }, "z",      function (c) c:move_to_screen() end,
864               {description = "move to screen", group = "client"}),
865     awful.key({ modkey,           }, "t",      function (c) c.ontop = not c.ontop            end,
866               {description = "toggle keep on top", group = "client"}),
867     awful.key({ modkey,           }, "n",
868         function (c)
869             -- The client currently has the input focus, so it cannot be
870             -- minimized, since minimized clients can't have the focus.
871             c.minimized = true
872         end ,
873         {description = "minimize", group = "client"}),
874     awful.key({ modkey,           }, "m",
875         function (c)
876             c.maximized = not c.maximized
877             c.maximized_horizontal = false
878             c.maximized_vertical = false
879             c:raise()
880         end ,
881         {description = "(un)maximize", group = "client"}),
882     awful.key({ modkey, "Control" }, "m",
883         function (c)
884             c.maximized_vertical = not c.maximized_vertical
885             c:raise()
886         end ,
887         {description = "(un)maximize vertically", group = "client"}),
888     awful.key({ modkey, "Shift"   }, "m",
889         function (c)
890             c.maximized_horizontal = not c.maximized_horizontal
891             c:raise()
892         end ,
893         {description = "(un)maximize horizontally", group = "client"})
894 )
895
896 -- Bind all key numbers to tags.
897 -- Be careful: we use keycodes to make it work on any keyboard layout.
898 -- This should map on the top row of your keyboard, usually 1 to 9.
899 for i = 1, 9 do
900     globalkeys = gears.table.join(globalkeys,
901         -- View tag only.
902         awful.key({ modkey }, "#" .. i + 9, toggle_tag_by_name(tostring(i), true),
903                   {description = "view tag #"..i, group = "tag"}),
904         -- Toggle tag display.
905         awful.key({ modkey, "Control" }, "#" .. i + 9, toggle_tag_by_name(tostring(i)),
906                   {description = "toggle tag #" .. i, group = "tag"}),
907         -- Move client to tag.
908         awful.key({ modkey, "Shift" }, "#" .. i + 9,
909                   function ()
910                       if client.focus then
911                           local tag = awful.tag.find_by_name(screen.primary, tostring(i))
912                           if tag then
913                               client.focus:move_to_tag(tag)
914                           end
915                      end
916                   end,
917                   {description = "move focused client to tag #"..i, group = "tag"}),
918         -- Toggle tag on focused client.
919         awful.key({ modkey, "Control", "Shift" }, "#" .. i + 9,
920                   function ()
921                       if client.focus then
922                           local tag = awful.tag.find_by_name(screen.primary, tostring(i))
923                           if tag then
924                               client.focus:toggle_tag(tag)
925                           end
926                       end
927                   end,
928                   {description = "toggle focused client on tag #" .. i, group = "tag"})
929     )
930 end
931
932 clientbuttons = gears.table.join(
933     awful.button({ }, 1, function (c) client.focus = c; c:raise() end),
934     awful.button({ modkey }, 1, awful.mouse.client.move),
935     awful.button({ modkey }, 3, awful.mouse.client.resize))
936
937 -- misc apps
938 globalkeys = awful.util.table.join(globalkeys,
939 awful.key({ cmdkey }, "n", function () awful.spawn("firefox") end),
940 awful.key({ cmdkey }, "c", function () awful.spawn("chromium --enable-remote-extensions") end),
941 awful.key({ cmdkey }, "y", function () awful.spawn(terminal .. " -e ipython3") end),
942 awful.key({ cmdkey }, "m", function () awful.spawn(terminal .. " -name mutt -e mutt") end),
943 awful.key({ cmdkey }, "t", function () awful.spawn("thunderbird") end),
944 awful.key({ cmdkey }, "g", function () awful.spawn("gscan2pdf") end),
945 awful.key({ cmdkey, "Shift" }, "v", function () awful.spawn("virt-manager") end),
946 awful.key({ cmdkey }, "l", function () awful.spawn("libreoffice") end),
947 awful.key({ cmdkey }, "v", function () awful.spawn("remmina") end),
948 awful.key({ cmdkey }, "p", function () awful.spawn("pavucontrol") end),
949 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),
950 awful.key({ cmdkey }, "x", function ()
951     awful.spawn("xautolock -enable")
952     awful.spawn("xautolock -locknow")
953 end),
954 awful.key({ cmdkey, "Shift" }, "x", function () awful.spawn("xautolock -disable") end),
955
956 awful.key({ cmdkey }, "BackSpace", function () awful.spawn("pkill -USR1 offlineimap") end),
957
958 -- function keys
959 awful.key(nil, "XF86ScreenSaver", function () awful.spawn("xset dpms force off") end),
960 awful.key(nil, "XF86AudioMute", function () awful.spawn("pactl set-sink-mute @DEFAULT_SINK@ toggle") end),
961 awful.key({ cmdkey }, "End", function () awful.spawn("pactl set-sink-mute @DEFAULT_SINK@ toggle") end),
962 awful.key(nil, "XF86AudioLowerVolume", function () awful.spawn("pactl set-sink-volume @DEFAULT_SINK@ -2%") end),
963 awful.key({ cmdkey }, "Next", function () awful.spawn("pactl set-sink-volume @DEFAULT_SINK@ -2%") end),
964 awful.key(nil, "XF86AudioRaiseVolume", function () awful.spawn("pactl set-sink-volume @DEFAULT_SINK@ +2%") end),
965 awful.key({ cmdkey }, "Prior", function () awful.spawn("pactl set-sink-volume @DEFAULT_SINK@ +2%") end),
966 awful.key(nil, "XF86AudioMicMute", function () awful.spawn("pactl set-source-mute @DEFAULT_SOURCE@ toggle") end),
967 awful.key({ cmdkey }, "Home", function () awful.spawn("pactl set-source-mute @DEFAULT_SOURCE@ toggle") end),
968 awful.key({ cmdkey }, "Insert", function () awful.spawn("pa_cycle_default source") end),
969 awful.key({ cmdkey }, "Delete", function () awful.spawn("pa_cycle_default sink") end),
970 awful.key(nil, "XF86MonBrightnessDown", function () awful.spawn("light -U 10") end),
971 awful.key(nil, "XF86MonBrightnessUp", function () awful.spawn("light -A 10") end),
972 awful.key(nil, "XF86Display", function () awful.spawn("autorandr --change --force"); initialise_to_autorandr_profile() end),
973 awful.key(nil, "XF86AudioStop", function () awful.spawn("autorandr --change --force"); initialise_to_autorandr_profile() end),
974 awful.key(nil, "XF86WLAN", function () awful.spawn("") end),
975 awful.key(nil, "XF86Tools", function () awful.spawn("") end),
976 awful.key(nil, "XF86Search", function () awful.spawn("") end),
977 awful.key(nil, "XF86LaunchA", function () awful.spawn("") end),
978 awful.key(nil, "XF86Explorer", function () awful.spawn("") end),
979 awful.key(nil, "XF86Calculator", function () awful.spawn(terminal .. " -e ipython3 --profile=calc") end),
980 awful.key(nil, "XF86Favorites", function () awful.spawn("systemctl suspend") end),
981
982 awful.key({ cmdkey }, "Multi_key", function () run_output_notify("flameshot gui", "Output") end),
983 awful.key({ cmdkey, "Shift" }, "Multi_key", function () run_output_notify("flameshot full --delay 2000 --clipboard", "Output") end),
984
985 awful.key({ cmdkey }, "Up", function () awful.spawn("pap prev") end),
986 awful.key({ cmdkey }, "Left", function () awful.spawn("pap seek -10") end),
987 awful.key({ cmdkey, "Shift" }, "Left", function () awful.spawn("pap seek -60") end),
988 awful.key({ cmdkey }, "Down", function () awful.spawn("pap next") end),
989 awful.key({ cmdkey }, "Right", function () awful.spawn("pap seek +10") end),
990 awful.key({ cmdkey, "Shift" }, "Right", function () awful.spawn("pap seek +60") end),
991 awful.key({ cmdkey }, "space", function () awful.spawn("pap pause") end),
992 awful.key({ cmdkey }, "\\", function () run_output_notify("pap info", "Track info") end),
993 awful.key({ cmdkey }, "]", function () run_output_notify("pap list", "Playlist") end)
994 )
995
996 function run_output_notify(cmd, title)
997     awful.spawn.easy_async(cmd, function(stdout, stderr, reason, exit_code)
998         if #stdout > 1 then
999             naughty.notify({
1000                 preset = naughty.config.presets.low,
1001                 title = title,
1002                 text = stdout})
1003             end
1004         end)
1005 end
1006
1007 -- Set keys
1008 root.keys(globalkeys)
1009 -- }}}
1010
1011 -- {{{ Rules
1012 -- Rules to apply to new clients (through the "manage" signal).
1013
1014 local function float_client_in_the_middle_with_margins(client, leftright, topbottom)
1015     local wa = client.screen.workarea
1016     if topbottom then
1017         client.y = wa.y + topbottom
1018         client.height = wa.height - 2*topbottom
1019     else
1020         client.y = wa.y + (wa.height - client.height)/2
1021     end
1022     if leftright then
1023         client.x = wa.x + leftright
1024         client.width = wa.width - 2*leftright
1025     else
1026         client.x = wa.x + (wa.width - client.width)/2
1027     end
1028 end
1029
1030 local function move_to_tag_by_name(s, tagname)
1031     return function(c)
1032         local t = awful.tag.find_by_name(s, tagname)
1033         if not t then
1034             error("No tag by the name of " .. tagname)
1035             return
1036         end
1037         c:move_to_tag(t)
1038     end
1039 end
1040
1041 local function move_to_tag_or_create_volatile(s, tagname)
1042     return function(c)
1043         local t = awful.tag.find_by_name(s, tagname)
1044         if t then
1045             c:move_to_tag(t)
1046         else
1047             th.move_to_new_tag(c, tagname, {}, true, true, true)
1048         end
1049     end
1050 end
1051
1052 awful.rules.rules = {
1053     -- All clients will match this rule.
1054     { rule = { },
1055       properties = { border_width = beautiful.border_width,
1056                      border_color = beautiful.border_normal,
1057                      focus = awful.client.focus.filter,
1058                      raise = true,
1059                      keys = clientkeys,
1060                      buttons = clientbuttons,
1061                      screen = awful.screen.preferred,
1062                      placement = awful.placement.no_overlap+awful.placement.no_offscreen,
1063                      floating = false,
1064                      maximized = false,
1065                  },
1066     },
1067     { rule = { type = "dialog" },
1068       properties = { floating = true,
1069                      ontop = true,
1070                      skip_taskbar = true,
1071                      urgent = true,
1072                      --new_tag = true,
1073                      --switchtotag = true,
1074                      placement = awful.placement.centered
1075                    }
1076     },
1077     { rule = { class = "URxvt" },
1078       properties = { size_hints_honor = false, }
1079     },
1080     { rule = { instance = "irc" },
1081       callback = move_to_tag_by_name(nil, "irc"),
1082     },
1083     { rule = { class = "scrcpy" },
1084       callback = move_to_tag_by_name(nil, "[]"),
1085     },
1086     { rule_any = { class = { "Firefox", "firefox" } },
1087       callback = move_to_tag_by_name(nil, "ffx"),
1088     },
1089     { rule = { class = "Chromium" },
1090       callback = move_to_tag_by_name(nil, "chr"),
1091     },
1092     { rule_any = { class = { "thunderbird", "Thunderbird" } },
1093       callback = move_to_tag_by_name(nil, "cal"),
1094     },
1095     { rule = { instance = "mutt" },
1096       properties = {
1097           new_tag = {
1098               name = "mutt",
1099               layout = awful.layout.suit.fair.horizontal,
1100               volatile = true
1101           },
1102           switchtotag = true,
1103       },
1104     },
1105     { rule_any = { class = { "zoom" } },
1106       callback = move_to_tag_or_create_volatile(nil, "Zoom"),
1107     },
1108     { rule_any = { class = { "Ssvnc.tcl" },
1109                    class = { "Ssvnc" },
1110                    name = { "SSL/SSH VNC Viewer.-" },
1111                  },
1112       callback = move_to_tag_or_create_volatile(nil, "SSVNC"),
1113     },
1114     { rule_any = { class = {
1115         "Gxmessage",
1116         "Pinentry"
1117     }},
1118       properties = { floating = true,
1119                      maximized = false,
1120                      focus = true,
1121                      placement = awful.placement.centered,
1122                    },
1123     },
1124     { rule_any = { instance = {
1125         "tridactyl-edit",
1126         "pdfshuffler",
1127         "vlc",
1128         "pavucontrol"
1129     }},
1130       properties = { floating = true,
1131                      maximized = false,
1132                      focus = true,
1133                      placement = awful.placement.centered,
1134                    },
1135     },
1136     { rule_any = { class = {
1137                         "Gimp",
1138                         "Inkscape",
1139                         "Pitivi",
1140                         "Audacity",
1141                         "Microsoft Teams - Preview",
1142                         "org.remmina.Remmina",
1143                     },
1144                     instance = {
1145                         "libreoffice",
1146                     }
1147                 },
1148       except_any = { type = { "dialog" } },
1149       properties = { new_tag = {
1150                         layout = layouts.maximised,
1151                         volatile = true,
1152                     },
1153                      --switchtotag = true,
1154                      focus = true,
1155                    },
1156                },
1157     { rule_any = { class = {
1158                         "Gscan2pdf",
1159                     },
1160                 },
1161       except_any = { type = { "dialog" } },
1162       properties = { new_tag = {
1163                         layout = layouts.default,
1164                         volatile = true,
1165                     },
1166                     floating = true,
1167                     maximized = false,
1168                     focus = true,
1169                     placement = awful.placement.centered,
1170                     switchtotag = true,
1171                     focus = true,
1172                    },
1173                },
1174 --XX--    { rule = { class = "Gscan2pdf" },
1175 --XX--               properties = {
1176 --XX--                   switchtotag = true
1177 --XX--               },
1178 --XX--               callback = move_to_tag(1, 5)
1179 --XX--           },
1180 --XX--    { rule = { name = "gscan2pdf .*" },
1181 --XX--               properties = {
1182 --XX--                   floating = false,
1183 --XX--               },
1184 --XX--           },
1185 --XX--    { rule = { class = "Thunar", type = "normal" },
1186 --XX--               properties = {
1187 --XX--                   floating = false,
1188 --XX--               },
1189 --XX--           },
1190 --XX--    { rule = { class = "Pinentry", instance = "pinentry" },
1191 --XX--               properties = {
1192 --XX--                   floating = true,
1193 --XX--               },
1194 --XX--           },
1195 --XX--    { rule = { class = "Gxmessage" },
1196 --XX--               properties = {
1197 --XX--                   floating = true,
1198 --XX--               },
1199 --XX--           },
1200 --XX--}
1201 }
1202 -- }}}
1203
1204 -- {{{ Signals
1205 -- Signal function to execute when a new client appears.
1206 client.connect_signal("manage", function (c)
1207     -- Set the windows at the slave,
1208     -- i.e. put it at the end of others instead of setting it master.
1209     -- if not awesome.startup then awful.client.setslave(c) end
1210     --if not awesome.startup then
1211     --    local t = awful.screen.focused().selected_tag
1212     --    if t.name == "xmutt" then
1213     --        awful.client.setslave(c)
1214     --    end
1215     --end
1216
1217     if awesome.startup and
1218       not c.size_hints.user_position
1219       and not c.size_hints.program_position then
1220         -- Prevent clients from being unreachable after screen count changes.
1221         awful.placement.no_offscreen(c)
1222     end
1223
1224     c.maximized_horizontal = false
1225     c.maximized_vertical = false
1226 end)
1227
1228 -- Add a titlebar if titlebars_enabled is set to true in the rules.
1229 client.connect_signal("request::titlebars", function(c)
1230     -- buttons for the titlebar
1231     local buttons = gears.table.join(
1232         awful.button({ }, 1, function()
1233             client.focus = c
1234             c:raise()
1235             awful.mouse.client.move(c)
1236         end),
1237         awful.button({ }, 3, function()
1238             client.focus = c
1239             c:raise()
1240             awful.mouse.client.resize(c)
1241         end)
1242     )
1243
1244     awful.titlebar(c) : setup {
1245         { -- Left
1246             awful.titlebar.widget.iconwidget(c),
1247             buttons = buttons,
1248             layout  = wibox.layout.fixed.horizontal
1249         },
1250         { -- Middle
1251             { -- Title
1252                 align  = "center",
1253                 widget = awful.titlebar.widget.titlewidget(c)
1254             },
1255             buttons = buttons,
1256             layout  = wibox.layout.flex.horizontal
1257         },
1258         { -- Right
1259             awful.titlebar.widget.floatingbutton (c),
1260             awful.titlebar.widget.maximizedbutton(c),
1261             awful.titlebar.widget.stickybutton   (c),
1262             awful.titlebar.widget.ontopbutton    (c),
1263             awful.titlebar.widget.closebutton    (c),
1264             layout = wibox.layout.fixed.horizontal()
1265         },
1266         layout = wibox.layout.align.horizontal
1267     }
1268 end)
1269
1270 -- Enable sloppy focus, so that focus follows mouse.
1271 client.connect_signal("mouse::enter", function(c)
1272     if awful.layout.get(c.screen) ~= awful.layout.suit.magnifier
1273         and awful.client.focus.filter(c) then
1274         client.focus = c
1275     end
1276 --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 
1277 --                    something like this
1278 end)
1279
1280 client.connect_signal("focus", function(c)
1281     c.border_color = beautiful.border_focus
1282 end)
1283 client.connect_signal("unfocus", function(c)
1284     c.border_color = beautiful.border_normal
1285 end)
1286
1287 awful.ewmh.add_activate_filter(function(c, context, hints)
1288     if context == "ewmh" then
1289         if (c.class == "Firefox-esr" or c.class == "Firefox") then
1290             return false
1291         end
1292     end
1293 end)
1294
1295 client.connect_signal("request::activate", function(c, context, hints)
1296     if gears.table.hasitem({
1297         "client.focus.byidx",
1298         "client.jumpto",
1299         "autofocus.check_focus",
1300         "rules",
1301         "ewmh",
1302     }, context) then
1303         gears.timer.delayed_call(function()
1304             -- we need a delayed call so that we execute *after layout changes
1305             if hints.raise and c == client.focus and client.focus:isvisible() then
1306                 move_mouse_to_area(client.focus)
1307             end
1308         end)
1309     end
1310 end)
1311
1312 -- vim:ft=lua:sw=4:sts=4:ts=4:et