Skip to main content

Your first config

This page writes a kiln config from an empty file. At the end you have three workspaces, a bar with a clock, terminals that tile themselves, and keys to open, close, switch and quit. The default config does far more, and Anatomy of rc.lua reads it; this page is for seeing the parts one at a time.

Two terminals tiled under a bar showing three tags, two tasks and a clock

You need a built kiln (see Installation) and a Wayland desktop to run it inside. Every step below runs kiln as a window in your current session, so nothing here needs a logout.

1. A screen, a tag and two keys​

Save this as rc.lua somewhere outside your checkout:

local kiln = require("kiln")
kiln.modkey = "super"

screen.on("added", function(s)
tag.new { name = "1", screen = s }
end)

kiln.key { mods = { "mod" }, key = "Return", press = function() kiln.spawn("foot") end }
kiln.key { mods = { "mod", "ctrl" }, key = "r", press = kiln.reload }
kiln.key { mods = { "mod", "shift" }, key = "q", press = kiln.quit }

Run it from the kiln checkout, with the path to your file:

kiln_demo=$(mktemp -d /tmp/kiln-demo.XXXXXX)
KILN_RC=/path/to/rc.lua KILN_SOCK="$kiln_demo/eval.sock" \
dbus-run-session -- ./build/kiln

A window titled kiln opens, empty. Click into it so it has your keyboard, then press super+Return twice. The first terminal fills the window; the second splits it into two columns.

What each line did:

  • screen.on("added", fn) runs fn for every monitor kiln finds, now and later. Everything that belongs to a screen is created here.
  • tag.new makes a workspace on that screen. A screen shows one or more tags; the tag holds its windows and the layout that arranges them. The layout defaults to tile, which is why the two terminals became columns with nothing in the file saying so.
  • kiln.key binds a chord. "mod" stands for whatever kiln.modkey is, so one line changes every binding. The handler is an ordinary function.
  • kiln.spawn("foot") starts a program. Replace foot with your terminal.

The titlebars and borders on the terminals come from the standard library's defaults; the default config replaces them with its own.

2. A bar​

Add this inside the screen.on("added") function, after tag.new, and press super+ctrl+r to reload:

  ui.bar(s, { edge = "top" }, function()
widgets.taglist(s)
ui.spacer()
widgets.clock()
end)

and these two shortcuts near the top of the file, after local kiln:

local ui, widgets = kiln.ui, kiln.widgets

A bar appears along the top and the terminals move down to make room. Its function lists what the bar contains, left to right: the tag list, then a spacer, then the clock. ui.spacer() takes all the width the others leave, so the clock sits at the right edge. Layout with Clay has the four sizing rules every element uses.

Kiln calls this function whenever the screen needs redrawing (a window opened, a tag changed, the minute rolled over). It does not keep bar objects that you update; it asks again. widgets.taglist and widgets.clock are functions that declare their boxes the same way, written with the same API this file uses.

Add widgets.tasklist(s) after the taglist and reload: one entry per open window, the focused one highlighted.

3. More tags, and keys to reach them​

Replace the single tag.new with a loop, and bind the number keys:

  for _, name in ipairs({ "1", "2", "3" }) do
tag.new { name = name, screen = s }
end
kiln.key { mods = { "mod" }, key = "1-3", press = function(i)
local t = screen.focused.tags[i]
if t then t:view() end
end }

A key of the form "1-3" binds each key in the range and passes the pressed number to the handler. t:view() makes that tag the one the screen shows. Reload, press super+2: the terminals disappear (they live on tag 1), and the bar's tag list marks tag 1 as occupied.

4. Close a window, change the layout​

kiln.key { mods = { "mod" }, key = "q", press = kiln.focused(function(c) c:close() end) }
kiln.key { mods = { "mod" }, key = "space", press = function()
local t = screen.focused.selected_tag
if t then t.layout = kiln.layout.next(t.layout) end
end }

kiln.focused(fn) wraps a handler so it receives the focused window and does nothing when there is none. c:close() asks the application to close.

The layout is a property of the tag. Writing t.layout redraws the screen; there is no arrange call. kiln.layout.next walks the stock layouts in order, so repeated presses of super+space move through tile, fair, max and the rest. Open three terminals and watch them rearrange.

The whole file​

local kiln = require("kiln")
local ui, widgets = kiln.ui, kiln.widgets
kiln.modkey = "super"

screen.on("added", function(s)
for _, name in ipairs({ "1", "2", "3" }) do
tag.new { name = name, screen = s }
end
ui.bar(s, { edge = "top" }, function()
widgets.taglist(s)
widgets.tasklist(s)
ui.spacer()
widgets.clock()
end)
end)

kiln.key { mods = { "mod" }, key = "Return", press = function() kiln.spawn("foot") end }
kiln.key { mods = { "mod" }, key = "q", press = kiln.focused(function(c) c:close() end) }
kiln.key { mods = { "mod" }, key = "space", press = function()
local t = screen.focused.selected_tag
if t then t.layout = kiln.layout.next(t.layout) end
end }
kiln.key { mods = { "mod" }, key = "1-3", press = function(i)
local t = screen.focused.tags[i]
if t then t:view() end
end }
kiln.key { mods = { "mod", "ctrl" }, key = "r", press = kiln.reload }
kiln.key { mods = { "mod", "shift" }, key = "q", press = kiln.quit }

Press super+shift+q to quit the nested window.

What this file does not do​

require("kiln") installs default policies, client borders and titlebars, and the notification display. This file adds tags, a bar and keybindings.

It adds no floating-window rules, mouse move/resize bindings, theme overrides, menu bindings or launcher binding. The bundled config provides those: