CONFIG

Awesome WM

#OrgConfig
Published — Updated

Introduction

Every once in a while I get bored of those smooth-running, everything basically works desktops, and I pull out awesomewm. It basically works too, but not after I’m through with it.

My starting rc.lua wasn’t much different from the stock default.rc.lua. I started with the themes that ship with the Ubuntu setup one gets with apt-get install awesomewm. You can find those in /usr/share/awesome/themes.

That chafed pretty quick, though. Awesome WM Copycats is a large theme collection for Awesome.

git clone --recursive https://github.com/lcpz/awesome-copycats.git && \
    mv -bv awesome-copycats/* ~/.config/awesome && rm -rf awesome-copycats

Course, it also needs a different rc.lua to drive it. So I scrapped my old config and used rc.lua.template from Awesome WM Copycats as my new starter.

Main rc.lua file

<<awesome-required-libraries>>
<<awesome-error-handling>>
<<awesome-autostart-windowless-processes>>
<<awesome-variable-definitions>>
<<awesome-menu>>
<<awesome-screen>>
<<awesome-mouse-bindings>>
<<awesome-key-bindings>>
<<awesome-rules>>
<<awesome-signals>>

Tangle fragments

Required libraries

awesome API index
a reference for all the core awesome libraries
  • gears
  • awful
  • beautiful
  • naughty
  • menubar
Awesome-Freedesktop
Freedesktop menu and desktop icon support for Awesome WM 4.x
Lain
layouts, widgets, and utilities for Awesome WM 4.x

  local awesome, client, mouse, screen, tag = awesome, client, mouse, screen, tag
  local ipairs, string, os, table, tostring, tonumber, type = ipairs, string, os, table, tostring, tonumber, type

  local gears         = require("gears")
  local awful         = require("awful")
  require("awful.autofocus")
  local wibox         = require("wibox")
  local beautiful     = require("beautiful")
  local naughty       = require("naughty")
  local lain          = require("lain")
  --local menubar       = require("menubar")
  local freedesktop   = require("freedesktop")
  local hotkeys_popup = require("awful.hotkeys_popup").widget
  require("awful.hotkeys_popup.keys")
  local my_table      = awful.util.table or gears.table -- 4.{0,1} compatibility
  local dpi           = require("beautiful.xresources").apply_dpi

Error handling

Check 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

  -- Handle runtime errors after startup
  do
    local in_error = false
    awesome.connect_signal(
      "debug::error",
      function (err)
        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

Autostart windowless processes

  • unclutter is a relatively ancient X utility that hides the pointer when you’re not using your mouse.

  -- This function will run once every time Awesome is started
  local function run_once(cmd_arr)
    for _, cmd in ipairs(cmd_arr) do
      awful.spawn.with_shell(string.format("pgrep -u $USER -fx '%s' > /dev/null || (%s)", cmd, cmd))
    end
  end

  local background_processes = {
    "unclutter -root",
  }

  run_once(background_processes)

  -- This function implements the XDG autostart specification
  awful.spawn.with_shell(
    'if (xrdb -query | grep -q "^awesome\\.started:\\s*true$"); then exit; fi;' ..
    'xrdb -merge <<< "awesome.started:true";' ..
    -- list each of your autostart commands, followed by ; inside single quotes, followed by ..
    'dex --environment Awesome --autostart --search-paths --"$XDG_CONFIG_DIRS/autostart:$XDG_CONFIG_HOME/autostart"'
  -- see https://github.com/jceb/dex
  )

Variable definitions

I found slock in suckless-tools.

  local themes = {
    "blackburn",       -- 1
    "copland",         -- 2
    "dremora",         -- 3
    "holo",            -- 4
    "multicolor",      -- 5
    "powerarrow",      -- 6
    "powerarrow-dark", -- 7
    "rainbow",         -- 8
    "steamburn",       -- 9
    "vertex",          -- 10
  }

  local chosen_theme = themes[5]
  local modkey       = "Mod4"
  local altkey       = "Mod1"
  local shiftkey     = "Shift"
  local controlkey   = "Control"
  local terminal     = "kitty"
  -- vi-like client focus
  -- https://github.com/lcpz/awesome-copycats/issues/275
  local vi_focus     = false
  -- cycle through all previous client or just the first
  -- https://github.com/lcpz/awesome-copycats/issues/274
  local cycle_prev   = true
  local editor       = os.getenv("EDITOR") or "vim"
  local gui_editor   = os.getenv("GUI_EDITOR") or "gvim"
  local browser      = os.getenv("BROWSER") or "firefox"
  local scrlocker    = "slock"

  awful.util.terminal = terminal
  awful.util.tagnames = { "1", "2", "3", "4", "5" }
  awful.layout.layouts = {
    awful.layout.suit.floating,
    awful.layout.suit.tile,
    awful.layout.suit.tile.left,
    awful.layout.suit.tile.bottom,
    awful.layout.suit.tile.top,
    awful.layout.suit.fair,
    awful.layout.suit.magnifier,
    awful.layout.suit.corner.sw,
    awful.layout.suit.corner.se,
    lain.layout.cascade,
    lain.layout.cascade.tile,
    lain.layout.centerwork,
    lain.layout.termfair,
    lain.layout.termfair.center,
  }

  awful.util.taglist_buttons = my_table.join(
    awful.button({ }, 1, function(t) t:view_only() end),
    awful.button({ modkey }, 1, function(t)
        if client.focus then
          client.focus:move_to_tag(t)
        end
    end),
    awful.button({ }, 3, awful.tag.viewtoggle),
    awful.button({ modkey }, 3, function(t)
        if client.focus then
          client.focus:toggle_tag(t)
        end
    end),
    awful.button({ }, 4, function(t) awful.tag.viewnext(t.screen) end),
    awful.button({ }, 5, function(t) awful.tag.viewprev(t.screen) end)
  )

  awful.util.tasklist_buttons = my_table.join(
    awful.button({ }, 1, function (c)
        if c == client.focus then
          c.minimized = true
        else
          --c:emit_signal("request::activate", "tasklist", {raise = true})<Paste>

          -- Without this, the following
          -- :isvisible() makes no sense
          c.minimized = false
          if not c:isvisible() and c.first_tag then
            c.first_tag:view_only()
          end
          -- This will also un-minimize
          -- the client, if needed
          client.focus = c
          c:raise()
        end
    end),
    awful.button({ }, 2, function (c) c:kill() end),
    awful.button({ }, 3, function ()
        local instance = nil

        return function ()
          if instance and instance.wibox.visible then
            instance:hide()
            instance = nil
          else
            instance = awful.menu.clients({theme = {width = dpi(250)}})
          end
        end
    end),
    awful.button({ }, 4, function () awful.client.focus.byidx(1) end),
    awful.button({ }, 5, function () awful.client.focus.byidx(-1) end)
  )

  lain.layout.termfair.nmaster           = 3
  lain.layout.termfair.ncol              = 1
  lain.layout.termfair.center.nmaster    = 3
  lain.layout.termfair.center.ncol       = 1
  lain.layout.cascade.tile.offset_x      = dpi(2)
  lain.layout.cascade.tile.offset_y      = dpi(32)
  lain.layout.cascade.tile.extra_padding = dpi(5)
  lain.layout.cascade.tile.nmaster       = 5
  lain.layout.cascade.tile.ncol          = 2

  beautiful.init(
    string.format("%s/.config/awesome/themes/%s/theme.lua", os.getenv("HOME"), chosen_theme)
  )

Better change that “edit config” menu option so it’s launching Emacs with this file. Sure, at some point I should be launching emacsclient, but one step at a time.

  local myawesomemenu = {
    { "hotkeys", function() return false, hotkeys_popup.show_help end },
    { "manual", terminal .. " -e man awesome" },
    { "edit config", "emacs ~/org/config/awesomewm.org" },
    { "restart", awesome.restart },
    { "quit", function() awesome.quit() end }
  }
  awful.util.mymainmenu = freedesktop.menu.build({
      icon_size = beautiful.menu_height or dpi(16),
      before = {
        { "Awesome", myawesomemenu, beautiful.awesome_icon },
        -- other triads can be put here
      },
      after = {
        { "Open terminal", terminal },
        -- other triads can be put here
      }
  })

Screen

  -- Re-set wallpaper when a screen's geometry changes (e.g. different resolution)
  screen.connect_signal(
    "property::geometry",
    function(s)
      -- Wallpaper
      if beautiful.wallpaper then
        local wallpaper = beautiful.wallpaper
        -- If wallpaper is a function, call it with the screen
        if type(wallpaper) == "function" then
          wallpaper = wallpaper(s)
        end
        gears.wallpaper.maximized(wallpaper, s, true)
      end
  end)

  -- No borders when rearranging only 1 non-floating or maximized client
  screen.connect_signal(
    "arrange",
    function (s)
      local only_one = #s.tiled_clients == 1
      for _, c in pairs(s.clients) do
        if only_one and not c.floating or c.maximized then
          c.border_width = 0
        else
          c.border_width = beautiful.border_width
        end
      end
  end)

  -- Create a wibox for each screen and add it
  awful.screen.connect_for_each_screen(function(s) beautiful.at_screen_connect(s) end)

Mouse bindings

  root.buttons(
    my_table.join(
      awful.button({ }, 3, function () awful.util.mymainmenu:toggle() end),
      awful.button({ }, 4, awful.tag.viewnext),
      awful.button({ }, 5, awful.tag.viewprev)
  ))

Key bindings

This chunk is big. It’s really really big. I broke it down into smaller chunks and subchunks. That doesn’t make it smaller, but it does make things easier to chew.

Global keys

Key bindings in effect at all times. I pick and choose from the available awesome-keys-global-* chunks.

  globalkeys = my_table.join(
    <<awesome-keys-global-screenshot>>
    <<awesome-keys-global-lock>>
    <<awesome-keys-global-hotkeys>>
    <<awesome-keys-global-tags-browse-all>>
    <<awesome-keys-global-tags-browse-nonempty>>
    <<awesome-keys-global-focus-byindex>>
    <<awesome-keys-global-layout>>
    <<awesome-keys-global-wibox>>
    <<awesome-keys-global-tagging>>
    <<awesome-keys-global-standard>>
    <<awesome-keys-global-dropdown>>
    <<awesome-keys-global-widgets>>
    <<awesome-keys-global-volume-alsa>>
    <<awesome-keys-global-media-mpd>>
    <<awesome-keys-global-clipboard>>
    <<awesome-keys-global-user>>
    <<awesome-keys-global-prompt>>
  )
Take a screenshot

Building on the example screenshot script at https://github.com/lcpz/dots/blob/master/bin/screenshot.

The screenshot script asks scrot to take a screenshot of the current root window, saving it to a timestamped file. Guaranteed to be more screenshot than I need 98% of the time, but it will do until I get the hang of things.

  timestamp="$(date +%Y-%m-%dT%H-%M-%S)"
  screenshot_file="screenshot-$timestamp.png"
  screenshot_dir="$HOME/Pictures"

  [ -d $screenshot_dir ] || exit 1

  scrot $screenshot_dir/$screenshot_file

  awful.key({ modkey }, "Print", function() os.execute("screenshot") end,
    {description = "take a screenshot", group = "hotkeys"}),
X screen locker

  awful.key({ modkey, controlkey }, "l", function () os.execute(scrlocker) end,
    {description = "lock screen", group = "hotkeys"}),
Hotkeys popup

  awful.key({ modkey,           }, "s",      hotkeys_popup.show_help,
    {description = "show help", group="awesome"}),
Tag browsing

  awful.key({ modkey,           }, "Left",   awful.tag.viewprev,
    {description = "view previous", group = "tag"}),
  awful.key({ modkey,           }, "Right",  awful.tag.viewnext,
    {description = "view next", group = "tag"}),
  awful.key({ modkey,           }, "Escape", awful.tag.history.restore,
    {description = "go back", group = "tag"}),
Non-empty tag browsing

  awful.key({ modkey, shiftkey }, "Left", function () lain.util.tag_view_nonempty(-1) end,
    {description = "view  previous nonempty", group = "tag"}),
  awful.key({ modkey, shiftkey }, "Right", function () lain.util.tag_view_nonempty(1) end,
    {description = "view  previous nonempty", group = "tag"}),
Default client focus

  awful.key({ modkey, }, "j",
    function ()
      awful.client.focus.byidx( 1)
    end,
    {description = "focus next by index", group = "client"}
  ),
  awful.key({ modkey, }, "k",
    function ()
      awful.client.focus.byidx(-1)
    end,
    {description = "focus previous by index", group = "client"}
  ),
By direction client focus

  awful.key({ modkey }, "j",
    function()
      awful.client.focus.global_bydirection("down")
      if client.focus then client.focus:raise() end
    end,
    {description = "focus down", group = "client"}),
  awful.key({ modkey }, "k",
    function()
      awful.client.focus.global_bydirection("up")
      if client.focus then client.focus:raise() end
    end,
    {description = "focus up", group = "client"}),
  awful.key({ modkey }, "h",
    function()
      awful.client.focus.global_bydirection("left")
      if client.focus then client.focus:raise() end
    end,
    {description = "focus left", group = "client"}),
  awful.key({ modkey }, "l",
    function()
      awful.client.focus.global_bydirection("right")
      if client.focus then client.focus:raise() end
    end,
    {description = "focus right", group = "client"}),
Layout manipulation

  awful.key({ modkey, "Shift"   }, "j", function () awful.client.swap.byidx(  1)    end,
    {description = "swap with next client by index", group = "client"}),
  awful.key({ modkey, "Shift"   }, "k", function () awful.client.swap.byidx( -1)    end,
    {description = "swap with previous client by index", group = "client"}),
  awful.key({ modkey, "Control" }, "j", function () awful.screen.focus_relative( 1) end,
    {description = "focus the next screen", group = "screen"}),
  awful.key({ modkey, "Control" }, "k", function () awful.screen.focus_relative(-1) end,
    {description = "focus the previous screen", group = "screen"}),
  awful.key({ modkey,           }, "u", awful.client.urgent.jumpto,
    {description = "jump to urgent client", group = "client"}),
  awful.key({ modkey,           }, "Tab",
    function ()
      if cycle_prev then
        awful.client.focus.history.previous()
      else
        awful.client.focus.byidx(-1)
      end
      if client.focus then
        client.focus:raise()
      end
    end,
    {description = "cycle with previous/go back", group = "client"}),
  awful.key({ modkey, "Shift"   }, "Tab",
    function ()
      if cycle_prev then
        awful.client.focus.byidx(1)
        if client.focus then
          client.focus:raise()
        end
      end
    end,
    {description = "go forth", group = "client"}),
Show / Hide Wibox

  awful.key({ modkey }, "b", function ()
      for s in screen do
        s.mywibox.visible = not s.mywibox.visible
        if s.mybottomwibox then
          s.mybottomwibox.visible = not s.mybottomwibox.visible
        end
      end
    end,
    {description = "toggle wibox", group = "awesome"}),
Dynamic tagging

  awful.key({ modkey, "Shift" }, "n", function () lain.util.add_tag() end,
    {description = "add new tag", group = "tag"}),
  awful.key({ modkey, "Shift" }, "r", function () lain.util.rename_tag() end,
    {description = "rename tag", group = "tag"}),
  awful.key({ modkey, "Shift" }, "Left", function () lain.util.move_tag(-1) end,
    {description = "move tag to the left", group = "tag"}),
  awful.key({ modkey, "Shift" }, "Right", function () lain.util.move_tag(1) end,
    {description = "move tag to the right", group = "tag"}),
  awful.key({ modkey, "Shift" }, "d", function () lain.util.delete_tag() end,
    {description = "delete tag", group = "tag"}),
Standard program

  awful.key({ modkey,           }, "Return", function () awful.spawn(terminal) end,
    {description = "open a terminal", group = "launcher"}),
  awful.key({ modkey, "Control" }, "r", awesome.restart,
    {description = "reload awesome", group = "awesome"}),
  awful.key({ modkey, "Shift"   }, "q", awesome.quit,
    {description = "quit awesome", group = "awesome"}),

  awful.key({ altkey, "Shift"   }, "l",     function () awful.tag.incmwfact( 0.05)          end,
    {description = "increase master width factor", group = "layout"}),
  awful.key({ altkey, "Shift"   }, "h",     function () awful.tag.incmwfact(-0.05)          end,
    {description = "decrease master width factor", group = "layout"}),
  awful.key({ modkey, "Shift"   }, "h",     function () awful.tag.incnmaster( 1, nil, true) end,
    {description = "increase the number of master clients", group = "layout"}),
  awful.key({ modkey, "Shift"   }, "l",     function () awful.tag.incnmaster(-1, nil, true) end,
    {description = "decrease the number of master clients", group = "layout"}),
  awful.key({ modkey, "Control" }, "h",     function () awful.tag.incncol( 1, nil, true)    end,
    {description = "increase the number of columns", group = "layout"}),
  awful.key({ modkey, "Control" }, "l",     function () awful.tag.incncol(-1, nil, true)    end,
    {description = "decrease the number of columns", group = "layout"}),
  awful.key({ modkey,           }, "space", function () awful.layout.inc( 1)                end,
    {description = "select next", group = "layout"}),
  awful.key({ modkey, "Shift"   }, "space", function () awful.layout.inc(-1)                end,
    {description = "select previous", group = "layout"}),

  awful.key({ modkey, "Control" }, "n",
    function ()
      local c = awful.client.restore()
      -- Focus restored client
      if c then
        client.focus = c
        c:raise()
      end
    end,
    {description = "restore minimized", group = "client"}),

  awful.key({ modkey,           }, "w", function () awful.util.mymainmenu:show() end,
    {description = "show main menu", group = "awesome"}),

  awful.key({ modkey, }, "z", function () awful.screen.focused().quake:toggle() end,
    {description = "dropdown application", group = "launcher"}),
Widgets popups

  awful.key({ altkey, }, "c", function () if beautiful.cal then beautiful.cal.show(7) end end,
    {description = "show calendar", group = "widgets"}),
  awful.key({ altkey, }, "h", function () if beautiful.fs then beautiful.fs.show(7) end end,
    {description = "show filesystem", group = "widgets"}),
  awful.key({ altkey, }, "w",
    function () if beautiful.weather then beautiful.weather.show(7) end end,
    {description = "show weather", group = "widgets"}),
Brightness

I might find this more useful if any of my monitors had a backlight property.

  awful.key({ }, "XF86MonBrightnessUp", function () os.execute("xbacklight -inc 10") end,
    {description = "+10%", group = "hotkeys"}),
  awful.key({ }, "XF86MonBrightnessDown", function () os.execute("xbacklight -dec 10") end,
    {description = "-10%", group = "hotkeys"}),
ALSA volume control

  awful.key({ altkey }, "Up",
    function ()
      os.execute(string.format("amixer -q set %s 1%%+", beautiful.volume.channel))
      beautiful.volume.update()
    end,
    {description = "volume up", group = "hotkeys"}),
  awful.key({ altkey }, "Down",
    function ()
      os.execute(string.format("amixer -q set %s 1%%-", beautiful.volume.channel))
      beautiful.volume.update()
    end,
    {description = "volume down", group = "hotkeys"}),
  awful.key({ altkey }, "m",
    function ()
      os.execute(
        string.format(
          "amixer -q set %s toggle",
          beautiful.volume.togglechannel or beautiful.volume.channel
      ))
      beautiful.volume.update()
    end,
    {description = "toggle mute", group = "hotkeys"}),
  awful.key({ altkey, "Control" }, "m",
    function ()
      os.execute(string.format("amixer -q set %s 100%%", beautiful.volume.channel))
      beautiful.volume.update()
    end,
    {description = "volume 100%", group = "hotkeys"}),
  awful.key({ altkey, "Control" }, "0",
    function ()
      os.execute(string.format("amixer -q set %s 0%%", beautiful.volume.channel))
      beautiful.volume.update()
    end,
    {description = "volume 0%", group = "hotkeys"}),
MPD control

  awful.key({ altkey, "Control" }, "Up",
    function ()
      os.execute("mpc toggle")
      beautiful.mpd.update()
    end,
    {description = "mpc toggle", group = "widgets"}),
  awful.key({ altkey, "Control" }, "Down",
    function ()
      os.execute("mpc stop")
      beautiful.mpd.update()
    end,
    {description = "mpc stop", group = "widgets"}),
  awful.key({ altkey, "Control" }, "Left",
    function ()
      os.execute("mpc prev")
      beautiful.mpd.update()
    end,
    {description = "mpc prev", group = "widgets"}),
  awful.key({ altkey, "Control" }, "Right",
    function ()
      os.execute("mpc next")
      beautiful.mpd.update()
    end,
    {description = "mpc next", group = "widgets"}),
  awful.key({ altkey }, "0",
    function ()
      local common = { text = "MPD widget ", position = "top_middle", timeout = 2 }
      if beautiful.mpd.timer.started then
        beautiful.mpd.timer:stop()
        common.text = common.text .. lain.util.markup.bold("OFF")
      else
        beautiful.mpd.timer:start()
        common.text = common.text .. lain.util.markup.bold("ON")
      end
      naughty.notify(common)
    end,
    {description = "mpc on/off", group = "widgets"}),
Clipboard

Transferring terminal clipboard to and from the GTK clipboard.

  awful.key({ modkey }, "c", function () awful.spawn.with_shell("xsel | xsel -i -b") end,
    {description = "copy terminal to gtk", group = "hotkeys"}),
  awful.key({ modkey }, "v", function () awful.spawn.with_shell("xsel -b | xsel") end,
      {description = "copy gtk to terminal", group = "hotkeys"}),
User programs

  awful.key({ modkey }, "q", function () awful.spawn(browser) end,
    {description = "run browser", group = "launcher"}),
  awful.key({ modkey }, "a", function () awful.spawn(gui_editor) end,
    {description = "run gui editor", group = "launcher"}),
Prompt

  awful.key({ modkey }, "r", function () awful.screen.focused().mypromptbox:run() end,
    {description = "run prompt", group = "launcher"}),

  awful.key({ modkey }, "x",
    function ()
      awful.prompt.run {
        prompt       = "Run Lua code: ",
        textbox      = awful.screen.focused().mypromptbox.widget,
        exe_callback = awful.util.eval,
        history_path = awful.util.get_cache_dir() .. "/history_eval"
      }
    end,
    {description = "lua execute prompt", group = "awesome"})
Default

Disabled in rc.lua.template. I uncommented the code bits but haven’t included it in my menu definition.

  -- Menubar
  awful.key({ modkey }, "p", function() menubar.show() end,
    {description = "show the menubar", group = "launcher"})

  -- dmenu
  awful.key({ modkey }, "x", function ()
      os.execute(
        string.format(
          "dmenu_run -i -fn 'Monospace' -nb '%s' -nf '%s' -sb '%s' -sf '%s'",
          beautiful.bg_normal, beautiful.fg_normal, beautiful.bg_focus, beautiful.fg_focus
        )
      )
    end,
    {description = "show dmenu", group = "launcher"})

  -- alternatively use rofi, a dmenu-like application with more features
  -- check https://github.com/DaveDavenport/rofi for more details
  -- rofi
  awful.key({ modkey }, "x", function ()
      os.execute(
        string.format("rofi -show %s -theme %s", 'run', 'dmenu')
      )
    end,
    {description = "show rofi", group = "launcher"}),

Client keys

Keys for managing the client — whatever has focus right now.

  clientkeys = my_table.join(
    awful.key({ altkey, "Shift"   }, "m",      lain.util.magnify_client,
      {description = "magnify client", group = "client"}),
    awful.key({ modkey,           }, "f",
      function (c)
        c.fullscreen = not c.fullscreen
        c:raise()
      end,
      {description = "toggle fullscreen", group = "client"}),
    awful.key({ modkey, "Shift"   }, "c",      function (c) c:kill()                         end,
      {description = "close", 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"}),
    awful.key({ modkey,           }, "o",      function (c) c:move_to_screen()               end,
      {description = "move to screen", group = "client"}),
    awful.key({ modkey,           }, "t",      function (c) c.ontop = not c.ontop            end,
      {description = "toggle keep on top", group = "client"}),
    awful.key({ modkey,           }, "n",
      function (c)
        -- The client currently has the input focus, so it cannot be
        -- minimized, since minimized clients can't have the focus.
        c.minimized = true
      end ,
      {description = "minimize", group = "client"}),
    awful.key({ modkey,           }, "m",
      function (c)
        c.maximized = not c.maximized
        c:raise()
      end ,
      {description = "maximize", group = "client"})
  )

Tag keys

Key bindings to manage your tags. That’s what AwesomeWM calls its workspaces.

Bind all key numbers to tags. Be careful: we use keycodes to make it works on any keyboard layout. This should map on the top row of your keyboard, usually 1 to 9.

  for i = 1, 9 do
    -- Hack to only show tags 1 and 9 in the shortcut window (mod+s)
    local descr_view, descr_toggle, descr_move, descr_toggle_focus
    if i == 1 or i == 9 then
      descr_view = {description = "view tag #", group = "tag"}
      descr_toggle = {description = "toggle tag #", group = "tag"}
      descr_move = {description = "move focused client to tag #", group = "tag"}
      descr_toggle_focus = {description = "toggle focused client on tag #", group = "tag"}
    end
    globalkeys = my_table.join(
      globalkeys,
      -- View tag only.
      awful.key({ modkey }, "#" .. i + 9,
        function ()
          local screen = awful.screen.focused()
          local tag = screen.tags[i]
          if tag then
            tag:view_only()
          end
        end,
        descr_view),
      -- Toggle tag display.
      awful.key({ modkey, "Control" }, "#" .. i + 9,
        function ()
          local screen = awful.screen.focused()
          local tag = screen.tags[i]
          if tag then
            awful.tag.viewtoggle(tag)
          end
        end,
        descr_toggle),
      -- Move client to tag.
      awful.key({ modkey, "Shift" }, "#" .. i + 9,
        function ()
          if client.focus then
            local tag = client.focus.screen.tags[i]
            if tag then
              client.focus:move_to_tag(tag)
            end
          end
        end,
        descr_move),
      -- Toggle tag on focused client.
      awful.key({ modkey, "Control", "Shift" }, "#" .. i + 9,
        function ()
          if client.focus then
            local tag = client.focus.screen.tags[i]
            if tag then
              client.focus:toggle_tag(tag)
            end
          end
        end,
        descr_toggle_focus)
    )
  end

Client buttons

Use the keyboard to send mouse clicks.

  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})
        awful.mouse.client.move(c)
    end),
    awful.button({ modkey }, 3, function (c)
        c:emit_signal("request::activate", "mouse_click", {raise = true})
        awful.mouse.client.resize(c)
    end)
  )

Putting it all together

  <<awesome-keys-global>>
  <<awesome-keys-client>>
  <<awesome-keys-tags>>
  <<awesome-keys-clientbuttons>>

  -- Set keys
  root.keys(globalkeys)

Rules

  -- Rules to apply to new clients (through the "manage" signal).
  awful.rules.rules = {
    -- All clients will match this rule.
    { rule = { },
      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,
        size_hints_honor = false
      }
    },

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

    { rule = { class = "Emacs" },
      properties = { screen = 2, tag = awful.util.tagnames[1] } },

    -- Set Firefox to always map on the first tag on screen 1.
    { rule = { class = "Firefox" },
      properties = { screen = 1, tag = awful.util.tagnames[1] } },

    { rule = { class = "Gimp", role = "gimp-image-window" },
      properties = { maximized = true } },
  }

Signals

  -- Signal function to execute when a new client appears.
  client.connect_signal(
    "manage",
    function (c)
      -- Set the windows at the slave,
      -- i.e. put it at the end of others instead of setting it master.
      -- if not awesome.startup then awful.client.setslave(c) end

      if awesome.startup and
        not c.size_hints.user_position
        and not c.size_hints.program_position then
        -- Prevent clients from being unreachable after screen count changes.
        awful.placement.no_offscreen(c)
      end
  end)

  -- Add a titlebar if titlebars_enabled is set to true in the rules.
  client.connect_signal(
    "request::titlebars",
    function(c)
      -- Custom
      if beautiful.titlebar_fun then
        beautiful.titlebar_fun(c)
        return
      end

      -- Default
      -- buttons for the titlebar
      local buttons = my_table.join(
        awful.button({ }, 1, function()
            c:emit_signal("request::activate", "titlebar", {raise = true})
            awful.mouse.client.move(c)
        end),
        awful.button({ }, 2, function() c:kill() end),
        awful.button({ }, 3, function()
            c:emit_signal("request::activate", "titlebar", {raise = true})
            awful.mouse.client.resize(c)
        end)
      )

      awful.titlebar(c, {size = dpi(16)}) : setup {
        { -- Left
          awful.titlebar.widget.iconwidget(c),
          buttons = buttons,
          layout  = wibox.layout.fixed.horizontal
        },
        { -- Middle
          { -- Title
            align  = "center",
            widget = awful.titlebar.widget.titlewidget(c)
          },
          buttons = buttons,
          layout  = wibox.layout.flex.horizontal
        },
        { -- Right
          awful.titlebar.widget.floatingbutton (c),
          awful.titlebar.widget.maximizedbutton(c),
          awful.titlebar.widget.stickybutton   (c),
          awful.titlebar.widget.ontopbutton    (c),
          awful.titlebar.widget.closebutton    (c),
          layout = wibox.layout.fixed.horizontal()
        },
        layout = wibox.layout.align.horizontal
                                                  }
  end)

  -- Enable sloppy focus, so that focus follows mouse.
  client.connect_signal(
    "mouse::enter",
    function(c)
      c:emit_signal("request::activate", "mouse_enter", {raise = vi_focus})
    end
  )

  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)

  -- possible workaround for tag preservation when switching back to default screen:
  -- https://github.com/lcpz/awesome-copycats/issues/251

autorun

  #!/usr/bin/env bash

  # stricter bash
  #  see http://redsymbol.net/articles/unofficial-bash-strict-mode/
  set -euo pipefail
  IFS=$'\n\t'

  function run {
    if ! pgrep -f $1 ;
    then
      $@&
    fi
  }

  run dropbox
  #run setxkbmap -option compose:ralt
  run xrandr --output DisplayPort-1 --mode 2560x1440 --rate 59.95
  if [ -f "~/.Xmodmap" ]; then
      xmodmap ~/.Xmodmap
  fi

  #run xcape -e 'Control_L=Escape'

Resources