Skip to main content
Version: 1.4

Keybindings

You will learn

  • The four parts of an awful.key binding
  • How to add global and per-client keybindings
  • How to bind media keys and mouse buttons
  • How to organize bindings into reusable tables
Building a full config?

This page retrofits one feature into an existing config. If you would rather build a complete desktop from the ground up, the Awesome From Scratch series covers this topic as part of a thirteen-chapter course.

Keybinding Anatomy​

Every keybinding in SomeWM has four parts:

awful.key(
{ "Mod4" }, -- 1. Modifiers (table)
"Return", -- 2. Key
function() -- 3. Callback function
awful.spawn(terminal)
end,
{ description = "open a terminal", group = "launcher" } -- 4. Metadata
)
PartDescription
ModifiersTable of modifier keys: "Mod4", "Shift", "Control", "Mod1" (Alt)
KeyThe key name: "Return", "space", "a", "F1", etc.
CallbackFunction to execute when pressed
MetadataDescription and group for the help popup

Adding a Global Keybinding​

Global keybindings work anywhere, regardless of which window is focused.

Open your rc.lua and find the keybindings section. Add a new keybinding:

awful.keyboard.append_global_keybindings({
-- Your existing keybindings...

-- Add a new one:
awful.key({ modkey }, "b", function()
awful.spawn("firefox")
end, { description = "open browser", group = "launcher" }),
})

Press Mod4 + Ctrl + r to reload, then Mod4 + b to open Firefox.

Adding a Client Keybinding​

Client keybindings only work when a window is focused, and the focused window (c) is passed to your callback.

Find the client keybindings section in your rc.lua:

client.connect_signal("request::default_keybindings", function()
awful.keyboard.append_client_keybindings({
-- Existing keybindings...

-- Toggle window opacity:
awful.key({ modkey }, "o", function(c)
if c.opacity == 1 then
c.opacity = 0.8
else
c.opacity = 1
end
end, { description = "toggle opacity", group = "client" }),
})
end)

Now Mod4 + o will toggle transparency on the focused window.

Key Names Reference​

For a complete list of modifier keys, key names, and mouse buttons, see the Key Names Reference.

Common modifiers: "Mod4" (Super), "Mod1" (Alt), "Shift", "Control"

Common keys: "Return" (Enter), "space", "Tab", "Escape", "Left"/"Right"/"Up"/"Down" (arrows)

Media Keys​

Media keys work without modifiers:

awful.keyboard.append_global_keybindings({
-- Volume controls
awful.key({}, "XF86AudioRaiseVolume", function()
awful.spawn("wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+")
end, { description = "raise volume", group = "media" }),

awful.key({}, "XF86AudioLowerVolume", function()
awful.spawn("wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-")
end, { description = "lower volume", group = "media" }),

awful.key({}, "XF86AudioMute", function()
awful.spawn("wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle")
end, { description = "toggle mute", group = "media" }),

-- Brightness controls
awful.key({}, "XF86MonBrightnessUp", function()
awful.spawn("brightnessctl s +5%")
end, { description = "increase brightness", group = "media" }),

awful.key({}, "XF86MonBrightnessDown", function()
awful.spawn("brightnessctl s 5%-")
end, { description = "decrease brightness", group = "media" }),
})

Organizing with Tables​

-- Define keybindings as data
local my_global_keys = {
-- { modifiers, key, callback, description, group }
{ { modkey }, "b", function() awful.spawn("firefox") end, "browser", "launcher" },
{ { modkey }, "e", function() awful.spawn("thunar") end, "file manager", "launcher" },
{ { modkey, "Shift" }, "s", function() awful.spawn("flameshot gui") end, "screenshot", "utility" },
}

-- Convert table entries to keybindings
local function make_keybindings(definitions)
local keys = {}
for _, def in ipairs(definitions) do
table.insert(keys, awful.key(
def[1], -- modifiers
def[2], -- key
def[3], -- callback
{ description = def[4], group = def[5] }
))
end
return keys
end

-- Apply them
awful.keyboard.append_global_keybindings(make_keybindings(my_global_keys))

Helper Functions​

For common patterns, create helper functions:

local helpers = {
-- Toggle a client property
toggle_property = function(property)
return function(c)
c[property] = not c[property]
c:raise()
end
end,

-- Spawn a command
spawn = function(cmd)
return function()
awful.spawn(cmd)
end
end,
}

-- Use them in keybindings:
awful.keyboard.append_client_keybindings({
awful.key({ modkey }, "f", helpers.toggle_property("fullscreen"),
{ description = "toggle fullscreen", group = "client" }),

awful.key({ modkey }, "m", helpers.toggle_property("maximized"),
{ description = "toggle maximized", group = "client" }),
})

awful.keyboard.append_global_keybindings({
awful.key({ modkey }, "b", helpers.spawn("firefox"),
{ description = "browser", group = "launcher" }),
})

The Hotkeys Popup​

Press Mod4 + s to see all your keybindings organized by group.

The popup automatically includes your keybindings if you set the description and group metadata.

Customizing the Popup​

You can style the popup through theme variables:

-- In your theme.lua
theme.hotkeys_bg = "#282828"
theme.hotkeys_fg = "#ebdbb2"
theme.hotkeys_border_color = "#fabd2f"
theme.hotkeys_modifiers_fg = "#fe8019"
theme.hotkeys_label_bg = "#b8bb26"

Numrow Keybindings​

For tag switching (1-9), use the declarative pattern with keygroup:

awful.keyboard.append_global_keybindings({
awful.key({
modifiers = { modkey },
keygroup = "numrow",
description = "view tag",
group = "tag",
on_press = function(index)
local screen = awful.screen.focused()
local tag = screen.tags[index]
if tag then
tag:view_only()
end
end,
}),

awful.key({
modifiers = { modkey, "Shift" },
keygroup = "numrow",
description = "move client to tag",
group = "tag",
on_press = function(index)
if client.focus then
local tag = client.focus.screen.tags[index]
if tag then
client.focus:move_to_tag(tag)
end
end
end,
}),
})

The keygroup = "numrow" automatically binds to keys 1-9.

Mouse Bindings​

Mouse bindings work similarly, using awful.button:

client.connect_signal("request::default_mousebindings", function()
awful.mouse.append_client_mousebindings({
-- Left click to focus
awful.button({}, 1, function(c)
c:activate({ context = "mouse_click" })
end),

-- Mod4 + Left click to move
awful.button({ modkey }, 1, function(c)
c:activate({ context = "mouse_click", action = "mouse_move" })
end),

-- Mod4 + Right click to resize
awful.button({ modkey }, 3, function(c)
c:activate({ context = "mouse_click", action = "mouse_resize" })
end),
})
end)

Button numbers:

  • 1 = Left click
  • 2 = Middle click
  • 3 = Right click
  • 4 = Scroll up
  • 5 = Scroll down

Complete Example​

Here's a complete keybindings module you could save as keybindings.lua:

-- keybindings.lua
local awful = require("awful")
local hotkeys_popup = require("awful.hotkeys_popup")

-- Helper to convert table format to keybindings
local function make_keys(definitions)
local keys = {}
for _, def in ipairs(definitions) do
table.insert(keys, awful.key(def[1], def[2], def[3],
{ description = def[4], group = def[5] }))
end
return keys
end

-- Global keybindings
local global_keys = {
-- Launchers
{ { modkey }, "Return", function() awful.spawn(terminal) end, "terminal", "launcher" },
{ { modkey }, "b", function() awful.spawn("firefox") end, "browser", "launcher" },
{ { modkey }, "e", function() awful.spawn("thunar") end, "file manager", "launcher" },
{ { modkey }, "r", function() awful.screen.focused().mypromptbox:run() end, "run prompt", "launcher" },

-- Awesome
{ { modkey }, "s", hotkeys_popup.show_help, "show help", "awesome" },
{ { modkey, "Control"}, "r", awesome.restart, "reload", "awesome" },
{ { modkey, "Shift" }, "q", awesome.quit, "quit", "awesome" },

-- Layout
{ { modkey }, "space", function() awful.layout.inc(1) end, "next layout", "layout" },
{ { modkey }, "h", function() awful.tag.incmwfact(-0.05) end, "shrink master", "layout" },
{ { modkey }, "l", function() awful.tag.incmwfact(0.05) end, "grow master", "layout" },

-- Focus
{ { modkey }, "j", function() awful.client.focus.byidx(1) end, "next window", "client" },
{ { modkey }, "k", function() awful.client.focus.byidx(-1) end, "prev window", "client" },
{ { modkey }, "Tab", function()
awful.client.focus.history.previous()
if client.focus then client.focus:raise() end
end, "last window", "client" },
}

-- Client keybindings
local client_keys = {
{ { modkey }, "f", function(c) c.fullscreen = not c.fullscreen; c:raise() end, "fullscreen", "client" },
{ { modkey }, "m", function(c) c.maximized = not c.maximized; c:raise() end, "maximize", "client" },
{ { modkey }, "n", function(c) c.minimized = true end, "minimize", "client" },
{ { modkey, "Shift" }, "c", function(c) c:kill() end, "close", "client" },
{ { modkey, "Control"}, "space", awful.client.floating.toggle, "float", "client" },
}

-- Apply global keybindings
awful.keyboard.append_global_keybindings(make_keys(global_keys))

-- Apply client keybindings
client.connect_signal("request::default_keybindings", function()
awful.keyboard.append_client_keybindings(make_keys(client_keys))
end)

-- Numrow tag switching (can't use table format for keygroups)
awful.keyboard.append_global_keybindings({
awful.key({
modifiers = { modkey },
keygroup = "numrow",
description = "view tag",
group = "tag",
on_press = function(index)
local tag = awful.screen.focused().tags[index]
if tag then tag:view_only() end
end,
}),
awful.key({
modifiers = { modkey, "Shift" },
keygroup = "numrow",
description = "move to tag",
group = "tag",
on_press = function(index)
if client.focus then
local tag = client.focus.screen.tags[index]
if tag then client.focus:move_to_tag(tag) end
end
end,
}),
})

Then in your rc.lua:

require("keybindings")

Troubleshooting​

Keybinding not working​

  1. Check for typos in key names (they're case-sensitive for modifiers)
  2. Look for conflicts with existing keybindings
  3. Make sure you reloaded config with Mod4 + Ctrl + r

Key name not found​

Use xev (X11) or check the Default Keybindings reference for correct key names.

Callback errors​

Check the notification for error messages. Common issues:

  • Missing local for variables
  • Typos in function names
  • Missing end for functions

Next Steps​