#+title: AwesomeWM configuration
#+setupfile: headers
#+OPTIONS: auto-id:t
#+HTML_HEAD_EXTRA: <meta name="description" content="Phundrak's AwesomeWM config" />
#+HTML_HEAD_EXTRA: <meta property="og:title" content="Phundrak's AwesomeWM config" />
#+HTML_HEAD_EXTRA: <meta property="og:description" content="Description of the AwesomeWM config file of Phundrak" />
#+PROPERTY: header-args :noweb yes :tangle no :exports none
#+PROPERTY: header-args:lua :tangle ~/.config/awesome/rc.lua :exports code :noweb yes :mkdirp yes

* Introduction
:PROPERTIES:
:CUSTOM_ID: Introduction-4c41360e
:END:
From the Arch Wiki: awesome is a highly configurable, next generation framework
window manager for Xorg. It is very fast and extensible. It is primarily
targeted at power users, developers and any people dealing with every day
computing tasks and who want to have fine-grained control on its graphical
environment.

Personally, what really made me want to try Awesome is the fact its
configuration file is written with an actual programming language and not just a
configuration language like with i3, and by the fact it works with tags and not
workspaces which makes window management much more flexible.

This document was written in Emacs with Org-mode and is both the documentation
and source code of my configuration file which can be extracted to
~$HOME/.config/awesome/rc.lua~ through a call to ~org-babel-tangle~.

* Loading libraries
:PROPERTIES:
:CUSTOM_ID: Loading_libraries-4df76999
:END:
First of all, some initialization is needed, and this initialization is about
math randomness. So, let’s initialize the ~random~ method of the ~math~ library:
#+BEGIN_SRC lua
  math.randomseed(os.time())
#+END_SRC

In order to be able to load libraries properly, I first need to make sure
LuaRocks is installed, so I can also make sure the packages our configuration
depends on installed through it can be found. If LuaRocks is not installed, then
do nothing.
#+BEGIN_SRC lua
  pcall(require, "luarocks.loader")
#+END_SRC

Next, we’ll also load the following libraries
#+NAME: table-imported-libraries
| Library             | Import as     | What it is                |
|---------------------+---------------+---------------------------|
| gears               | gears         | Standard Awesome library  |
| awful               | awful         | Standard Awesome library  |
| wibox               | wibox         | Widget and layout library |
| beautiful           | beautiful     | Theme handling library    |
| naughty             | naughty       | Notification library      |
| menubar             | menubar       | Create menus              |
| awful.hotkeys_popup | hotkeys_popup | Help window for hotkeys   |

#+NAME: imported-libraries
#+BEGIN_SRC emacs-lisp :var libs=table-imported-libraries :cache yes
  (mapconcat (lambda (x) (format "local %s = require(\"%s\")"
                                 (cadr x)
                                 (car x)))
             libs
             "\n")
#+END_SRC

#+RESULTS[a66d7b66dbd4b4001dc3f649840b66761cc915b5]: imported-libraries
: local gears = require("gears")
: local awful = require("awful")
: local wibox = require("wibox")
: local beautiful = require("beautiful")
: local naughty = require("naughty")
: local menubar = require("menubar")
: local hotkeys_popup = require("awful.hotkeys_popup")

Here is the actual code in the config file:
#+BEGIN_SRC lua
<<imported-libraries()>>
#+END_SRC

I also want to be able to autofocus the first window when I go to another workspace, so let’s require that:
#+BEGIN_SRC lua
  require("awful.autofocus")
#+END_SRC

And finally, I want to be able to declare some shortcuts specific to some apps
thanks to the hotkeys help widget.
#+BEGIN_SRC lua
  require("awful.hotkeys_popup.keys")
#+END_SRC
By the way, let’s initialize the ~random~ method of the ~math~ library:
#+BEGIN_SRC lua
  math.randomseed(os.time())
#+END_SRC

* Error handling
:PROPERTIES:
:CUSTOM_ID: Error_handling-f6a6668f
:END:
This code checks if Awesome encountered an error during startup and fell back to
another config. This code will only ever execute for the fallback config.
#+BEGIN_SRC lua
  if awesome.startup_errors then
    naughty.notify({ preset = naughty.config.presets.critical,
                     title = "Oops, there were errors during startup!",
                     text = awesome.startup_errors })
  end
#+END_SRC

And this code handles runtime errors after startup thanks to signals.
#+BEGIN_SRC lua
  do
    local in_error = false
    awesome.connect_signal("debug::error", function (err)
                             -- Make sure we don't go into an endless error loop
                             if in_error then return end
                             in_error = true

                             naughty.notify({ preset = naughty.config.presets.critical,
                                              title = "Oops, an error happened!",
                                              text = tostring(err) })
                             in_error = false
    end)
  end
#+END_SRC

* Variable definitions
:PROPERTIES:
:CUSTOM_ID: Variable_definitions-06b2bcbf
:END:
** Themes
:PROPERTIES:
:CUSTOM_ID: Variable_definitions-Themes-591886b4
:END:
With Awesome, it is possible to load or write custom themes in order to give
Awesome a special look that fits the user. I am currently using a custom theme
that is not yet included in my dotfiles. I will add it later, along with the
images used for the theme.
#+BEGIN_SRC lua
  beautiful.init("/home/phundrak/.config/awesome/nord/theme.lua")
#+END_SRC

** Default terminal and text editor
:PROPERTIES:
:CUSTOM_ID: Variable_definitions-Default_terminal_and_text_editor-44b84e20
:END:
The two following variables are set so that I don’t need to go over my whole
config file in order to modify which terminal or text editor I use, not that I
do it often though.
#+BEGIN_SRC lua
  terminal = "kitty"
  editor = os.getenv("EDITOR") or "emacsclient -c"
#+END_SRC

** Keys
:PROPERTIES:
:CUSTOM_ID: Variable_definitions-Keys-b8def4ac
:END:
The following declares the default Modkey. Usually, ~Mod4~ is the Super key,
situated between the Ctrl key and the Alt key with a logo (usually Windows’).
Another usual value for this is ~Mod1~, which is the Alt key, but it has greater
chances of interfering with other software. I also defined some other obvious
variables in order to make my code cleaner later on.
#+BEGIN_SRC lua
  modkey = "Mod4"
  shift = "Shift"
  control = "Control"
  meta = "Mod1"
  alt = "Mod1" -- Just in case
#+END_SRC

* Custom functions
:PROPERTIES:
:CUSTOM_ID: Custom_functions-ed54dbe2
:END:
** Wallpaper-related functions
:PROPERTIES:
:CUSTOM_ID: Custom_functions-Wallpaper-related_functions-5912f7dd
:END:
*** Set a random wallpaper
:PROPERTIES:
:CUSTOM_ID: Custom_functions-Wallpaper-related_functions-Set_a_random_wallpaper-104bbeec
:END:
This function sets a random wallpaper from the directory
=~/Pictures/Wallpapers=, see [[file:bin.org::#pape-update-bdecbadf][pape-update]] in my custom scripts.
#+BEGIN_SRC lua
  local function set_random_pape()
    awful.spawn.with_shell("pape-update")
    naughty.notify({ preset = naughty.config.presets.normal,
                     title = "Wallpaper change",
                     text = "Done!"})
  end
#+END_SRC

*** Restore previous wallpaper
:PROPERTIES:
:CUSTOM_ID: Custom_functions-Wallpaper-related_functions-Restore_previous_wallpaper-8b5bc08c
:END:
I also wrote the following function that will restore the previously set
wallpaper:
#+BEGIN_SRC lua
  local function set_wallpaper(_)
    awful.spawn.with_shell("nitrogen --restore")
  end
#+END_SRC

** Layout manipulation
:PROPERTIES:
:CUSTOM_ID: Custom_functions-Layout_manipulation-6bc7db06
:END:
The following function is used by a shortcut described below in
[[#Keybindings-Clients-f9f96d60]].
#+BEGIN_SRC lua
  local function client_go_back()
    awful.client.focus.history.previous()
    if client.focus then
      client.focus:raise()
    end
  end
#+END_SRC

** Clients manipulation
:PROPERTIES:
:CUSTOM_ID: Custom_functions-Clients_manipulation-7e958fed
:END:
#+BEGIN_SRC lua
  local function restore_minimized_clients()
    local c = awful.client.restore()
    -- Focus restored client
    if c then
      c:emit_signal(
        "request::activate", "key.unminimize", {raise = true}
      )
    end
  end
#+END_SRC

#+BEGIN_SRC lua
 local function toggle_fullscreen_client(c)
   c.fullscreen = not c.fullscreen
   c:raise()
 end
#+END_SRC

#+BEGIN_SRC lua
  local function toggle_maximized(c)
    c.maximized = not c.maximized
    c:raise()
  end
#+END_SRC

#+BEGIN_SRC lua
 local function toggle_vertical_maximized(c)
   c.maximized_vertical = not c.maximized_vertical
   c:raise()
 end
#+END_SRC

#+BEGIN_SRC lua
 local function toggle_horizontal_maximized(c)
   c.maximized_horizontal = not c.maximized_horizontal
   c:raise()
 end
#+END_SRC

** Tag manipulation
:PROPERTIES:
:CUSTOM_ID: Custom_functions-Tag_manipulation-5fc67669
:END:
#+BEGIN_SRC lua
  local function view_tag_n(i)
    local screen = awful.screen.focused()
    local tag = screen.tags[i]
    if tag then
      tag:view_only()
    end
  end
#+END_SRC

#+BEGIN_SRC lua
  local function toggle_tag_n(i)
    local screen = awful.screen.focused()
    local tag = screen.tags[i]
    if tag then
      awful.tag.viewtoggle(tag)
    end
  end
#+END_SRC

#+BEGIN_SRC lua
  local function move_focused_to_tag_n(i)
    if client.focus then
      local tag = client.focus.screen.tags[i]
      if tag then
        client.focus:move_to_tag(tag)
      end
    end
  end
#+END_SRC

#+BEGIN_SRC lua
  local function toggle_focused_client_to_tag_n(i)
    if client.focus then
      local tag = client.focus.screen.tags[i]
      if tag then
        client.focus:toggle_tag(tag)
      end
    end
  end
#+END_SRC

** Awesome prompt
:PROPERTIES:
:CUSTOM_ID: Custom_functions-Awesome_prompt-de4fde50
:END:
#+BEGIN_SRC lua
  local function invoke_lua_execute_prompt()
    awful.prompt.run {
      prompt       = "Run Lua code: ",
      textbox      = awful.screen.focused().promptbox.widget,
      exe_callback = awful.util.eval,
      history_path = awful.util.get_cache_dir() .. "/history_eval"
    }
  end
#+END_SRC

* Layouts
:PROPERTIES:
:CUSTOM_ID: Layouts-be55a7fd
:END:
The following is a list of available windows layouts. I only enable some of
them, and their order in the table is their order in Awesome.
#+NAME: table-layouts
| Layout          | Enabled? |
|-----------------+----------|
| magnifier       | yes      |
| tile.left       | yes      |
| tile            | yes      |
| tile.bottom     | yes      |
| tile.top        | yes      |
| max             | yes      |
| max.fullscreen  | yes      |
| floating        | yes      |
| fair            | yes      |
| fair.horizontal | yes      |
| spiral          | yes      |
| spiral.dwindle  | yes      |
| corner.nw       | no       |
| corner.ne       | no       |
| corner.sw       | no       |
| corner.se       | no       |

#+NAME: list-layouts
#+BEGIN_SRC emacs-lisp :var layouts=table-layouts :cache yes
  (mapconcat (lambda (x) (if (string= (cadr x) "yes")
                             (format "awful.layout.suit.%s,\n" (car x))))
             layouts
             "")
#+END_SRC

#+RESULTS[0bd0312ff10526c8b24199453ff235cf71425fb5]: list-layouts
#+begin_example
awful.layout.suit.magnifier,
awful.layout.suit.tile,
awful.layout.suit.tile.left,
awful.layout.suit.tile.bottom,
awful.layout.suit.tile.top,
awful.layout.suit.max,
awful.layout.suit.max.fullscreen,
awful.layout.suit.floating,
awful.layout.suit.fair,
awful.layout.suit.fair.horizontal,
awful.layout.suit.spiral,
awful.layout.suit.spiral.dwindle,
#+end_example

Here is the code that activates these layouts:
#+BEGIN_SRC lua
  awful.layout.layouts = {
    <<list-layouts()>>
  }
#+END_SRC

* Top bar
:PROPERTIES:
:CUSTOM_ID: Top_bar-d3117294
:END:
The top bar in Awesome is declared thanks to a ~wibar~ widget fro the ~awful~
library. It is comprised of several buttons and widgets that will be declared
below.

** Menus
:PROPERTIES:
:CUSTOM_ID: Top_bar-Menus-cf468ca8
:END:
#+NAME: make-menu
#+BEGIN_SRC emacs-lisp :var menu=table-awesome-menu
  (mapconcat (lambda (x)
               (format "{ \"%s\", %s }"
                       (car x)
                       (mapconcat (lambda (y) (format "%s" y))
                                  (cdr x)
                                  ",")))
             menu
             ",\n")
#+END_SRC

#+RESULTS: make-menu
: { "hotkeys", function() hotkeys_popup.show_help(nil, awful.screen.focused()) end }
: { "edit config", editor .. " " .. awesome.conffile }
: { "restart", awesome.restart }
: { "quit", function() awesome.quit() end }

It is possible to create actual menus in Awesome, including the one available at
the top-left corner of the screen. First, let’s declare a menu related to
Awesome:
#+NAME: table-awesome-menu
| Name        | Command                                                             |
|-------------+---------------------------------------------------------------------|
| hotkeys     | function() hotkeys_popup.show_help(nil, awful.screen.focused()) end |
| edit config | editor .. " " .. awesome.conffile                                   |
| restart     | awesome.restart                                                     |
| quit        | function() awesome.quit() end                                       |

And here is the actual code:
#+BEGIN_SRC lua
  awesomewm_menu = {
    <<make-menu(menu=table-awesome-menu)>>
  }
#+END_SRC

Next, let’s create the main menu that will be used on ~S-w~ and at the top left
of the window:
#+NAME: table-main-menu
| Name          | Command        | Icon                   |
|---------------+----------------+------------------------|
| awesome       | awesomewm_menu | beautiful.awesome_icon |
| open terminal | terminal       | nil                    |

Here is the actual code:
#+BEGIN_SRC lua
  mainmenu = awful.menu({ items = {
                            <<make-menu(menu=table-main-menu)>>
                       }})
#+END_SRC

For now it only has two entries: the Awesome menu and opening a terminal, I will
add some more later probably. Let’s specify it as being our main launcher:
#+BEGIN_SRC lua
  launcher = awful.widget.launcher({ image = beautiful.awesome_icon,
                                     menu = mainmenu })
#+END_SRC

Finally, let’s declare the menubar’s terminal for applications that require it.
#+BEGIN_SRC lua
  menubar.utils.terminal = terminal
#+END_SRC

** Widgets
:PROPERTIES:
:CUSTOM_ID: Top_bar-Widgets-0b255378
:END:
Let’s declare the keyboard map indicator and switcher for the top bar:
#+BEGIN_SRC lua
  keyboardlayout = awful.widget.keyboardlayout()
#+END_SRC

Let’s also create a clock widget:
#+BEGIN_SRC lua
  textclock = wibox.widget.textclock()
#+END_SRC

** Tag list
:PROPERTIES:
:CUSTOM_ID: Top_bar-Tag_list-d43dbb62
:END:
In order to create the taglist (an equivalent to workspaces, but better), we
need to create first a local variable that will hold the widget. It will be
declared as you can see below:
#+BEGIN_SRC lua :tangle no
  local tasklist_buttons = gears.table.join(
    -- configuration goes here
  )
#+END_SRC

~gears.table.join()~ joins several tables together, as described [[https://awesomewm.org/doc/api/libraries/gears.table.html][here]], which
will be useful since all its arguments will be tables generated by the
~awful.button~ method which will be useful in order to manage what clicks on the
tags should do. First, let’s manage left clicks.

Left clicks in general are dedicated to tag visibility. A simple left click on a
tag should switch this tag as the only visible tag, no matter how many of them
were visible beforehand.
#+NAME: tag-simple-left-click
#+BEGIN_SRC lua :tangle no
  awful.button({ }, 1, function(t) t:view_only() end)
#+END_SRC

However, left clicks combined with the modkey will add the clicked tag to the
list of visible tags, which allows the user to see windows from several tags at
once.
#+NAME: tag-mod-left-click
#+BEGIN_SRC lua :tangle no
  awful.button({ modkey }, 1, awful.tag.viewtoggle)
#+END_SRC

Right clicks are dedicated to window tagging. A simple right click will untag
the currently focused window and tag it again with the clicked tag, moving it
effectively from one tag to another.
#+NAME: tag-simple-right-click
#+BEGIN_SRC lua :tangle no
  awful.button({ }, 3, function(t)
      if client.focus then
        client.focus:move_to_tag(t)
      end
  end)
#+END_SRC

However, a right click combined with the modkey will add the clicked tag to the
currently focused window, making it visible to both tags.
#+NAME: tag-mod-right-click
#+BEGIN_SRC lua :tangle no
  awful.button({ modkey }, 3, function(t)
      if client.focus then
        client.focus:toggle_tag(t)
      end
  end)
#+END_SRC

The scroll wheel is treated as clicks just as any right or left clicks and can
be interpreted by Awesome. They can prove useful when it comes to tags. If a
scroll up is detected over tags, then Awesome will display the previous tag.
#+NAME: tag-simple-scroll-up
#+BEGIN_SRC lua :tangle no
  awful.button({ }, 4, function(t) awful.tag.viewprev(t.screen) end)
#+END_SRC

Otherwise, if a scroll down is detected, the next tag will be displayed.
#+NAME: tag-simple-scroll-down
#+BEGIN_SRC lua :tangle no
  awful.button({ }, 5, function(t) awful.tag.viewnext(t.screen) end)
#+END_SRC

So, here’s the actual configuration code for the taglist:
#+BEGIN_SRC lua
  local taglist_buttons = gears.table.join(
    <<tag-simple-left-click>>,
    <<tag-mod-left-click>>,
    <<tag-simple-right-click>>,
    <<tag-mod-right-click>>,
    <<tag-simple-scroll-up>>,
    <<tag-simple-scroll-down>>
  )
#+END_SRC

** Tasks list
:PROPERTIES:
:CUSTOM_ID: Top_bar-Tasks_list-fb7c9b20
:END:
Similarly to the tag list, the task list can display some special behavior
depending on the clicks it receives. These clicks are set like so:
#+BEGIN_SRC lua :tangle no
  local tasklist_buttons = gears.table.join(
    -- List of clicks
  )
#+END_SRC

A left click on a task in the taskbar will simply focus and raise the window
linked to it if it is not focused. Otherwise, if the window is focused, the
window will be minimized.
#+NAME: task-simple-left-click
#+BEGIN_SRC lua :tangle no
  awful.button({ }, 1, function (c)
      if c == client.focus then
        c.minimized = true
      else
        c:emit_signal(
          "request::activate",
          "tasklist",
          {raise = true}
        )
      end
  end)
#+END_SRC

If the right click is detected, then a list of all the opened clients is invoked
so we can switch to another (and if needed switch visible tag). The width of
this list will be 250px.
#+NAME: task-simple-right-click
#+BEGIN_SRC lua :tangle no
  awful.button({ }, 3, function()
        awful.menu.client_list({ theme = { width = 250 } })
  end)
#+END_SRC

If a scroll up is detected, then let’s select the previous client in the
tasklist.
#+NAME: task-simple-scroll-up
#+BEGIN_SRC lua :tangle no
  awful.button({ }, 4, function ()
        awful.client.focus.byidx(1)
  end)
#+END_SRC

If a scroll down is detected, then let’s select the next client in the tasklist.
#+NAME: task-simple-scroll-down
#+BEGIN_SRC lua :tangle no
  awful.button({ }, 5, function ()
        awful.client.focus.byidx(-1)
  end)
#+END_SRC

So, here’s the actual code for the tasklist:
#+BEGIN_SRC lua
  local tasklist_buttons = gears.table.join(
    <<task-simple-left-click>>,
    <<task-simple-right-click>>,
    <<task-simple-scroll-up>>,
    <<task-simple-scroll-down>>
  )
#+END_SRC

* Theme and display
:PROPERTIES:
:CUSTOM_ID: Theme_and_display-6f94bad4
:END:
** Screen update
:PROPERTIES:
:CUSTOM_ID: Theme_and_display-Screen_update-e162a27a
:END:
When a screen’s geometry changes (e.g. when a different resolution is applied),
the signal ~property::geometry~ is sent. When this is the case, the wallpaper
should be redisplayed since it won’t necessarily fit the new geometry of the
screen. And remember, I have a [[#Custom_functions-Wallpaper-related_functions-Restore_previous_wallpaper-8b5bc08c][function that does exactly that]]! Let’s connect
this function to the geometry change signal:
#+BEGIN_SRC lua
  screen.connect_signal("property::geometry", set_wallpaper)
#+END_SRC

If a new screen gets connected, it will need to get a new wallpaper. A lot needs
to be done, and all the following lines of code will be inside a block like
this:
#+BEGIN_SRC lua :tangle no
  awful.screen.connect_for_each_screen(function(s)
      -- Code to be executed goes here
  end)
#+END_SRC

So, due the code block above, if you see any reference to ~s~ in the code blocks
below, it will refer to the screen being set up by the function.

First, let’s set its wallpaper:
#+NAME: screen-set-pape
#+BEGIN_SRC lua :tangle no
  set_wallpaper()
#+END_SRC

Next, let’s build a list of tags for the screen. *Be aware that each screen has
its own tag table!* The default layout will be the first refered to in the
layouts list described above.
#+NAME: screen-taglist
#+BEGIN_SRC lua :tangle no
  awful.tag({ "1", "2", "3", "4", "5", "6", "7", "8", "9", "0" }, s, awful.layout.layouts[1])
#+END_SRC

Next, let’s create the taglist widget. It will use the ~taglist_buttons~
[[#Top_bar-Tag_list-d43dbb62][declared above]] in order to handle clicks on tags, and due to the filter, all
tags will be displayed in the tagbar ([[https://awesomewm.org/apidoc/widgets/awful.widget.taglist.html#List_filters][more about tag filters]]).
#+NAME: screen-taglist-widget
#+BEGIN_SRC lua :tangle no
  s.taglist = awful.widget.taglist {
    screen  = s,
    filter  = awful.widget.taglist.filter.all,
    buttons = taglist_buttons
  }
#+END_SRC

A tasklist widget will also get created thanks with the ~tasklist_button~
[[#Top_bar-Tag_list-d43dbb62][declared above]] that will handle clicks on tasks. Contrarily to the taglist
widget above, the tasklist will only display the screen’s current tags thanks to
its filter.
#+NAME: screen-tasklist-widget
#+BEGIN_SRC lua :tangle no
  s.tasklist = awful.widget.tasklist {
    screen  = s,
    filter  = awful.widget.tasklist.filter.currenttags,
    buttons = tasklist_buttons
  }
#+END_SRC

A promptbox will also be created for the screen:
#+NAME: screen-promptbox
#+BEGIN_SRC lua :tangle no
  s.promptbox = awful.widget.prompt()
#+END_SRC

Then, Let’s create an imagebox widget in which will be contained an icon
indicating which layout is being used. We need one per screen. We will also make
it clickable: if there is a left click or a scroll up detected above it, the
next layout will be loaded; otherwise if a right click or a scroll down is
detected, the previous layout will be loaded.
#+NAME: screen-layout-indicator
#+BEGIN_SRC lua :tangle no
  s.layoutbox = awful.widget.layoutbox(s)
  s.layoutbox:buttons(gears.table.join(
                        awful.button({ }, 1, function () awful.layout.inc( 1) end),
                        awful.button({ }, 3, function () awful.layout.inc(-1) end),
                        awful.button({ }, 4, function () awful.layout.inc( 1) end),
                        awful.button({ }, 5, function () awful.layout.inc(-1) end)))
#+END_SRC

Now it is time to create the widget, a ~wibox~ that will contain our bar.
#+NAME: screen-wibox-widget
#+BEGIN_SRC lua :tangle no
  s.wibox = awful.wibar({ position = "top", screen = s })
#+END_SRC

Finally, let’s set up our bar. Since it is a horizontal bar, its layout will be
horizontal too. Our launcher, taglist and promptbox will be part of the left
widgets, while the tasklist will be at the center, and the keyboard indicator,
the system tray, the clock and the layout indicator will be on the right.
#+NAME: screen-wibox-setup
#+BEGIN_SRC lua :tangle no
  s.wibox:setup {
    layout = wibox.layout.align.horizontal,
    { -- Left widgets
      layout = wibox.layout.fixed.horizontal,
      launcher,
      s.taglist,
      s.promptbox,
    },
    s.tasklist, -- Middle widget
    { -- Right widgets
      layout = wibox.layout.fixed.horizontal,
      keyboardlayout,
      wibox.widget.systray(),
      textclock,
      s.layoutbox,
    },
  }
#+END_SRC

In the end, our code looks like this:
#+BEGIN_SRC lua
  awful.screen.connect_for_each_screen(function(s)
        <<screen-set-pape>>
        <<screen-taglist>>
        <<screen-taglist-widget>>
        <<screen-tasklist-widget>>
        <<screen-promptbox>>
        <<screen-layout-indicator>>
        <<screen-wibox-widget>>
        <<screen-wibox-setup>>
  end)
#+END_SRC

* Mouse bindings
:PROPERTIES:
:CUSTOM_ID: Mouse_bindings-eb4a69a8
:END:
It is possible with Awesome to bind some shortcuts to mouse events when the
mouse is above Awesome itself (not above some client). Only one is set: the
right click opens the Awesome menu.
#+BEGIN_SRC lua
  root.buttons(gears.table.join(
                 awful.button({}, 3, function() mainmenu:toggle() end)
  ))
#+END_SRC

I will also set three mouse bindings for when the mouse is above a client:
- A simple click on a client will focus and raise it.
- A click on a client combined with a modkey press will allow the user to move a
  client after focusing it and making it floating.
- A middle click on a client combined with a modkey press will toggle the
  floating status of the client.
- A right click combined with the modkey will allow the user to resize a after
  focusing it and making it a floating client.

#+BEGIN_SRC lua
  clientbuttons = gears.table.join(
    awful.button({ }, 1, function (c)
        c:emit_signal("request::activate", "mouse_click", {raise = true})
    end),
    awful.button({ modkey }, 1, function (c)
        c:emit_signal("request::activate", "mouse_click", {raise = true})
        c.floating = true
        awful.mouse.client.move(c)
    end),
    awful.button({ modkey }, 2, function (c)
          awful.client.floating.toggle(c)
    end),
    awful.button({ modkey }, 3, function (c)
        c:emit_signal("request::activate", "mouse_click", {raise = true})
        c.floating = true
        awful.mouse.client.resize(c)
    end)
  )
#+END_SRC

* Keybindings
:PROPERTIES:
:CUSTOM_ID: Keybindings-a4e415b3
:END:
Keybindings allow the user to execute some Lua code all across Awesome. They all
bear at least a list of modifier keys, the actual key to be pressed, the action
they keybinding should yield, a description, and a group. The latter two will be
useful for the keybindings help window which will display them all, sorted by
group and with the description displayed next to the keybinding itself.

Here are some keybindings related to Awesome itself. Most of them will be
described in tables, but due to some limitations from Org-mode (the Emacs mode
used to write this document and generate my Awesome configuration), a few of
them will be directly written as Lua code.

Here is a description of the tables displayed below:
- Key :: key which toggles the shortcut
- Modifiers :: modifier keys that are required to toggle the shortcut
- Lambda? :: whether or not the ~Action~ should be nested in a lambda function.
  Possible values are:
  - ~no~ :: The value is a Lua function to be executed as is
  - ~yes~ :: The value is to be inserted into a lambda
  - ~spawn~ :: The value is to be inserted in an ~awful.spawn~ call in a lambda
  - ~shell~ :: The value is to be inserted in an ~awful.spawn.with_shell~ call
    in a lambda
- Action :: code to be executed by the shortcut
- What it does :: short description of the shortcut’s action
- Group :: group in which the shortcut will appear in Awesome’s help window
- Clientkey? :: whether this should be a global shortcut or a shortcut only
  aimed at clients (value is ~yes~ or ~no~)

#+NAME: gen-sc-text
#+BEGIN_SRC emacs-lisp
  (lambda (x)
    (format "awful.key({%s},\"%s\",%s,\n\t{description=\"%s\",group=\"%s\"})"
            (nth 1 x) (nth 0 x)
            (cond
             ((string= "yes" (nth 2 x))
              (format "function(%s) %s end"
                      (if (string= (nth 6 x) "yes") "c" "")
                      (nth 3 x)))
             ((string= "shell" (nth 2 x))
              (format "function(%s) awful.spawn.with_shell(\"%s\") end"
                      (if (string= (nth 6 x) "yes") "c" "")
                      (nth 3 x)))
             ((string= "terminal" (nth 2 x))
              (format "function(%s) awful.spawn(terminal..\" -e %s\") end"
                      (if (string= (nth 6 x) "yes") "c" "")
                      (nth 3 x)))
             ((string= "spawn" (nth 2 x))
              (format "function(%s) awful.spawn(\"%s\") end"
                      (if (string= (nth 6 x) "yes") "c" "")
                      (nth 3 x)))
             (t (nth 3 x)))
            (nth 4 x) (nth 5 x)))
#+END_SRC

#+NAME: gen-sc-glob
#+BEGIN_SRC emacs-lisp :var table=sc-client
  (mapconcat
   <<gen-sc-text>>
   (seq-filter (lambda (x)
                 (or (not (nth 6 x))
                     (string= "no" (nth 6 x))))
               table)
   ",\n")
#+END_SRC

#+NAME: gen-sc-client
#+BEGIN_SRC emacs-lisp :var table=sc-client :cache yes
  (mapconcat
   <<gen-sc-text>>
   (seq-filter (lambda (x)
                 (string= "yes" (nth 6 x)))
               table)
   ",\n")
#+END_SRC

#+RESULTS[5a0293b89fe106cb2b71ba10d9548b29aa2f9543]: gen-sc-client
#+begin_example
awful.key({modkey},"f",function(c) toggle_fullscreen_client(c) end,
  {description="toggle fullscreen",group="client"}),
awful.key({modkey},"m",function(c) toggle_maximized(c) end,
  {description="toggle maximized",group="client"}),
awful.key({modkey, control},"m",function(c) toggle_vertical_maximized(c) end,
  {description="toggle vertically maximized",group="client"}),
awful.key({modkey, shift},"m",function(c) toggle_horizontal_maximized(c) end,
  {description="toggle horizontally maximized",group="client"}),
awful.key({modkey, shift},"n",function(c) c.minimized = true end,
  {description="minimize",group="client"}),
awful.key({modkey},"o",function(c) c:move_to_screen() end,
  {description="move to screen",group="client"}),
awful.key({modkey},"q",function(c) c:kill() end,
  {description="close client",group="client"}),
awful.key({modkey},"v",function(c) c.ontop = not c.ontop end,
  {description="toggle keep on top",group="client"}),
awful.key({modkey, control},"space",awful.client.floating.toggle,
  {description="toggle floating",group="client"}),
awful.key({modkey, control},"Return",function(c) c:swap(awful.client.getmaster()) end,
  {description="move to master",group="client"})
#+end_example

#+NAME: sc-tag-num-gen
#+BEGIN_SRC emacs-lisp :var input=sc-tag-num :results drawer
  (let (result)
    (dotimes (i 10 result)
      (let* ((j (+ 1 i)))
        (setq result
              (cons
               (mapconcat
                (lambda (line)
                  (format
                   "awful.key({%s},\"#%d\",function() %s%d) end,
  \t{description=\"%s%d\",group=\"%s\"})"
                   (nth 1 line) (+ j 9) (nth 2 line) j
                   (nth 3 line) j (nth 4 line)))
                input
                ",\n") result))))
    (mapconcat (lambda (x) x)
               result
               ",\n\n"))
#+END_SRC

Most of these keybindings are available at root-level of Awesome and will be
declared in the ~globalkeys~ variable, which will be added then to ~root.keys~
(see [[https://awesomewm.org/doc/api/libraries/root.html#keys]]).
#+BEGIN_SRC lua :cache yes
  globalkeys = gears.table.join(
    -- Awesome
    <<gen-sc-glob(sc-awesome)>>,
    -- App
    <<gen-sc-glob(sc-app)>>,
    <<gen-sc-glob(sc-app-internet)>>,
    <<gen-sc-glob(sc-app-screenshot)>>,
    <<gen-sc-glob(sc-app-emacs)>>,
    <<gen-sc-glob(sc-app-rofi)>>,
    -- Client
    <<gen-sc-glob(sc-client)>>,
    -- Layout
    <<gen-sc-glob(sc-layout)>>,
    -- Media
    <<gen-sc-glob(sc-media)>>,
    -- Screen
    <<gen-sc-glob(sc-screen)>>,
    -- Tags
    <<gen-sc-glob(sc-tag)>>,
    -- Misc
    <<gen-sc-glob(sc-misc)>>,
    <<sc-tag-num-gen()>>
  )
  root.keys(globalkeys)

  clientkeys = gears.table.join(
    -- Client
    <<gen-sc-client(sc-client)>>
  )
#+END_SRC

** Applications
:PROPERTIES:
:CUSTOM_ID: Keybindings-Applications-8321c6c9
:END:
#+NAME: sc-app
| Key    | Modifiers | Lambda? | Action                | What it does      | Group |
|--------+-----------+---------+-----------------------+-------------------+-------|
| Return | modkey    | yes     | awful.spawn(terminal) | open a terminal   | app   |
| n      | modkey    | spawn   | nemo                  | open file manager | app   |
| g      | modkey    | spawn   | gimp                  | open GIMP         | app   |

*** Internet apps
:PROPERTIES:
:CUSTOM_ID: Keybindings-Applications-Internet_apps-87e80705
:END:
#+NAME: sc-app-internet
| Key | Modifiers      | Lambda? | Action                            | What it does       | Group    |
|-----+----------------+---------+-----------------------------------+--------------------+----------|
| b   | modkey         | yes     | awful.spawn(os.getenv("BROWSER")) | invoke web browser | internet |
| d   | control, shift | spawn   | discord-canary                    | launch Discord     | internet |

*** Screenshots
:PROPERTIES:
:CUSTOM_ID: Keybindings-Applications-Screenshots-fa63a5a5
:END:
#+NAME: sc-app-screenshot
| Key   | Modifiers | Lambda? | Action     | What it does                | Group      |
|-------+-----------+---------+------------+-----------------------------+------------|
| Print |           | spawn   | scrot      | Screenshot                  | screenshot |
| Print | control   | spawn   | scrot -s   | Screenshot (area selection) | screenshot |
| Print | shift     | spawn   | scrot -d 3 | Screenshot (3s delay)       | screenshot |

*** Emacs
:PROPERTIES:
:CUSTOM_ID: Keybindings-Applications-Emacs-95f8f6a4
:END:
#+NAME: sc-app-emacs
| Key | Modifiers     | Lambda? | Action                        | What it does             | Group |
|-----+---------------+---------+-------------------------------+--------------------------+-------|
| e   | modkey        | spawn   | emacsclient -c -n             | invoke Emacs             | emacs |
| e   | modkey, shift | spawn   | emacsclient -c -n -e '(mu4e)' | invoke Emacs mail client | emacs |

*** Rofi
:PROPERTIES:
:CUSTOM_ID: Keybindings-Applications-Rofi-ca998c87
:END:
#+NAME: sc-app-rofi
| Key | Modifiers              | Lambda? | Action                                   | What it does                    | Group |
|-----+------------------------+---------+------------------------------------------+---------------------------------+-------|
| a   | modkey                 | shell   | awiki                                    | find and open an ArchWiki page  | rofi  |
| d   | modkey                 | spawn   | rofi -combi-modi drun,window -show combi | rofi for drun and windows       | rofi  |
| d   | modkey, meta           | spawn   | rofi -show ssh                           | rofi for SSH                    | rofi  |
| p   | modkey, shift          | shell   | rofi-pass -t                             | types password from ~pass~      | rofi  |
| p   | modkey, control, shift | shell   | rofi-pass                                | copy password from ~pass~       | rofi  |
| e   | modkey, meta           | shell   | rofi-emoji                               | select and copy emoji from list | rofi  |
| m   | modkey, meta           | shell   | rofi-mount                               | volume mounting helper          | rofi  |
| u   | modkey, meta           | shell   | rofi-umount                              | volume unmounting helper        | rofi  |
| w   | modkey, control        | shell   | wacom-setup                              | set up my wacom tablet          | rofi  |
| y   | modkey                 | shell   | ytplay                                   | play web video in mpv           | rofi  |
| y   | modkey, shift          | shell   | rofi-ytdl                                | download video from web         | rofi  |

** Awesome
:PROPERTIES:
:CUSTOM_ID: Keybindings-Awesome-7b691e10
:END:
Here will be declared some shortcuts directly related to Awesome itself.
#+NAME: sc-awesome
| Key | Modifiers              | Lambda? | Action                    | What it does            | Group   |
|-----+------------------------+---------+---------------------------+-------------------------+---------|
| h   | modkey                 | no      | hotkeys_popup.show_help   | show help               | awesome |
| h   | modkey, shift          | yes     | mainmenu:show()           | show main menu          | awesome |
| l   | modkey                 | spawn   | plock                     | lock screen             | awesome |
| q   | modkey, shift          | no      | awesome.quit              | quit awesome            | awesome |
| r   | modkey, shift, control | no      | awesome.restart           | reload awesome          | awesome |
| w   | modkey                 | no      | set_random_pape           | set random wallpaper    | awesome |
| w   | modkey, shift          | spawn   | select-pape               | set wallpaper           | awesome |
| x   | modkey                 | no      | invoke_lua_execute_prompt | lua execute prompt      | awesome |
| F4  | modkey, control        | spawn   | systemctl hibernate       | hibernate computer      | awesome |
| F4  | modkey, shift          | spawn   | systemctl suspend         | suspend to RAM computer | awesome |
| F4  | modkey, shift, control | spawn   | poweroff                  | power off computer      | awesome |

** Clients
:PROPERTIES:
:CUSTOM_ID: Keybindings-Clients-f9f96d60
:END:
These shortcuts are related to clients (aka windows) management.
#+NAME: sc-client
| Key | Modifiers              | Lambda? | Action                           | What it does                  | Group  | Clientkey? |
|-----+------------------------+---------+----------------------------------+-------------------------------+--------+------------|
| c   | modkey, meta           | yes     | awful.placement.centered(c)      | center client                 | client | yes        |
| f   | modkey                 | yes     | toggle_fullscreen_client(c)      | toggle fullscreen             | client | yes        |
| m   | modkey                 | yes     | toggle_maximized(c)              | toggle maximized              | client | yes        |
| m   | modkey, shift          | yes     | toggle_horizontal_maximized(c)   | toggle horizontally maximized | client | yes        |
| m   | modkey, control        | yes     | toggle_vertical_maximized(c)     | toggle vertically maximized   | client | yes        |
| n   | modkey, shift          | yes     | c.minimized = true               | minimize                      | client | yes        |
| n   | modkey, control        | no      | restore_minimized_clients        | restore minimized             | client | no         |
| o   | modkey, shift          | yes     | c:move_to_screen()               | move to screen                | client | yes        |
| q   | modkey                 | yes     | c:kill()                         | close client                  | client | yes        |
| s   | modkey                 | yes     | awful.client.focus.byidx(-1)     | focus previous client         | client | no         |
| t   | modkey                 | yes     | awful.client.focus.byidx(1)      | focus next client             | client | no         |
| s   | modkey, shift          | yes     | awful.client.swap.byidx(-1)      | swap with previous client     | client | no         |
| t   | modkey, shift          | yes     | awful.client.swap.byidx(1)       | swap with next client         | client | no         |
| m   | modkey, shift, control | yes     | c:swap(awful.client.getmaster()) | swap with master client       | client | yes        |
| u   | modkey                 | no      | awful.client.urgent.jumpto       | jump to urgent client         | client | no         |
| v   | modkey                 | yes     | c.ontop = not c.ontop            | toggle keep on top            | client | yes        |
| f   | modkey, control        | no      | awful.client.floating.toggle     | toggle floating               | client | yes        |
| Tab | modkey                 | no      | client_go_back                   | go back                       | client | no         |

** Layout manipulation
:PROPERTIES:
:CUSTOM_ID: Keybindings-Layout_manipulation-648b4581
:END:
#+NAME: sc-layout
| Key   | Modifiers       | Lambda? | Action                              | What it does                      | Group  |
|-------+-----------------+---------+-------------------------------------+-----------------------------------+--------|
| r     | modkey          | yes     | awful.tag.incmwfact(0.05)           | increase master width factor      | layout |
| c     | modkey          | yes     | awful.tag.incmwfact(-0.05)          | decrease master width factor      | layout |
| r     | modkey, shift   | yes     | awful.tag.incnmaster(1, nil, true)  | increase number of master clients | layout |
| c     | modkey, shift   | yes     | awful.tag.incnmaster(-1, nil, true) | decrease number of master clients | layout |
| r     | modkey, control | yes     | awful.tag.incncol(1, nil, true)     | increase number of colums         | layout |
| c     | modkey, control | yes     | awful.tag.incncol(-1, nil, true)    | decrease number of colums         | layout |
| space | modkey          | yes     | awful.layout.inc(1)                 | next layout                       | layout |
| space | modkey, meta    | yes     | awful.layout.inc(-1)                | previous layout                   | layout |

** Media
:PROPERTIES:
:CUSTOM_ID: Keybindings-Media-f2cc1324
:END:
#+NAME: sc-media
| Key                  | Modifiers       | Lambda?  | Action                          | What it does             | Group |
|----------------------+-----------------+----------+---------------------------------+--------------------------+-------|
| +                    | modkey, meta    | shell    | mpc volume +2                   | increase mpd volume      | media |
| -                    | modkey, meta    | shell    | mpc volume -2                   | decrease mpd volume      | media |
| n                    | modkey, meta    | terminal | ncmpcpp -q                      | spawn ncmpcpp            | media |
| v                    | modkey, meta    | terminal | ncmpcpp -qs visualizer          | spawn ncmpcpp visualizer | media |
| XF86AudioLowerVolume |                 | shell    | amixer -q set Master 2%- unmute | lower volume             | media |
| Prior                | modkey, control | shell    | amixer -q set Master 2%- unmute | lower volume             | media |
| XF86AudioRaiseVolume |                 | shell    | amixer -q set Master 2%+ unmute | raise volume             | media |
| Next                 | modkey, control | shell    | amixer -q set Master 2%+ unmute | lower volume             | media |
| XF86AudioMute        |                 | shell    | amixer -q set master 1+ toggle  | toggle mute audio        | media |
| Prior                | modkey, control | shell    | amixer -q set master 1+ toggle  | toggle mute audio        | media |
| XF86AudioPrev        |                 | shell    | mpc prev                        | previous mpd track       | media |
| XF86AudioLowerVolume | meta            | shell    | mpc prev                        | prevous mpd track        | media |
| Prior                | modkey          | shell    | mpc prev                        | previous mpd track       | media |
| XF86AudioNext        |                 | shell    | mpc next                        | next mpd track           | media |
| XF86AudioRaiseVolume | meta            | shell    | mpc next                        | next mpd track           | media |
| Next                 | modkey          | shell    | mpc next                        | next mpd track           | media |
| XF86AudioPlay        |                 | shell    | mpc toggle                      | toggle mpd playback      | media |
| p                    | modkey          | shell    | mpc toggle                      | toggle mpd playback      | media |
| XF86AudioStop        |                 | shell    | mpc stop                        | stop playback            | media |
| XF86AudioPlay        | meta            | shell    | mpc stop                        | stop playback            | media |
| p                    | modkey, meta    | shell    | mpc stop                        | stop playback            | media |

** Screen
:PROPERTIES:
:CUSTOM_ID: Keybindings-Screen-b73991f0
:END:
#+NAME: sc-screen
| Key                   | Modifiers    | Lambda? | Action                          | What it does               | Group  |
|-----------------------+--------------+---------+---------------------------------+----------------------------+--------|
| t                     | modkey, meta | yes     | awful.screen.focus_relative(1)  | focus next screen          | screen |
| s                     | modkey, meta | yes     | awful.screen.focus_relative(-1) | focus previous screen      | screen |
| XF86MonBrightnessDown |              | shell   | xbacklight -dec 1               | decrease screen brightness | screen |
| Next                  | modkey, meta | shell   | xbacklight -dec 1               | decrease screen brightness | screen |
| XF86MonBrightnessUp   |              | shell   | xbacklight -inc 1               | increase screen brightness | screen |
| Prev                  | modkey, meta | shell   | xbacklight -inc 1               | increase screen brightness | screen |
| F3                    | modkey       | spawn   | arandr                          | randr graphical frontend   | screen |
| o                     | modkey       | yes     | awful.screen.focus_relative(1)  | focus next screen          | screen |

** Tags
:PROPERTIES:
:CUSTOM_ID: Keybindings-Tags-3424b757
:END:
#+NAME: sc-tag
| Key    | Modifiers       | Lambda? | Action                    | What it does | Group |
|--------+-----------------+---------+---------------------------+--------------+-------|
| Escape | modkey          | no      | awful.tag.history.restore | go back      | tag   |
| t      | modkey, control | no      | awful.tag.viewprev        | view prev    | tag   |
| s      | modkey, control | no      | awful.tag.viewnext        | view next    | tag   |

Another set of shortcuts is linked to the number row on the keyboard that allow
the manipulation of the default tags that range from ~1~ to ~10~ (the latter is
displayed as ~0~). Here is what the possible actions are:
#+NAME: sc-tag-num
| Key    | Modifiers              | Action                          | What it does                   | Group |
|--------+------------------------+---------------------------------+--------------------------------+-------|
| Number | modkey                 | view_tag_n(                     | view tag #                     | tag   |
| Number | modkey, control        | toggle_tag_n(                   | toggle tag #                   | tag   |
| Number | modkey, shift          | move_focused_to_tag_n(          | move focused client to tag #   | tag   |
| Number | modkey, control, shift | toggle_focused_client_to_tag_n( | Toggle focused client on tag # | tag   |

** Misc
:PROPERTIES:
:CUSTOM_ID: Keybindings-Misc-0b45ce02
:END:
In this category you will find other keybindings that do not fit in other
categories. For now, the only keybinding that is in this category is for
toggling the touchpad’s tapping ability. This is linked to a special script I
wrote [[file:bin.org::#Toggle_touchpad_tapping-23348b00][here]].
#+NAME: sc-misc
| Key                | Modifiers | Lambda? | Action    | What it does            | Group |
|--------------------+-----------+---------+-----------+-------------------------+-------|
| XF86TouchpadToggle |           | shell   | tttapping | toggle touchpad tapping | misc  |

* Rules
:PROPERTIES:
:CUSTOM_ID: Rules-c6142cdf
:END:
With ~awful.rules~, users are able to describe some rules for window clients
when the latter spawn, such as their placement, their properties or even execute
a script. A rule can be applied through the ~manage~ signal, and they are all
stored in ~awful.rules.rules~, the global rules table, as follows:
#+BEGIN_SRC lua :tangle no
  awful.rules.rules = {
    -- Rules here
  }
#+END_SRC

# Block for exporting rules from below
#+BEGIN_SRC lua :exports none
  awful.rules.rules = {
    <<rules-universal>>,
    <<rules-floating>>,
    <<rules-titlebars>>,
    <<rules-default-tags>>
  }
#+END_SRC

For more documentation on rules and their syntax, you can read the [[https://awesomewm.org/doc/api/libraries/awful.rules.html][official
documentation]].

** Universal rules
:PROPERTIES:
:CUSTOM_ID: Rules-Universal_rules-50aad2ce
:END:
The first rule is a universal rule which will match all clients, as you can see
with its syntax below:
#+BEGIN_SRC lua :tangle no
  { rule = {},
    properties = {
      -- List of properties
    }
  }
#+END_SRC

Here is the list of properties with their value to apply to all clients, and a
short explanation as to what they do.
#+NAME: rules-universal-properties-table
| Property      | Value                                                   | What it does                                                            |
|---------------+---------------------------------------------------------+-------------------------------------------------------------------------|
| border_width  | beautiful.border_width                                  | Set the width of the window’s border                                    |
| border_color  | beautiful.border_normal                                 | Set the color of the window’s border                                    |
| focus         | awful.client.focus.filter                               | Set focus on the new window, except filtered out windows                |
| raise         | true                                                    | Set it as raised window                                                 |
| keys          | clientkeys                                              | Set the client’s shortcuts set in [[*Clients][Shortcuts/Clients]]                     |
| buttons       | clientbuttons                                           | Set the client’s mouse shortcuts from [[#Mouse_bindings-eb4a69a8][Mouse bindings]]                    |
| screen        | awful.screen.preferred                                  | Spawn the client on the main screen                                     |
| placement     | awful.placement.no_overlap+awful.placement.no_offscreen | Avoid the client to appear off the screen and overlaping another client |
| round_corners | true                                                    | Enable rounded corners for client                                       |

#+NAME: rules-universal-properties
#+BEGIN_SRC emacs-lisp :tangle no :exports none :var properties=rules-universal-properties-table :cache yes
  (mapconcat (lambda (x)
               (format "%s = %s"
                       (car x)
                       (cadr x)))
             properties
             ",\n")
#+END_SRC

#+RESULTS[5ddcb42f901ac56237de5ed102800b588f462100]: rules-universal-properties
: border_width = beautiful.border_width,
: border_color = beautiful.border_normal,
: focus = awful.client.focus.filter,
: raise = true,
: keys = clientkeys,
: buttons = clientbuttons,
: screen = awful.screen.preferred,
: placement = awful.placement.no_overlap+awful.placement.no_offscreen,
: round_corners = true

This is what my universal rules look like:
#+NAME: rules-universal
#+BEGIN_SRC lua :tangle no
   { rule = {},
     properties = {
       <<rules-universal-properties()>>
     }
   }
#+END_SRC

** Floating clients
:PROPERTIES:
:CUSTOM_ID: Rules-Floating_clients-49ab582e
:END:
Some clients will be declared by default as floating windows. For this, we will
declare a rule that will match any of the provided conditions:
#+NAME: rules-floating-conditions-table
| Property | Matches      | Comment                                                        |
|----------+--------------+----------------------------------------------------------------|
| instance | pinentry     | Matches any Polkit                                             |
| class    | Arandr       | Visual frontend for Randr                                      |
| class    | Sxiv         | Simple X Image Viewer                                          |
| class    | Tor Browser  | Needs a fixed window size to avoid fingerprinting              |
| name     | Event Tester | xev                                                            |
| role     | pop-up       | Any pop-up window, such as Chromium’s detached Developer Tools |

#+NAME: rules-floating-conditions
#+BEGIN_SRC emacs-lisp :exports none :tangle no :var conditions=rules-floating-conditions-table :cache yes
  (mapconcat (lambda (x)
               (format "%s = { \"%s\" }" (car x) (cadr x)))
             conditions
             ",\n")
#+END_SRC

#+RESULTS[1fbe7dc1e85b5170957c9583e39c4cbec9a7d7ca]: rules-floating-conditions
: instance = { "pinentry" },
: class = { "Arandr" },
: class = { "Sxiv" },
: class = { "Tor Browser" },
: name = { "Event Tester" },
: role = { "pop-up" }

If any of these conditions is matched, then the client will be set as floating,
as you can see below:
#+NAME: rules-floating
#+BEGIN_SRC lua :tangle no
  { rule_any = {
      <<rules-floating-conditions()>>
  }, properties = { floating = true }}
#+END_SRC

** Titlebars
:PROPERTIES:
:CUSTOM_ID: Rules-Titlebars-b532fdba
:END:
Any normal or dialog client will get a titlebar. This is enabled like so:
#+NAME: rules-titlebars
#+BEGIN_SRC lua :tangle no
  { rule_any = {type = { "normal", "dialog" }
               }, properties = { titlebars_enabled = true }
  }
#+END_SRC

** Default tag for clients
:PROPERTIES:
:CUSTOM_ID: Rules-Default_tag_for_clients-6ded2a47
:END:
With the use of some rules, it is possible to define which client are assigned
to which tag by default.
#+NAME: rules-default-tags-table
| Client Property | Value      | Tag |
|-----------------+------------+-----|
| class           | Emacs      |   2 |
| class           | firefox    |   3 |
| class           | Nemo       |   4 |
| class           | Gimp*      |   5 |
| class           | Steam      |   9 |
| class           | discord    |   0 |
| class           | mattermost |   0 |

#+NAME: rules-default-tags-generate
#+BEGIN_SRC emacs-lisp :tangle no :exports none :cache yes :var tags=rules-default-tags-table
  (mapconcat (lambda (x)
               (format "{rule = {%s = \"%s\"}, properties = {screen = 1, tag = \"%d\"} }"
                       (nth 0 x) (nth 1 x) (nth 2 x)))
             tags
             ",\n")
#+END_SRC

#+RESULTS[ec74b9e45bd477d22e2865de30a9c64973cf0927]: rules-default-tags-generate
: {rule = {class = "Emacs"}, properties = {screen = 1, tag = "2"} },
: {rule = {class = "firefox"}, properties = {screen = 1, tag = "3"} },
: {rule = {class = "Nemo"}, properties = {screen = 1, tag = "4"} },
: {rule = {class = "Gimp*"}, properties = {screen = 1, tag = "5"} },
: {rule = {class = "discord"}, properties = {screen = 1, tag = "0"} },
: {rule = {class = "mattermost"}, properties = {screen = 1, tag = "0"} },
: {rule = {class = "Steam"}, properties = {screen = 1, tag = "9"} }

This is what these rules look like:
#+NAME: rules-default-tags
#+BEGIN_SRC lua :tangle no
  <<rules-default-tags-generate()>>
#+END_SRC

* Signals
:PROPERTIES:
:CUSTOM_ID: Signals-e32971d6
:END:
Signals are a way for Awesome to handle events, such as client creation or
deletion.

** Client creation
:PROPERTIES:
:CUSTOM_ID: Signals-Client_creation-8048ac12
:END:
When a new client is created, the ~manage~ signal is emited. When so, the
following snippet ensures this new client is not off the screen, unless its
position was deliberately set by a program or by the user. It will also spawn
the new client where the mouse currently is.
#+BEGIN_SRC lua
  client.connect_signal("manage", function (c)
    awful.client.movetoscreen(c, mouse.screen)
    if awesome.startup
        and not c.size_hints.user_position
        and not c.size_hints.program_position then
      awful.placement.no_offscreen(c)
    end
  end)
#+END_SRC

** Titlebar creation
:PROPERTIES:
:CUSTOM_ID: Signals-Titlebar_creation-3b1aaa14
:END:
It is possible for Awesome to send request signals, such as the request to
create titlebar (generally for new clients). The following snippet handles this
titlebar creation if titlebar creation was set to ~true~ in the [[#Rules-c6142cdf][rules]]. For a
detailed explanation of the code, see below.
#+BEGIN_SRC lua
  client.connect_signal("request::titlebars", function(c)
                          local buttons = gears.table.join(
                            <<signal-titlebar-button1>>,
                            <<signal-titlebar-button3>>
                          )
                          <<signal-titlebar-create>>
  end)
#+END_SRC

The function has two main parts: the creation of the titlebar buttons (mouse
handling on the titlebar), and the creation of the titlebar itself. The creation
of the button is done by creating a local variable ~buttons~ which will be a
table created by the library ~gears~, in which will be buttons created by the
user.
#+BEGIN_SRC lua :tangle no
  local buttons = gears.table.join(
    -- Buttons declared here
  )
#+END_SRC

You can see a left click will enable the user to raise the window, but also it
will enable the user to move the window (if it is floating of course).
#+NAME: signal-titlebar-button1
#+BEGIN_SRC lua :tangle no
  awful.button({ }, 1, function()
      c:emit_signal("request::activate", "titlebar", {raise = true})
      awful.mouse.client.move(c)
  end)
#+END_SRC

A right click on the titlebar will also raise the window, but will instead allow
the user to resize the client.
#+NAME: signal-titlebar-button3
#+BEGIN_SRC lua :tangle no
  awful.button({ }, 3, function()
    c:emit_signal("request::activate", "titlebar", {raise = true})
    awful.mouse.client.resize(c)
  end)
#+END_SRC

Next comes the actual creation of the titlebar for the client ~c~. For that, we
call ~awful.titlebar()~, tell it where the titlebar should be relative to the
client and what its setup should be. The full call should look like so:
#+NAME: signal-titlebar-create
#+BEGIN_SRC lua :tangle no
  awful.titlebar(c, {position="left", size = 22}) : setup {
    <<signal-titlebar-setup>>
  }
#+END_SRC

In the setup, I need to repeat to Awesome the titlebar should be on the left of
the client, and I also tell it the layout alignment of the titlebar will be
vertical, because I like vertial titlebars. I also first send it three tables:
- The top or left elements of the titlebar (here the top)
- The middle elements of the titlebar
- The bottom or right elements of the titlebar (here the bottom)
You can notice in the setup’s code below that I haven’t included anything in the
middle elements, the only elements I am interested in are the top and bottom
elements. In the top elements, I have (top to bottom):
- A close button
- A maximize button
- A minimize button
- And an indication to Awesome these elements should be vertically aligned
To make Awesome happy, I also must indicate that the middle elements are
vertically aligned, and then I can declare my bottom elements:
- A button for toggling client floating
- And again the indication to Awesome these elements should be vertically
  aligned
#+NAME: signal-titlebar-setup
#+BEGIN_SRC lua :tangle no
  { -- Top
    awful.titlebar.widget.closebutton(c),
    awful.titlebar.widget.maximizedbutton(c),
    awful.titlebar.widget.minimizebutton(c),
    layout = wibox.layout.fixed.vertical()
  },
  {
    layout = wibox.layout.fixed.vertical()
  }, -- Middle
  { -- Bottom
    awful.titlebar.widget.floatingbutton(c),
    layout = wibox.layout.fixed.vertical()
  },
  layout = wibox.layout.align.vertical,
  position = "left"
#+END_SRC

** Changes of focus
:PROPERTIES:
:CUSTOM_ID: Signals-Changes_of_focus-1b73902c
:END:
The default Awesome configuration enables the following snippet of code that
makes windows hovered by the user’s mouse focused. Just for completeness’ sake,
I included it in this document, but be aware this won’t be tangled into my
configuration file and focus will not follow my mouse.
#+BEGIN_SRC lua :tangle no
  client.connect_signal("mouse::enter", function(c)
                          c:emit_signal("request::activate", "mouse_enter", {raise = false})
  end)
#+END_SRC

It is also possible to change the color of the borders based on client focus.
While my clients don’t have any border, they do have a titlebar which color
changes based on the client’s focus. This is handled by the following code
snippet:
#+BEGIN_SRC lua
  client.connect_signal("focus", function(c) c.border_color = beautiful.border_focus end)
  client.connect_signal("unfocus", function(c) c.border_color = beautiful.border_normal end)
#+end_SRC

* Autostart
:PROPERTIES:
:CUSTOM_ID: Autostart-f2cf42fe
:END:
By simply adding a line requesting to spawn a command, it is possible to create
some autolaunch. All of my autolaunched apps are launch through a custom script
which you can [[file:~/org/config/bin.org::#Autostart-a99e99e7][find here]]. The command gets called with
~awful.spawn.with_shell()~, as you can see below.
#+BEGIN_SRC lua
  awful.spawn.with_shell("autostart")
#+END_SRC

* What to do now                                                   :noexport:
:PROPERTIES:
:CUSTOM_ID: What_to_do_now-bce61fe1
:END:
** DONE Error on S-q
   CLOSED: [2020-04-12 dim. 15:47]
:PROPERTIES:
:CUSTOM_ID: What_to_do_now-Error_on_S-q-beea9b99
:END:
~attempt to index a nil value (global 'c')~

** TODO Make custom theme
:PROPERTIES:
:CUSTOM_ID: What_to_do_now-Make_custom_theme-5837307e
:END:
