Skip to main content
Version: 1.4

CLI Control somewm-only

somewm-client is SomeWM's IPC tool for controlling the compositor from scripts and the command line. It communicates with SomeWM via a Unix socket.

note

AwesomeWM has its own CLI tool (awesome-client), but it uses a different protocol. This guide covers somewm-client, which is specific to SomeWM.

tip

To try commands without affecting your real session, run them against a nested instance via Testing with a nested compositor.

Basic Usage​

Check If Running​

# Prints PONG if SomeWM is running
somewm-client ping

Help​

# Usage and the flag list
somewm-client --help

# Every command registered in this build
somewm-client commands

There is no per-group help verb. somewm-client commands prints the authoritative list, and the reference documents the arguments.

Window (Client) Management​

List Windows​

# List all windows with IDs
somewm-client client list

Output format:

id=1 title="Firefox" class="firefox" tags=1 floating=false
id=2 title="Alacritty" class="Alacritty" tags=1 floating=false
id=3 title="Code - project" class="code" tags=2 floating=false

Client IDs are simple integers assigned when windows open. They increment but don't reuse within a session, and reset when the compositor restarts. The id property is read-only in Lua (c.id).

Focus Window​

# Focus window by ID
somewm-client client focus 1

Close Window​

# Close window by ID
somewm-client client close 1

# Close focused window
somewm-client client close

Window Properties​

These are get-or-set, not toggles. Called with a target alone they report the current value; called with true/1 or false/0 they set it. There is no toggle keyword.

# Read the current value
somewm-client client floating focused

# Set it
somewm-client client floating focused true
somewm-client client fullscreen focused true
somewm-client client maximized 3 false
somewm-client client minimized focused true
somewm-client client ontop focused true

The same shape covers sticky, hidden, modal, focusable, urgent, above, below, skip_taskbar, and the maximized_horizontal / maximized_vertical pair. To actually toggle, read then write, or use eval:

somewm-client eval "local c = client.focus; if c then c.floating = not c.floating end"

Tag Management​

# View tag by index (1-9)
somewm-client tag view 3

# List all tags
somewm-client tag list

# Toggle tag visibility
somewm-client tag toggle 2

tag view takes an index, not a name. somewm-client tag view "web" fails with Invalid tag number: web. Use tag list to map names to indices:

1: dev [active]
2: web
3: chat

Screen Management​

# List screens
somewm-client screen list

# Get/set scale
somewm-client screen scale # Get current scale
somewm-client screen scale 1.5 # Set focused screen to 1.5
somewm-client screen scale 1 1.5 # Set screen 1 to 1.5

Input Configuration somewm-only​

Change input settings at runtime without editing rc.lua:

Pointer Settings​

# Tap to click (0 = off, 1 = on)
somewm-client input tap_to_click 1

# Natural scrolling
somewm-client input natural_scrolling 1

# Pointer speed (-1.0 to 1.0)
somewm-client input accel_speed 0.5

# Left-handed mode
somewm-client input left_handed 1

# Scroll method ("two_finger", "edge", "button", or nil for default)
somewm-client input scroll_method two_finger
caution

scroll_button, scroll_button_lock, dwtp, and clickfinger_button_map exist on the awful.input module but are not reachable over IPC. somewm-client input scroll_button 274 fails with Unknown setting: scroll_button. Set those in rc.lua.

Keyboard Settings​

# Repeat rate (keys per second)
somewm-client input keyboard_repeat_rate 30

# Repeat delay (milliseconds)
somewm-client input keyboard_repeat_delay 300

# Keyboard layout
somewm-client input xkb_layout "us"

# Layout variant
somewm-client input xkb_variant "dvorak"

# XKB options
somewm-client input xkb_options "ctrl:nocaps"

Query Current Values​

# Get current value (no argument)
somewm-client input tap_to_click
somewm-client input accel_speed
somewm-client input xkb_layout

Screenshots​

The path is required; there is no bare screenshot command.

# Whole desktop
somewm-client screenshot save ~/Pictures/shot.png

# Preserve alpha
somewm-client screenshot save ~/Pictures/shot.png --transparent

# One window, or one screen
somewm-client screenshot client ~/Pictures/window.png 3
somewm-client screenshot screen ~/Pictures/screen.png 1

# Pick a region interactively
somewm-client screenshot interactive ~/Pictures/region.png

Session Locking somewm-only​

somewm-client lock

This triggers awesome.lock() via IPC. Requires a lock surface to be registered (i.e., require("lockscreen").init() must have been called in rc.lua).

Lua Evaluation​

Run arbitrary Lua code in SomeWM's context:

Reach for eval only when no dedicated command covers the job. Command output is parseable and survives API changes; eval output does neither:

# Version, focused window, client count: use the commands
somewm-client version
somewm-client client info focused
somewm-client client list | grep -c '^id='

# Move the focused window to tag 2
somewm-client client movetotag 2

What eval is actually for:

# Simple expression
somewm-client eval "return 1 + 1"
# Output: 2

# Toggle rather than set, which no command does
somewm-client eval "local c = client.focus; if c then c.floating = not c.floating end"

# Reach state no command exposes
somewm-client eval "return awesome.startup_errors"

Eval for Scripting​

Two constraints decide how these get written, and neither announces itself:

  • One line only. The protocol is one command per line. In a multi-line string only the first line runs and the rest is silently dropped; a string starting with a newline fails with Missing Lua code to evaluate. Use semicolons.
  • Only capi globals are in scope. client, screen, tag, mouse, awesome, root, and require. awful, gears, beautiful, naughty, wibox, and ruled are nil until you require them.
# Windows that are floating right now, one per line
somewm-client eval "local r = {}; for _, c in ipairs(client.get()) do if c.floating then table.insert(r, c.id .. ' ' .. (c.name or '')) end end; return table.concat(r, '\n')"

# Anything from the Lua libraries needs a require first
somewm-client eval "local awful = require('awful'); return awful.screen.focused().index"

Two more things that bite in scripts:

  • Use c.id, not c.window. c.window is an X11 window ID, so it is 0 for native Wayland clients and only meaningful for XWayland ones. c.id is what client list prints and what every somewm-client client ... command accepts.
  • Replies begin with an OK line and end with a blank line. Filter on the payload (grep '^id=') rather than capturing the reply whole.

Exit Codes​

CodeMeaning
0Success
1The compositor replied ERROR, or the arguments were unusable
2Could not connect to the compositor socket (SomeWM not running)

Use in scripts:

if somewm-client ping > /dev/null 2>&1; then
echo "SomeWM is running"
else
echo "SomeWM is not running"
fi

Scripting Examples​

Rofi Window Switcher​

#!/bin/bash
# window-switcher.sh

# Get window list, dropping the OK line and the trailing blank
windows=$(somewm-client client list | grep '^id=')

# Show in rofi (display full line, return selected)
selected=$(echo "$windows" | rofi -dmenu -p "Window")

if [ -n "$selected" ]; then
# Extract numeric ID from "id=N ..."
id=$(echo "$selected" | sed 's/id=\([0-9]*\).*/\1/')
somewm-client client focus "$id"
fi

Toggle Layout Script​

#!/bin/bash
# toggle-layout.sh

current=$(somewm-client input xkb_layout)

if [ "$current" = "us" ]; then
somewm-client input xkb_layout "de"
else
somewm-client input xkb_layout "us"
fi

Polybar Integration​

# In polybar config
[module/layout]
type = custom/script
exec = somewm-client input xkb_layout
interval = 1
click-left = somewm-client input xkb_layout "$([ $(somewm-client input xkb_layout) = 'us' ] && echo 'de' || echo 'us')"

Focus or Launch​

#!/bin/bash
# focus-or-launch.sh <class> <command>

class="$1"
command="$2"

# Check if a window with this class exists, return its ID
window_id=$(somewm-client client list \
| grep "class=\"$class\"" \
| head -1 \
| sed 's/^id=\([0-9]*\).*/\1/')

if [ -n "$window_id" ]; then
somewm-client client focus "$window_id"
else
$command &
fi

Usage:

./focus-or-launch.sh Firefox firefox
./focus-or-launch.sh Alacritty alacritty

Workspace Indicator for Status Bars​

#!/bin/bash
# Get current tag info for status bar

somewm-client eval "local awful = require('awful'); local s = awful.screen.focused(); local t = {}; for _, tag in ipairs(s.tags) do table.insert(t, (tag.selected and '*' or '') .. tag.name .. (#tag:clients() > 0 and '+' or '')) end; return table.concat(t, ' ')" | tail -n +2

Troubleshooting​

Connection Failed​

somewm-client ping
# Error: Failed to connect to /run/user/1000/somewm-socket
# Is somewm running?
# connect: No such file or directory

Exit code 2. Check that:

  1. SomeWM is actually running
  2. The socket exists: ls /run/user/$(id -u)/somewm-*
  3. You're running as the same user as SomeWM
  4. SOMEWM_SOCKET is unset, or points at the instance you meant

Command Not Found​

somewm-client foobar
# ERROR Unknown command: foobar

Exit code 1. The compositor validates command names, not the binary, so a typo reaches the socket before it is rejected. Check the list:

somewm-client commands

Two-word commands map to one name, so client foo is reported as client.foo.

Eval Syntax Errors​

somewm-client eval "return client.focus.name"
# ERROR ./lua/awful/ipc.lua:1845: [string "return client.focus.name"]:1: attempt to index a nil value

Nothing was focused, so client.focus was nil. Guard it:

somewm-client eval "return client.focus and client.focus.name or 'none'"

Or skip the guard entirely and let the command report it: client info focused errors with No focused client.

See Also​