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

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