The Object Model
Understanding SomeWM's object model is the foundation for everything else. Once you grasp how outputs, screens, tags, clients, and widgets relate, configuration becomes intuitive.
The Core Objects
Output somewm-only
An output represents a physical monitor connector (HDMI-A-1, DP-2, eDP-1). Output objects persist from the moment a monitor is plugged in until it is physically disconnected, even if the display is temporarily disabled.
-- Iterate outputs
for o in output do
print(o.name, o.make, o.enabled)
end
-- Find by connector name
local hdmi = output.get_by_name("HDMI-A-1")
-- Access from a screen
local o = screen.primary.output
Each output has:
- Hardware info - make, model, serial, physical size
- Display state - scale, transform, mode, position, enabled
- Screen link - the associated screen object (nil when disabled)
The distinction from screens: a screen is destroyed when a monitor is disabled and recreated when re-enabled. The output object survives that cycle, making it a stable key for per-monitor configuration.
Screen
A screen represents a physical or virtual display. If you have two monitors, you have two screens.
-- Get the primary screen
local s = screen.primary
-- Get all screens
for s in screen do
print(s.index, s.geometry.width .. "x" .. s.geometry.height)
end
-- Get the currently focused screen
local focused = awful.screen.focused()
Each screen has:
- Geometry - position and size (
s.geometry) - Workarea - usable space after subtracting panels (
s.workarea) - Tags - the virtual workspaces for this screen
- Wibars - the panels attached to this screen
Tag
A tag is a virtual workspace. Windows are assigned to tags, and you view tags to see those windows. Unlike traditional workspaces, a window can belong to multiple tags simultaneously.
-- Get tags for a screen
local tags = screen.primary.tags
-- View a specific tag
tags[2]:view_only()
-- Toggle a tag's visibility (show multiple tags at once)
awful.tag.viewtoggle(tags[3])
-- Create tags for a screen
awful.tag({"1", "2", "3", "web", "code"}, s, awful.layout.suit.tile)
Key tag concepts:
- Selected tags are currently visible on their screen
- A screen can show multiple selected tags at once
- Each screen has its own independent set of tags
Client
A client is a window - any application window you can see and interact with. Terminals, browsers, editors - they're all clients.
-- Get the focused client
local c = client.focus
-- Get all clients
for _, c in ipairs(client.get()) do
print(c.name)
end
-- Common client properties
c.floating = true -- float above tiled windows
c.maximized = true -- fill the screen
c.minimized = true -- hide from view
c.ontop = true -- stay above other windows
Clients belong to:
- One screen - the display they're on
- One or more tags - the workspaces they appear in
Clients also have ordering in:
- The stack - z-order for visual overlap
- Focus history - determines what gets focus when you close a window
Wibox and Wibar
A wibox is a box that holds widgets. A wibar is a wibox that's docked to a screen edge (top, bottom, left, or right).
-- Create a wibar at the top of a screen
s.mywibar = awful.wibar {
position = "top",
screen = s,
}
-- Create a floating wibox anywhere
local popup = wibox {
width = 200,
height = 100,
visible = true,
}
Widget
A widget is a visual element inside a wibox. Widgets come in three flavors:
| Type | Purpose | Examples |
|---|---|---|
| Primitives | Display content | textbox, imagebox, progressbar |
| Containers | Modify one widget | background, margin, constraint |
| Layouts | Arrange multiple widgets | fixed, flex, align |
Widgets compose by nesting:
-- A textbox inside a margin inside a background
wibox.widget {
{
{
text = "Hello",
widget = wibox.widget.textbox,
},
margins = 8,
widget = wibox.container.margin,
},
bg = "#333333",
widget = wibox.container.background,
}
The Hierarchy
Here's how everything fits together:
Output "HDMI-A-1" Output "DP-2"
└── Screen 1 └── Screen 2
├── Tag "1" (selected) ├── Tag "1"
│ ├── Client: Firefox │ └── Client: Slack
│ └── Client: Terminal │
├── Tag "2" ├── Tag "2" (selected)
│ └── Client: VS Code │ └── Client: Spotify
├── Tag "3" (empty) │
│ └── Wibar (top)
└── Wibar (top) └── [widgets...]
├── Taglist widget
├── Tasklist widget
└── Clock widget
Key points:
- Each output has zero or one screen (zero when disabled)
- Each screen has its own tags and wibars
- Each tag belongs to exactly one screen
- Each client belongs to one screen but can have multiple tags
- Wibars are attached to a screen edge
- Widgets live inside wibars (or standalone wiboxes)
Focus vs Selection
These terms are easy to confuse but mean different things:
| Term | Applies to | Meaning |
|---|---|---|
| Focused | Clients | Receives keyboard input. Only ONE client is focused globally. |
| Selected | Tags | Currently visible. Multiple tags can be selected per screen. |
-- The focused client (receives keyboard input)
local c = client.focus
-- Selected tags on a screen (currently visible)
local selected = screen.primary.selected_tags
-- The focused screen (where the focused client is)
local s = awful.screen.focused()
Focus flow
When you type, keystrokes go to:
- The focused client (if any)
- Global keybindings (if not consumed by the client)
The focused client is always on a selected tag of some screen.
Common Operations
Now that you understand the objects, here's how common operations map to them:
| "I want to..." | Object | API |
|---|---|---|
| Switch workspace | Tag | tag:view_only() |
| View multiple workspaces | Tag | awful.tag.viewtoggle(tag) |
| Move window to workspace | Client + Tag | c:move_to_tag(tag) |
| Move window to screen | Client + Screen | c:move_to_screen(s) |
| Close window | Client | c:kill() |
| Toggle floating | Client | c.floating = not c.floating |
| Add widget to bar | Wibar | Modify wibar's widget property |
| Focus next window | Client | awful.client.focus.byidx(1) |
| Focus next screen | Screen | awful.screen.focus_relative(1) |
Accessing Objects
From anywhere
-- All clients
client.get()
-- All screens
for s in screen do ... end
-- All outputs
for o in output do ... end
-- Primary screen
screen.primary
-- Currently focused
client.focus
awful.screen.focused()
From a client
local c = client.focus
c.screen -- the screen this client is on
c:tags() -- tags this client belongs to
c.first_tag -- the first tag (useful for single-tag clients)
From a screen
local s = awful.screen.focused()
s.output -- the backing output object (SomeWM only)
s.tags -- all tags for this screen
s.selected_tags -- currently visible tags
s.clients -- all clients on this screen
s.geometry -- screen dimensions
s.workarea -- usable area (minus panels)
From a tag
local t = awful.screen.focused().selected_tag
t.screen -- the screen this tag belongs to
t:clients() -- clients with this tag
t.selected -- is this tag currently visible?
t.layout -- current layout for this tag
Signals: Reacting to Changes
Objects emit signals when things change. Connect to signals to react:
-- React when a client is created
client.connect_signal("manage", function(c)
-- c is the new client
print("New client: " .. c.name)
end)
-- React when focus changes
client.connect_signal("focus", function(c)
c.border_color = "#ff0000"
end)
-- React when a screen is added (monitor plugged in)
screen.connect_signal("added", function(s)
-- Set up tags and wibar for new screen
end)
Common signals:
| Object | Signal | When |
|---|---|---|
| output | added / removed | Monitor plugged/unplugged |
| output | property::scale | Display scale changed |
| screen | added / removed | Screen created/destroyed |
| client | manage | New client created |
| client | focus / unfocus | Focus changes |
| client | property::name | Window title changes |
| tag | property::selected | Tag visibility changes |
Next Steps
Now that you understand the object model:
- Architecture - How the layers communicate
- Basics - Hands-on practice with these concepts
- Widgets - Build widgets that use these objects