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

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