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

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