Skip to main content
Version: 1.4

Migrating from AwesomeWM

Quick Compatibility Check

Before migrating, scan your config for potential issues:

somewm --check ~/.config/awesome/rc.lua

To only fail on issues that would actually hang the compositor (ignoring warnings):

somewm --check ~/.config/awesome/rc.lua --check-level=critical

This checks for:

  • Lua syntax errors - Caught before execution
  • X11-specific APIs - Functions like awesome.get_xproperty() that don't exist on Wayland
  • Blocking X11 tools - Calls to xrandr, xdotool, xprop via io.popen() that would hang
  • Missing local modules - require() statements for files that can't be found
  • Luacheck issues - Code quality warnings (if luacheck is installed)

Severity Levels

Issues are categorized by severity:

  • CRITICAL - Will fail or hang on Wayland (must fix)
  • WARNING - Needs a Wayland alternative
  • INFO - May not work but won't break config

Example output:

somewm config compatibility report
====================================
Config: /home/user/.config/awesome/rc.lua

X CRITICAL:
rc.lua:45 - io.popen with xrandr (blocks)
→ Use screen:geometry() or screen.outputs instead

! WARNING:
rc.lua:112 - maim screenshot tool
→ Use awful.screenshot or grim instead

Summary: 1 critical, 1 warning

Suppressing False Positives

If --check flags a pattern you've already handled (e.g. behind a runtime guard), add -- somewm:ignore to that line:

awful.spawn("flameshot gui") -- somewm:ignore using XDG portal on Wayland

The suppression also works at runtime, not just in --check mode. SomeWM's startup prescan will skip suppressed lines too.

X11 Pattern Replacements

Screen/Display Information

X11 PatternWayland Alternative
io.popen("xrandr")screen:geometry() or screen.outputs
xdpyinfoscreen.geometry properties
-- X11 (don't do this)
local handle = io.popen("xrandr | grep ' connected'")

-- Wayland
for s in screen do
print(s.geometry.width .. "x" .. s.geometry.height)
for name, output in pairs(s.outputs) do
print(" Output: " .. name)
end
end

Window/Client Information

X11 PatternWayland Alternative
io.popen("xdotool")awful.spawn() or client:send_key()
io.popen("xprop")client.class, client.instance
awesome.register_xproperty()Not needed on Wayland
-- X11 (don't do this)
local handle = io.popen("xprop -id " .. c.window)

-- Wayland
local class = c.class -- e.g., "firefox"
local instance = c.instance -- e.g., "Navigator"

Screenshots

X11 PatternWayland Alternative
scrotawful.screenshot or grim
maimawful.screenshot or grim
import (ImageMagick)grim
-- X11 (don't do this)
awful.spawn("scrot ~/screenshot.png")

-- Wayland
awful.screenshot({ directory = "~" })
-- or
awful.spawn("grim ~/screenshot.png")

Input Simulation

X11 PatternWayland Alternative
xdotool keyKeybindings, or wait for root.fake_input()
xdotool mousemoveNot yet implemented
note

root.fake_input() is currently a stub in SomeWM. Virtual input requires wlroots virtual pointer/keyboard protocols which are planned for a future release.

GTK/GDK via LGI

PatternSeverityNotes
lgi.require("Gtk")WARNINGImporting it is safe, but anything needing a display returns nil
lgi.require("Gdk")CRITICALFreezes the compositor the moment the import runs

Your rc.lua runs inside the compositor process. GTK and GDK are written to be Wayland clients: on startup they connect to the display server and wait for it to answer. That display server is SomeWM, which is busy running your config and cannot answer until your config returns. It waits for itself, forever.

SomeWM defuses half of this by preloading an empty lgi.override.Gtk, so importing Gtk never calls gtk_init_check(). There is no equivalent for Gdk: lgi/override/Gdk.lua calls Gdk.threads_init() at import, which connects to the display and hangs. Recovering means a hard power cycle.

Loading it later does not help. A callback, a signal handler and a timer all run on the compositor's own thread inside its event loop, so deferring the import only changes when it hangs:

-- Still freezes: the handler runs inside the compositor
awesome.connect_signal("my::signal", function()
local Gdk = lgi.require("Gdk", "3.0")
end)

What does work is anything that never touches the display. GdkPixbuf decodes images and is safe. Gtk's non-display types are safe. See Icon theme lookups for the common case.

Running a GTK app

PatternSeverity
Gtk.Application(...), Gtk.Application.new, Gtk.main()CRITICAL

A settings window or a preferences dialog written with GTK runs its own main loop. Started from your config, that loop runs inside the compositor and never returns, so SomeWM stops drawing, stops handling input, and stops responding to somewm-client. There is no keybinding out of it.

-- Freezes the session the moment the handler runs
local app = Gtk.Application({ application_id = "com.example.settings" })
function app:on_activate() ... end
return app:run()

A GTK app has to be its own process, the same as any other Wayland client. Move the code into a standalone script and spawn it:

-- settings.lua is a normal Lua script with a #! line, run outside SomeWM
awful.spawn("/usr/share/mywm/settings.lua")

The script is usually the file you already have. If it ends in app:run() it is already a complete program; it only needed to stop being required into the compositor.

Icon theme lookups

PatternSeverity
Gtk.IconTheme.get_default()WARNING

get_default() returns the icon theme belonging to the default display, so it needs gtk_init to have run. SomeWM skips gtk_init on purpose (see GTK/GDK via LGI), so get_default() returns nil and the next line fails with attempt to index a nil value.

Dock, launcher and desktop-icon widgets are the usual casualties, and the error surfaces far from the cause.

Gtk.IconTheme.new() needs no display. It reads the XDG icon directories directly and works normally:

local icontheme = Gtk.IconTheme.new()
icontheme:set_custom_theme(beautiful.icons) -- e.g. "Papirus-Light"

local info = icontheme:lookup_icon("firefox", 64, 0)
local path = info and info:get_filename()

menubar.utils.lookup_icon_uncached(name) is a pure-Lua fallback when the theme has no match.

Client icons

PatternSeverity
c.icon, awful.widget.clienticonINFO

On X11 a window carries its own icon in the _NET_WM_ICON property, so c.icon is almost always set and a tasklist just draws it. Wayland has no equivalent that SomeWM implements, so c.icon is nil for native Wayland clients.

Nothing errors. Every client simply gets whatever fallback your config draws when the icon is missing, so a dock or tasklist shows the same generic image for every window.

Resolve the icon from the class instead, which is set for both Wayland and XWayland clients:

local icontheme = Gtk.IconTheme.new()
icontheme:set_custom_theme(beautiful.icons)

local function icon_for(class)
if not class then return nil end
-- Reverse-DNS classes are common: try "com.mitchellh.ghostty", then "ghostty"
for _, name in ipairs({ class:lower(), class:lower():match("([^.]+)$") }) do
local info = icontheme:lookup_icon(name, 64, 0)
if info and info:get_filename() then return info:get_filename() end
end
return menubar.utils.lookup_icon_uncached(class:lower())
end

Xresources and xrdb

PatternSeverity
xrdbWARNING

The X resource database belongs to the X server. Without one, xrdb has nothing to write to and nothing reads what it writes. Configs hit this by generating an Xresources file for terminal colours and then calling os.execute("xrdb ...").

beautiful.xresources still works for DPI and for reading values a config supplies, but there is no live database behind it. Terminal colours belong in the terminal's own config file, which every Wayland terminal has.

X11 properties

PatternSeverityNotes
awesome.get_xpropertyCRITICALNot defined. Calling it aborts the config
awesome.set_xpropertyCRITICALNot defined. Calling it aborts the config
awesome.register_xpropertyWARNINGDefined, but does nothing

X11 properties are values attached to a window by the X server. Wayland has no equivalent, so SomeWM does not implement them.

Only register_xproperty exists, as a stub that accepts its arguments and does nothing. get_xproperty and set_xproperty are not bound at all, so calling either raises attempt to call field 'get_xproperty' (a nil value). That error escapes rc.lua, and SomeWM falls back to its own config. Your whole desktop is missing, from one line.

The usual reason a config calls these is to work out whether this is a fresh start or a restart:

-- AwesomeWM
local function restarted()
awesome.register_xproperty("restarted", "boolean")
local detected = awesome.get_xproperty("restarted") ~= nil
awesome.set_xproperty("restarted", true)
return detected
end

if not restarted() then
-- run autostart
end

awesome.startup answers the same question and needs no property:

if awesome.startup then
-- run autostart
end

Read it while your config is loading. It is computed from the compositor's main loop rather than stored, so it is true only during a fresh start's config load, and false during a reload's and at every point afterwards. Reading it from a signal handler or a timer always gives false.

What Works Unchanged

Most of your config will work without changes:

  • All awful. modules* - layouts, keybindings, rules, spawn, etc.
  • All gears. modules* - timers, shapes, filesystem, etc.
  • All wibox. widgets* - text, image, progressbar, etc.
  • Naughty notifications
  • Theming - beautiful.* properties
  • Client rules - awful.rules.rules
  • Signals - client.connect_signal(), screen.connect_signal(), etc.

Automatic Detection

Before loading, SomeWM scans your rc.lua and every file it requires, and reports the X11-specific code it finds. It loads the config either way: a report is a warning, not a refusal.

A config that genuinely hangs is caught by a ten second alarm on the load, after which SomeWM falls back to its own config. Run somewm --check to read the same report without starting the compositor.

If you share a config between AwesomeWM and SomeWM, you can detect which compositor is running:

local is_somewm = awesome.release == "somewm"

if is_somewm then
awful.spawn("grim ~/screenshot.png")
else
awful.spawn("scrot ~/screenshot.png")
end

Testing Your Config

  1. Run the compatibility check first:

    somewm --check ~/.config/awesome/rc.lua
  2. Fix any CRITICAL issues

  3. Try loading the config:

    somewm -c ~/.config/awesome/rc.lua
  4. Check debug output if needed:

    somewm -d -c ~/.config/awesome/rc.lua 2>&1 | tee migration.log

Common Migration Scenarios

Autostart Scripts

If you're spawning X11 tools on startup:

-- X11 (problematic)
awful.spawn.once("xset r rate 200 30") -- Keyboard repeat

-- Wayland (use awful.input)
awful.input.keyboard_repeat_delay = 200
awful.input.keyboard_repeat_rate = 30

Status Bar with X11 Tools

-- X11 (won't work)
awful.widget.watch("xbacklight -get", 5, function(widget, stdout)
widget:set_text(stdout)
end)

-- Wayland (use brightnessctl or similar)
awful.widget.watch("brightnessctl -m | cut -d',' -f4", 5, function(widget, stdout)
widget:set_text(stdout)
end)

Next Steps