dotfiles/org/config/awesome.org

73 KiB
Raw Blame History

AwesomeWM configuration

Introduction

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

First of all, some initialization is needed, and this initialization is about math randomness. So, lets initialize the random method of the math library:

  math.randomseed(os.time())

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.

  pcall(require, "luarocks.loader")

Next, well also load the following 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
  (mapconcat (lambda (x) (format "local %s = require(\"%s\")"
                                 (cadr x)
                                 (car x)))
             libs
             "\n")
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:

<<imported-libraries()>>

I also want to be able to autofocus the first window when I go to another workspace, so lets require that:

  require("awful.autofocus")

And finally, I want to be able to declare some shortcuts specific to some appls thanks to the hotkeys help widget.

  require("awful.hotkeys_popup.keys")

By the way, lets initialize the random method of the math library:

  math.randomseed(os.time())

Error handling

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.

  if awesome.startup_errors then
    naughty.notify({ preset = naughty.config.presets.critical,
                     title = "Oops, there were errors during startup!",
                     text = awesome.startup_errors })
  end

And this code handles runtime errors after startup thanks to signals.

  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

Variable definitions

Themes

With Awesome, it is possible to load or write custom themes in order to give Awesome a special look that fits the user. I used to load the default theme from Xresources, but now I would like to use my own theme, tweaked to my liking.

Creating my own theme (WIP, not yet used)

Here I will create my own theme, which will be exported to ~/.config/awesome/theme/theme.lua. It is based on my former Xresources theme, but also on some other themes I found on the internet, particularly on Github. Here are a few I would like to mention:

My theme has two main dependencies: lain and freedesktop for Awesome. The former is a framework for Awesome theming while the second enables its users to add .desktop entries to the Awesome menu.

Loading assets

So first off, lets load our libraries or anything I might need to import. First, let's import the theme_assets module of beautiful, a collection of functions for theming, as well as the xresources library which will let us interact with the Xresources theme. The filesystem module of gears will also enable us to interact with the systems filesystem, getting paths and files. I will also import lain, the above described dependency, so I get some useful functions and constructs.

Module Import as What it is
beautiful.theme_assets theme_assets theming library
beautiful.xresources xresources Xresources interactivity
gears.filesystem gfs filesystem interactivity
lain lain theming framework
  (mapconcat (lambda (x)
               (format formstr
                       (cadr x) (car x)))
             table "\n")

Here is what the code looks like:

  <<theme-modules-import-gen()>>

Lets also import some functions and values!

What to import Import as What it is
xresources.apply_dpi dpi Apply screens DPI (documentation)
xresources.get_current_theme() xrdb Get current Xresources theme
gfs.get_themes_dir() themes_path Get path to default Awesome themes

Here is what the code looks like:

  <<theme-modules-import-gen(table=theme-modules-variables-table, formstr="local %s = %s")>>
Inherit the default theme

Now that I imported what I need, I can get a default theme which I will later modify:

  local theme = dofile(themes_path.."default/theme.lua")
Loading default fonts and colors

With the default theme loaded, let's modify it! The themes font will be the same as the one I use for st: Source Code Pro for Powerline

  theme.font = "Source Code Pro for Powerline 8"

Loading the theme

Finally, lets load our theme.

  beautiful.init("/home/phundrak/.config/awesome/xresources/theme.lua")

On top of that, I would like to edit some settings. First of all, lets set some default transparency for Awesome, with an alpha background. By default, it is completely opaque with a max value of 256 and a minimal value of 0.

Default terminal and text editor

The two following variables are set so that I dont 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.

  terminal = "st"
  editor = os.getenv("EDITOR") or "emacsclient -c"

Keys

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.

  modkey = "Mod4"
  shift = "Shift"
  control = "Control"
  meta = "Mod1"
  alt = "Mod1" -- Just in case

Wallpapers directory

This variable is a variable I personally set for a function described below in order to have a variable pointing to my wallpaper directory.

  local papes_dir = "~/Pictures/Wallpapers"

Custom functions

Wallpaper-related functions

Set a random wallpaper

The following function allows the user to get the list of available wallpapers I have in my wallpapers directory. This depends on the variable described in #h-027a8758-fa96-473f-8ad3-44e07a5e7f4b. By the way, the assert on line 3 allows Lua to wait for popen to run.

  local function get_papes()
    local i, papes, popen = 0, {}, io.popen
    local pfile = assert(popen('find '..papes_dir..' -type f'))
    for filename in pfile:lines() do
      i = i + 1
      papes[i] = filename
    end
    pfile:close()
    return papes
  end

This function is another one that sets a random wallpaper from the list of wallpapers provided by get_papes() described above. This depends on Nitrogen and Pywal.

  local function set_random_pape()
    papes = get_papes()
    pape = papes[math.random(#papes)]
    awful.spawn.with_shell("nitrogen --set-scaled "..pape.." && wal -o wal-set -i "..pape)
    naughty.notify({ preset = naughty.config.presets.normal,
                     title = "Wallpaper change",
                     text = "Done!"})
  end

Restore previous wallpaper

I also wrote the following function that will restore the previously set wallpaper:

  local function set_wallpaper(_)
    awful.spawn.with_shell("nitrogen --set-scaled (cat $HOME/.cache/wal/wal)")
    awful.spawn.with_shell("wal -i (cat $HOME/.cache/wal/wal)")
    awful.spawn.with_shell("xrdb $HOME/.Xresources")
  end

Layout manipulation

The following function is used by a shortcut described below in #h-521b02ca-0ad3-44e8-8d5b-1e75401490da.

  local function client_go_back()
    awful.client.focus.history.previous()
    if client.focus then
      client.focus:raise()
    end
  end

Clients manipulation

  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
 local function toggle_fullscreen_client(c)
   c.fullscreen = not c.fullscreen
   c:raise()
 end
  local function toggle_maximized(c)
    c.maximized = not c.maximized
    c:raise()
  end
 local function toggle_vertical_maximized(c)
   c.maximized_vertical = not c.maximized_vertical
   c:raise()
 end
 local function toggle_horizontal_maximized(c)
   c.maximized_horizontal = not c.maximized_horizontal
   c:raise()
 end

Tag manipulation

  local function view_tag_n(i)
    local screen = awful.screen.focused()
    local tag = screen.tags[i]
    if tag then
      tag:view_only()
    end
  end
  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
  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
  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

Awesome prompt

  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

Layouts

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.

Layout Enabled?
magnifier yes
tile.left yes
tile yes
max yes
max.fullscreen yes
floating yes
fair yes
fair.horizontal yes
tile.bottom yes
tile.top yes
spiral yes
spiral.dwindle yes
corner.nw no
corner.ne no
corner.sw no
corner.se no
  (mapconcat (lambda (x) (if (string= (cadr x) "yes")
                             (format "awful.layout.suit.%s,\n" (car x))))
             layouts
             "")
awful.layout.suit.magnifier,
awful.layout.suit.tile,
awful.layout.suit.tile.left,
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.tile.bottom,
awful.layout.suit.tile.top,
awful.layout.suit.spiral,
awful.layout.suit.spiral.dwindle,

Here is the code that activates these layouts:

  awful.layout.layouts = {
    <<list-layouts()>>
  }

Top bar

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

  (mapconcat (lambda (x)
               (format "{ \"%s\", %s }"
                       (car x)
                       (mapconcat (lambda (y) (format "%s" y))
                                  (cdr x)
                                  ",")))
             menu
             ",\n")
{ "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, lets declare a menu related to Awesome:

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:

  awesomewm_menu = {
    <<make-menu(menu=table-awesome-menu)>>
  }

Next, lets create the main menu that will be used on S-w and at the top left of the window:

Name Command Icon
awesome awesomewm_menu beautiful.awesome_icon
open terminal terminal nil

Here is the actual code:

  mainmenu = awful.menu({ items = {
                            <<make-menu(menu=table-main-menu)>>
                       }})

For now it only has two entries: the Awesome menu and opening a terminal, I will add some more later probably. Lets specify it as being our main launcher:

  launcher = awful.widget.launcher({ image = beautiful.awesome_icon,
                                     menu = mainmenu })

Finally, lets declare the menubars terminal for applications that require it.

  menubar.utils.terminal = terminal

Other widgets

Lets declare the keyboard map indicator and switcher for the top bar:

  keyboardlayout = awful.widget.keyboardlayout()

Lets also create a clock widget:

  textclock = wibox.widget.textclock()

Tag list

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:

  local tasklist_buttons = gears.table.join(
    -- configuration goes here
  )

gears.table.join() joins several tables together, as described 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, lets 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.

  awful.button({ }, 1, function(t) t:view_only() end)

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.

  awful.button({ modkey }, 1, awful.tag.viewtoggle)

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.

  awful.button({ }, 3, function(t)
      if client.focus then
        client.focus:move_to_tag(t)
      end
  end)

However, a right click combined with the modkey will add the clicked tag to the currently focused window, making it visible to both tags.

  awful.button({ modkey }, 3, function(t)
      if client.focus then
        client.focus:toggle_tag(t)
      end
  end)

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.

  awful.button({ }, 4, function(t) awful.tag.viewnext(t.screen) end)

Otherwise, if a scroll down is detected, the next tag will be displayed.

  awful.button({ }, 5, function(t) awful.tag.viewprev(t.screen) end)

So, heres the actual configuration code for the taglist:

  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>>
  )

Tasks list

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:

  local tasklist_buttons = gears.table.join(
    -- List of clicks
  )

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.

  awful.button({ }, 1, function (c)
      if c == client.focus then
        c.minimized = true
      else
        c:emit_signal(
          "request::activate",
          "tasklist",
          {raise = true}
        )
      end
  end)

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.

  awful.button({ }, 3, function()
        awful.menu.client_list({ theme = { width = 250 } })
  end)

If a scroll up is detected, then lets select the previous client in the tasklist.

  awful.button({ }, 4, function ()
        awful.client.focus.byidx(1)
  end)

If a scroll down is detected, then lets select the next client in the tasklist.

  awful.button({ }, 5, function ()
        awful.client.focus.byidx(-1)
  end)

So, heres the actual code for the tasklist:

  local tasklist_buttons = gears.table.join(
    <<task-simple-left-click>>,
    <<task-simple-right-click>>,
    <<task-simple-scroll-up>>,
    <<task-simple-scroll-down>>
  )

Theme and display

Screen update

When a screens 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 wont necessarily fit the new geometry of the screen. And remember, I have a function that does exactly that! Lets connect this function to the geometry change signal:

  screen.connect_signal("property::geometry", set_wallpaper)

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:

  awful.screen.connect_for_each_screen(function(s)
      -- Code to be executed goes here
  end)

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, lets set its wallpaper:

  set_wallpaper()

Next, lets 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.

  awful.tag({ "1", "2", "3", "4", "5", "6", "7", "8", "9", "0" }, s, awful.layout.layouts[1])

Next, lets create the taglist widget. It will use the taglist_buttons declared above in order to handle clicks on tags, and due to the filter, all tags will be displayed in the tagbar (more about tag filters).

  s.taglist = awful.widget.taglist {
    screen  = s,
    filter  = awful.widget.taglist.filter.all,
    buttons = taglist_buttons
  }

A tasklist widget will also get created thanks with the tasklist_button declared above that will handle clicks on tasks. Contrarily to the taglist widget above, the tasklist will only display the screens current tags thanks to its filter.

  s.tasklist = awful.widget.tasklist {
    screen  = s,
    filter  = awful.widget.tasklist.filter.currenttags,
    buttons = tasklist_buttons
  }

A promptbox will also be created for the screen:

  s.promptbox = awful.widget.prompt()

Then, Lets 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.

  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)))

Now it is time to create the widget, a wibox that will contain our bar.

  s.wibox = awful.wibar({ position = "top", screen = s })

Finally, lets 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.

  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,
    },
  }

In the end, our code looks like this:

  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)

Mouse bindings

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.

  root.buttons(gears.table.join(
                 awful.button({}, 3, function() mainmenu:toggle() end)
  ))

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.
  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)
  )

Keybindings

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 shortcuts action
Group
group in which the shortcut will appear in Awesomes help window
Clientkey?
whether this should be a global shortcut or a shortcut only aimed at clients (value is yes or no)
  (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)))
  (mapconcat
   <<gen-sc-text>>
   (seq-filter (lambda (x)
                 (or (not (nth 6 x))
                     (string= "no" (nth 6 x))))
               table)
   ",\n")
  (mapconcat
   <<gen-sc-text>>
   (seq-filter (lambda (x)
                 (string= "yes" (nth 6 x)))
               table)
   ",\n")
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"})
  (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"))

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).

  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)>>
  )

Applications

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

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

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

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

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

Here will be declared some shortcuts directly related to Awesome itself.

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

These shortcuts are related to clients (aka windows) management.

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 yes 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 by index client no
t modkey yes awful.client.focus.byidx(1) focus next client by index client no
s modkey, shift yes awful.client.swap.byidx(-1) swap with previous client by index client no
t modkey, shift yes awful.client.swap.byidx(1) swap with next client by index client no
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
space modkey, control no awful.client.floating.toggle toggle floating client yes
Left modkey yes awful.client.focus.byidx(1) view previous client no
Return modkey, control yes c:swap(awful.client.getmaster()) move to master client yes
Right modkey yes awful.client.focus.byidx(-1) view next client no
Tab modkey yes client_go_back() go back client no

Layout manipulation

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

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

Key Modifiers Lambda? Action What it does Group
t modkey, control yes awful.screen.focus_relative(1) focus next screen screen
s modkey, control 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

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:

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

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:

  awful.rules.rules = {
    -- Rules here
  }

For more documentation on rules and their syntax, you can read the official documentation.

Universal rules

The first rule is a universal rule which will match all clients, as you can see with its syntax below:

  { rule = {},
    properties = {
      -- List of properties
    }
  }

Here is the list of properties with their value to apply to all clients, and a short explanation as to what they do.

Property Value What it does
border_width beautiful.border_width Set the width of the windows border
border_color beautiful.border_normal Set the color of the windows 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 clients shortcuts set in Shortcuts/Clients
buttons clientbuttons Set the clients mouse shortcuts from 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
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:

   { rule = {},
     properties = {
       <<rules-universal-properties()>>
     }
   }

Floating clients

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:

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 Chromiums detached Developer Tools

If any of these conditions is matched, then the client will be set as floating, as you can see below:

  { rule_any = {
      <<rules-floating-conditions()>>
  }, properties = { floating = true }}

Titlebars

Any normal or dialog client will get a titlebar. This is enabled like so:

  { rule_any = {type = { "normal", "dialog" }
               }, properties = { titlebars_enabled = true }
  }

Default tag for clients

With the use of some rules, it is possible to define which client are assigned to which tag by default.

Client Property Value Tag
class Emacs 2
class firefox 3
class Nemo 4
class Gimp* 5
class discord 0
class Steam 9
{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:

  <<rules-default-tags-generate()>>

Signals

Signals are a way for Awesome to handle events, such as client creation or deletion.

Client creation

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.

  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)

Titlebar creation

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. For a detailed explanation of the code, see below.

  client.connect_signal("request::titlebars", function(c)
                          local buttons = gears.table.join(
                            <<signal-titlebar-button1>>,
                            <<signal-titlebar-button3>>
                          )
                          <<signal-titlebar-create>>
  end)

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.

  local buttons = gears.table.join(
    -- Buttons declared here
  )

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).

  awful.button({ }, 1, function()
      c:emit_signal("request::activate", "titlebar", {raise = true})
      awful.mouse.client.move(c)
  end)

A right click on the titlebar will also raise the window, but will instead allow the user to resize the client.

  awful.button({ }, 3, function()
    c:emit_signal("request::activate", "titlebar", {raise = true})
    awful.mouse.client.resize(c)
  end)

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:

  awful.titlebar(c, {position="left"}) : setup {
    <<signal-titlebar-setup>>
  }

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 setups code below that I havent included anything in the middle or bottom elements, the only elements I am interested in are the top elements. I have (top to bottom):

  • A close button
  • A maximize button
  • A minimize button
  • A button for toggling client floating
  • And an indication to Awesome these elements should be vertically aligned
  { -- Top
    awful.titlebar.widget.closebutton(c),
    awful.titlebar.widget.maximizedbutton(c),
    awful.titlebar.widget.minimizebutton(c),
    awful.titlebar.widget.floatingbutton(c),
    layout = wibox.layout.fixed.vertical()
  },
  layout = wibox.layout.align.vertical,
  position = "left"

Changes of focus

The default Awesome configuration enables the following snippet of code that makes windows hovered by the users mouse focused. Just for completeness sake, I included it in this document, but be aware this wont be tangled into my configuration file and focus will not follow my mouse.

  client.connect_signal("mouse::enter", function(c)
                          c:emit_signal("request::activate", "mouse_enter", {raise = false})
  end)

It is also possible to change the color of the borders based on client focus. While my clients dont have any border, they do have a titlebar which color changes based on the clients focus. This is handled by the following code snippet:

  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)

Autostart

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.

Command What it is for
pkill xfce-polkit; /usr/lib/xfce-polkit/xfce-polkit Launch or relaunch Polkit
pkill picom; compton experimental-backends Launch or relaunch Picom
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
awful.spawn.with_shell("pkill xfce-polkit; /usr/lib/xfce-polkit/xfce-polkit")
awful.spawn.with_shell("pkill picom; compton --experimental-backends")
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)\"")

Each of the command gets called with the call of awful.spawn.with_shell(), as you can see below.

  <<autostart()>>