# -*- org-confirm-babel-evaluate: nil -*-
#+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

* 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
  appls 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 = "st"
     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. This depends
    on [[https://github.com/l3ib/nitrogen/][Nitrogen]].
    #+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

** Other widgets
   :PROPERTIES:
   :CUSTOM_ID: Top_bar-Other_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.viewnext(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.viewprev(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
   [[#Layouts-be55a7fd][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)>>,
      <<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   |

*** 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 -show combi                      | invoke rofi                     | rofi  |
    | d   | modkey, shift          | spawn   | rofi -combi-modi drun,run -show combi | invoke j4-dmenu-desktop         | 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  |
    | w   | modkey, shift          | shell   | rofi-wifi-menu                        | connect to available wifi       | 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   | lock                      | 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 |
   | 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                 | 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 5               | decrease screen brightness | screen |
   | Next                  | modkey, meta | shell   | xbacklight -dec 5               | decrease screen brightness | screen |
   | XF86MonBrightnessUp   |              | shell   | xbacklight -inc 5               | increase screen brightness | screen |
   | Prev                  | modkey, meta | shell   | xbacklight -inc 5               | increase screen brightness | screen |
   | F3                    | modkey       | spawn   | arandr                          | randr graphical frontend   | 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   |

* 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: 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           | discord |   0 |
   | class           | Steam   |   9 |

   #+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[2b9fe1774ebfde3fc2cfadb24a32dee9b2639144]: 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 = "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"}) : 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 the
  shell.
  #+NAME: autostart-table
  | Command                                         | What it is for                         |
  |-------------------------------------------------+----------------------------------------|
  | pumopm                                          | Power manager                          |
  | /usr/lib/xfce-polkit/xfce-polkit                | Launch or relaunch Polkit              |
  | sleep 1 && set-screens                          | Auto-organize screens                  |
  | picom --experimental-backends                   | Launch or relaunch Picom               |
  | nitrogen --restore                              | Restore background                     |
  | xss-lock -- lock                                | Enable lockscreen after a blank screen |
  | nm-applet                                       | Launch NetworkManager applet           |
  | numlockx on                                     | Enable numlock                         |
  | mpc stop                                        | Stop music                             |
  | mpd_discord_richpresence --no-idle --fork       | Launch MPD rich presence for Discord   |
  | sleep 3 && emacsclient -c -e \"(delete-frame)\" | Launch blank EmacsClient               |

  #+NAME: autostart
  #+BEGIN_SRC emacs-lisp :tangle no :exports none :cache yes :var apps=autostart-table
    (mapconcat (lambda (x)
                 (format "awful.spawn.with_shell(\"%s\")"
                         (car x)))
               apps
               "\n")
  #+END_SRC

  #+RESULTS[e65349733980220944a79123e3ee0f4d638e5068]: autostart
  #+begin_example
  awful.spawn.with_shell("pumopm")
  awful.spawn.with_shell("pkill xfce-polkit; /usr/lib/xfce-polkit/xfce-polkit")
  awful.spawn.with_shell("sleep 1 && set-screens")
  awful.spawn.with_shell("picom --experimental-backends")
  awful.spawn.with_shell("nitrogen --restore")
  awful.spawn.with_shell("xss-lock -- lock")
  awful.spawn.with_shell("nm-applet")
  awful.spawn.with_shell("numlockx on")
  awful.spawn.with_shell("mpc stop")
  awful.spawn.with_shell("mpd_discord_richpresence --no-idle --fork")
  awful.spawn.with_shell("sleep 3 && emacsclient -c -e \"(delete-frame)\"")
  #+end_example

  Each of the command gets called with the call of ~awful.spawn.with_shell()~,
  as you can see below.
  #+BEGIN_SRC lua
    <<autostart()>>
  #+END_SRC

  PumoPM is a custom power manager I’ve written, because other power managers
  didn’t fit my needs, so I wrote my own. You can find it [[https://labs.phundrak.com/phundrak/pumopm][here]].

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