EWMHlib¶
A complete EWMH (Extended Window Manager Hints) implementation in Python 3 and python-xlib. Provides full, Pythonic access to the EWMH spec — query and control any compliant Window Manager, manage window hints, and subscribe to X events.
Tested on: Ubuntu/GNOME · Mint/Cinnamon · Manjaro/KDE · Raspbian/LXDE
⚠️ Wayland note: The new protocol used by GNOME in Ubuntu (Wayland) is not EWMH-compliant. Many features will not work under Wayland.
Why EWMHlib?¶
Complete and updated spec coverage — all EWMH properties, atoms, states, window types, hints and messages are implemented and accessible.
Full read and write support — not just querying, but sending messages and changing properties through the Window Manager.
Typed, structured API — methods return typed values (integers, strings, named tuples) instead of raw Xlib structs, with
text=Truevariants where atom names are more useful than atom ids.Multi-display and multi-screen support — convenience functions to enumerate displays, screens and roots, going beyond the default display assumption.
Window hints management — read and write
WM_HINTSandWM_NORMAL_HINTS, including urgency, icon, input model and size constraints.Event watching — subscribe to any X event on a window via a background watchdog thread and a callback, without managing the event loop yourself.
Low-level escape hatch — module-level
getProperty/changeProperty/sendMessagefunctions for custom or non-standard properties, with built-in atom resolution and reply parsing.
Quick start¶
from ewmhlib import EwmhRoot, EwmhWindow
# --- Root-level operations ---
root = EwmhRoot()
# Get the active (focused) window
win_id = root.getActiveWindow()
# List all open windows
all_windows = root.getClientList()
# How many virtual desktops are there?
n = root.getNumberOfDesktops()
print(f"{n} desktops available")
# Switch to desktop 1
root.setCurrentDesktop(1)
# --- Window-level operations ---
if win_id:
win = EwmhWindow(win_id)
# Read title and PID
print(win.getName())
print(win.getPid())
# Move the window to desktop 2
win.setDesktop(2)
# Maximize it
win.setMaximized(True, True)
# Focus it
win.setActive()
# Close it
win.setClosed()
Watching for events¶
import time
import Xlib.X
from ewmhlib import EwmhRoot, EwmhWindow
root = EwmhRoot()
win_id = root.getActiveWindow()
if win_id:
win = EwmhWindow(win_id)
def on_event(event):
print("Event received:", event)
win.extensions.checkEvents.start(
[Xlib.X.ConfigureNotify, Xlib.X.ClientMessage],
Xlib.X.StructureNotifyMask | Xlib.X.SubstructureNotifyMask,
on_event
)
print("Move or resize the window. Press Ctrl-C to stop.")
try:
while True:
time.sleep(0.1)
except KeyboardInterrupt:
pass
win.extensions.checkEvents.stop()
Ecosystem¶
EWMHlib is used as the Linux backend for PyWinCtl, a cross-platform window control library.
Table of Contents¶
General functions and variables¶
These are module-level — no class instantiation needed.
General objects¶
Provide direct access to the default display, screen and root, ready to use in any Xlib-related function.
Object |
Description |
|---|---|
|
Default X display connection |
|
Default screen |
|
Default root window |
General functions¶
Useful for multi-display or multi-screen setups. In most single-display scenarios the objects above are sufficient.
Function |
Description |
|---|---|
Get info on all present displays, including screens and roots |
|
Get display connection, screen and root from a root id |
|
Get display connection, screen and root from a window id |
|
Get all root window objects |
EwmhRoot — Root queries, changes and messages¶
Class to access root window features.
For the default root, use the convenience object defaultRootWindow — it gives instant access to all root methods without instantiating anything (equivalent to myRoot = EwmhRoot()).
To instantiate EwmhRoot for a specific root, pass its id. You can obtain it in several ways:
You already have a root → pass
root.idYou want to search across roots → use
getDisplaysInfo()to enumerate all roots and pick the desired oneYou have a window → use
getDisplayFromWindow(window.id)to retrieve the associated rootNo argument → retrieves the default display and root
Note: although a regular window and a root window share the same type, EwmhRoot methods are not intended for regular windows.
EwmhRoot methods¶
Method |
Description |
|---|---|
List hints supported by the Window Manager |
|
List managed windows in mapping order (oldest first) |
|
List managed windows in stacking order (bottom to top) |
|
Get the number of virtual desktops |
|
Request a change in the number of virtual desktops |
|
Get the common size of all desktops |
|
Request a change in the desktop geometry |
|
Get the top-left corner of each desktop’s viewport |
|
Request a change in the current desktop viewport |
|
Get the index of the current desktop |
|
Switch to a different virtual desktop |
|
Get the names of all virtual desktops |
|
Get the id of the currently active (focused) window |
|
Get the work area geometry for each desktop |
|
Check whether a compliant Window Manager is active |
|
Get the list of virtual root window ids |
|
Set the pager’s virtual desktop layout |
|
Check whether “show desktop” mode is active |
|
Enter or exit “show desktop” mode |
|
Request the Window Manager to close a window |
|
Move and/or resize a window |
|
Initiate interactive move/resize controlled by the WM |
|
Restack a window relative to a sibling (pager use) |
|
Ask the WM to estimate frame extents before mapping |
WM_PROTOCOLS messages (PING/SYNC) are available via the wmProtocols subclass (EwmhRoot.wmProtocols.*):
Method |
Description |
|---|---|
Send a WM_PING to check if a client is still responsive |
|
Synchronize WM frame repaints with the client window |
EwmhRoot variables¶
These are accessible as EwmhRoot.* and can be passed directly to python-xlib. In most cases they match the default general variables above.
Variable |
Description |
|---|---|
|
XDisplay connection the root belongs to |
|
Screen the root belongs to (Struct) |
|
Root as an X Window object |
|
Root window id |
EwmhWindow — Window queries, changes and messages¶
Class to access application window features.
Only a window id is needed to instantiate. You can obtain it in several ways:
Use an external module:
pywinctl.getAllWindowsWithTitle(title)orpywinctl.getActiveWindow()Retrieve it from your own application: PyQt’s
winId()or Tkinter’sframe()
Note: although a root is also a window, most of these methods will not work with a root.
EwmhWindow methods¶
Method |
Description |
|---|---|
Get the window title (_NET_WM_NAME) |
|
Set the window title |
|
Get the visible name shown by the WM (may differ from getName) |
|
Set the visible name |
|
Get the window icon name |
|
Set the window icon name |
|
Get the visible icon name shown by the WM |
|
Set the visible icon name |
|
Get the desktop index this window is on (0xFFFFFFFF = all desktops) |
|
Move the window to a specific desktop |
|
Get the functional window type (NORMAL, DIALOG, DOCK, etc.) |
|
Change the window type |
|
Get the list of current window states |
|
Add, remove or toggle a window state |
|
Set or unset horizontal/vertical maximized states |
|
Iconify (minimize) the window |
|
Get the list of user actions currently allowed on this window |
|
Get reserved screen-border space (legacy, see getStrutPartial) |
|
Set reserved screen-border space |
|
Get detailed reserved space at each screen border |
|
Get the geometry of the window’s taskbar icon |
|
Get the process id (PID) of the window’s owner |
|
Check whether the pager is handling icons for this window |
|
Get the timestamp of last user activity in this window |
|
Get the WM frame extents (left, right, top, bottom) |
|
Get the fully-opaque rectangles within the window |
|
Check whether the compositor should be bypassed |
|
Activate (focus) this window |
|
Request the WM to close this window |
|
Change this window’s stacking position relative to siblings |
|
Move and/or resize this window |
|
Initiate interactive move/resize via the WM |
|
Restack this window (pager use) |
|
Ask WM to estimate frame extents before this window is mapped |
EwmhWindow variables¶
Variable |
Description |
|---|---|
|
XDisplay connection the window belongs to |
|
Screen the window belongs to (Struct) |
|
Root the window belongs to (X Window object) |
|
Root the window belongs to (EwmhRoot object) |
|
X Window object associated with this window |
|
Window id |
EwmhWindow Extensions: Geometry, Hints, Protocols and Events¶
Low-level, non-EWMH window features — hints, protocols, and event monitoring — available via EwmhWindow.extensions.*.
Method |
Description |
|---|---|
Get WM_HINTS (input model, icon, urgency, etc.) |
|
Set or update WM_HINTS |
|
Get WM_NORMAL_HINTS (size constraints, gravity, aspect) |
|
Set or update WM_NORMAL_HINTS |
|
Get the WM protocols supported by this window |
|
Add supported WM protocols |
|
Remove supported WM protocols |
|
Watch for X events and invoke a callback |
CheckEvents runs a background watchdog thread. Control it with:
Method |
Description |
|---|---|
Start watching for events (can be called again to update args) |
|
Pause the watchdog (resume by calling start() again) |
|
Stop and terminate the watchdog thread |
Properties and Messages functions¶
These module-level functions let you query and control windows or roots directly, without instantiating EwmhRoot or EwmhWindow. They are closer to raw Xlib — more flexible for custom or non-standard use cases, but also more verbose. They simplify handling of atoms, replies and data format conversions.
Function |
Description |
|---|---|
Retrieve a property from a window or root |
|
Extract data from a retrieved property struct |
|
Set or change a property on a window or root |
|
Send a ClientMessage event to a window or root |
Properties, atoms and hints values¶
All EWMH-defined properties, atoms and hint values are accessible through the Props class (ewmhlib.Props.*). They cover everything recognized by the spec, organized into subclasses by category so they’re easy to discover and enumerate.
Subclass |
Description |
|---|---|
Root window properties (client list, desktops, active window…) |
|
Desktop layout orientation and corner constants |
|
Per-window properties (name, desktop, PID, strut…) |
|
Window type atoms (NORMAL, DIALOG, DOCK, TOOLBAR…) |
|
Window state atoms (maximized, minimized, fullscreen…) |
|
State change actions (ADD, REMOVE, TOGGLE) |
|
Move/resize direction and edge constants |
|
Property data format constants (8, 16, 32 bit) |
|
Property change mode (REPLACE, APPEND, PREPEND) |
|
Window stacking modes (Above, Below, TopIf…) |
|
Hint modification actions (KEEP, REMOVE, or new value) |
Data Structs¶
Named tuples to help interpret the structured data returned by some methods.
Struct |
Description |
|---|---|
Display/screen/root info returned by getDisplaysInfo() |
|
Per-screen info |
|
WM_HINTS fields returned by getWmHints() |
|
WM_NORMAL_HINTS fields returned by getWmNormalHints() |
Install ¶
To install this module on your system, you can use pip:
python -m pip install ewmhlib
or using uv:
uv add ewmhlib
Alternatively, you can download the wheel file (.whl) available in the Download page and the dist folder, and run this (don’t forget to replace ‘x.xx’ with proper version number):
python -m pip install EWMHlib-x.xx-py3-none-any.whl
You may want to add --force-reinstall option to be sure you are installing the right dependencies version.
Then, you can use it on your own projects just importing it:
import ewmhlib
Support ¶
In case you have a problem, comments or suggestions, do not hesitate to open issues on the project homepage
Using this code ¶
If you want to use this code or contribute, you can either:
Create a fork of the repository, or
Download the repository, uncompress, and open it on your IDE of choice (e.g. PyCharm)
Be sure you install all dev dependencies by running:
uv sync
or python -m venv .venv python -m pip install -e . –group=dev
Test ¶
To test this module on your own system, cd to “tests” folder and run:
uv run test_ewmhlib.py
List of EWMH-compliant window managers¶
A (likely) incomplete list of EWMH-compliant window managers (via Wikipedia, here and here):
Name |
Notes |
|---|---|
aewm |
|
awesome |
|
Blackbox |
(1) |
bspwm |
Partial |
CTWM |
(2) |
Compiz |
|
echinus |
|
edewm |
|
Enlightenment |
|
evilwm |
Partial (3) |
EXWM |
Partial |
Fluxbox |
|
FVWM |
|
goomwwm |
|
herbstluftwm |
|
i3 |
|
IceWM |
|
interfacewm |
|
JWM |
|
KWin (KDE) |
|
LeftWM |
|
Marco |
|
Matchbox |
|
Metacity (GNOME) |
|
Mutter (GNOME/MeeGo) |
|
Notion |
|
Openbox |
|
PekWM |
Partial |
PlayWM |
|
Qtile |
|
Sawfish |
Partial |
spectrwm |
|
subtle |
|
Window Maker |
Partial |
Wingo |
|
WMFS |
|
wmii |
|
Xfwm (Xfce) |
|
xmonad |
(4) |
(1) Through 0.65 / from 0.70
(2) As of 4.0.0
(3) Releases following and including version 1.1.0 follow the EWMH standard
(4) Must activate EWMH (XMonad.Hooks.EwmhDesktops)