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

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