EWMHlib

CI PyPI version Documentation Status

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=True variants 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_HINTS and WM_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 / sendMessage functions 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

defaultDisplay

Default X display connection

defaultScreen

Default screen

defaultRoot

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

getDisplaysInfo()

Get info on all present displays, including screens and roots

getDisplayFromRoot()

Get display connection, screen and root from a root id

getDisplayFromWindow()

Get display connection, screen and root from a window id

getRoots()

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.id

  • You want to search across roots → use getDisplaysInfo() to enumerate all roots and pick the desired one

  • You have a window → use getDisplayFromWindow(window.id) to retrieve the associated root

  • No 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

getSupportedHints()

List hints supported by the Window Manager

getClientList()

List managed windows in mapping order (oldest first)

getClientListStacking()

List managed windows in stacking order (bottom to top)

getNumberOfDesktops()

Get the number of virtual desktops

setNumberOfDesktops()

Request a change in the number of virtual desktops

getDesktopGeometry()

Get the common size of all desktops

setDesktopGeometry()

Request a change in the desktop geometry

getDesktopViewport()

Get the top-left corner of each desktop’s viewport

setDesktopViewport()

Request a change in the current desktop viewport

getCurrentDesktop()

Get the index of the current desktop

setCurrentDesktop()

Switch to a different virtual desktop

getDesktopNames()

Get the names of all virtual desktops

getActiveWindow()

Get the id of the currently active (focused) window

getWorkArea()

Get the work area geometry for each desktop

getSupportingWMCheck()

Check whether a compliant Window Manager is active

getVirtualRoots()

Get the list of virtual root window ids

setDesktopLayout()

Set the pager’s virtual desktop layout

getShowingDesktop()

Check whether “show desktop” mode is active

setShowingDesktop()

Enter or exit “show desktop” mode

setClosed()

Request the Window Manager to close a window

setMoveResize()

Move and/or resize a window

setWmMoveResize()

Initiate interactive move/resize controlled by the WM

setWmStacking()

Restack a window relative to a sibling (pager use)

requestFrameExtents()

Ask the WM to estimate frame extents before mapping

WM_PROTOCOLS messages (PING/SYNC) are available via the wmProtocols subclass (EwmhRoot.wmProtocols.*):

Method

Description

ping()

Send a WM_PING to check if a client is still responsive

sync()

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

display

XDisplay connection the root belongs to

screen

Screen the root belongs to (Struct)

root

Root as an X Window object

id

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) or pywinctl.getActiveWindow()

  • Retrieve it from your own application: PyQt’s winId() or Tkinter’s frame()

Note: although a root is also a window, most of these methods will not work with a root.

EwmhWindow methods

Method

Description

getName()

Get the window title (_NET_WM_NAME)

setName()

Set the window title

getVisibleName()

Get the visible name shown by the WM (may differ from getName)

setVisibleName()

Set the visible name

getIconName()

Get the window icon name

setIconName()

Set the window icon name

getVisibleIconName()

Get the visible icon name shown by the WM

setVisibleIconName()

Set the visible icon name

getDesktop()

Get the desktop index this window is on (0xFFFFFFFF = all desktops)

setDesktop()

Move the window to a specific desktop

getWmWindowType()

Get the functional window type (NORMAL, DIALOG, DOCK, etc.)

setWmWindowType()

Change the window type

getWmState()

Get the list of current window states

changeWmState()

Add, remove or toggle a window state

setMaximized()

Set or unset horizontal/vertical maximized states

setMinimized()

Iconify (minimize) the window

getAllowedActions()

Get the list of user actions currently allowed on this window

getStrut()

Get reserved screen-border space (legacy, see getStrutPartial)

setStrut()

Set reserved screen-border space

getStrutPartial()

Get detailed reserved space at each screen border

getIconGeometry()

Get the geometry of the window’s taskbar icon

getPid()

Get the process id (PID) of the window’s owner

getHandledIcons()

Check whether the pager is handling icons for this window

getUserTime()

Get the timestamp of last user activity in this window

getFrameExtents()

Get the WM frame extents (left, right, top, bottom)

getOpaqueRegion()

Get the fully-opaque rectangles within the window

getBypassCompositor()

Check whether the compositor should be bypassed

setActive()

Activate (focus) this window

setClosed()

Request the WM to close this window

changeStacking()

Change this window’s stacking position relative to siblings

setMoveResize()

Move and/or resize this window

setWmMoveResize()

Initiate interactive move/resize via the WM

setWmStacking()

Restack this window (pager use)

requestFrameExtents()

Ask WM to estimate frame extents before this window is mapped

EwmhWindow variables

Variable

Description

display

XDisplay connection the window belongs to

screen

Screen the window belongs to (Struct)

root

Root the window belongs to (X Window object)

rootWindow

Root the window belongs to (EwmhRoot object)

XWindow

X Window object associated with this window

id

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

getWmHints()

Get WM_HINTS (input model, icon, urgency, etc.)

setWmHints()

Set or update WM_HINTS

getWmNormalHints()

Get WM_NORMAL_HINTS (size constraints, gravity, aspect)

setWmNormalHints()

Set or update WM_NORMAL_HINTS

getWmProtocols()

Get the WM protocols supported by this window

addWmProtocols()

Add supported WM protocols

delWmProtocols()

Remove supported WM protocols

CheckEvents

Watch for X events and invoke a callback

CheckEvents runs a background watchdog thread. Control it with:

Method

Description

start()

Start watching for events (can be called again to update args)

pause()

Pause the watchdog (resume by calling start() again)

stop()

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

getProperty()

Retrieve a property from a window or root

getPropertyValue()

Extract data from a retrieved property struct

changeProperty()

Set or change a property on a window or root

sendMessage()

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

Root window properties (client list, desktops, active window…)

DesktopLayout

Desktop layout orientation and corner constants

Window

Per-window properties (name, desktop, PID, strut…)

WindowType

Window type atoms (NORMAL, DIALOG, DOCK, TOOLBAR…)

State

Window state atoms (maximized, minimized, fullscreen…)

StateAction

State change actions (ADD, REMOVE, TOGGLE)

MoveResize

Move/resize direction and edge constants

DataFormat

Property data format constants (8, 16, 32 bit)

Mode

Property change mode (REPLACE, APPEND, PREPEND)

StackMode

Window stacking modes (Above, Below, TopIf…)

HintAction

Hint modification actions (KEEP, REMOVE, or new value)


Data Structs

Named tuples to help interpret the structured data returned by some methods.

Struct

Description

DisplaysInfo

Display/screen/root info returned by getDisplaysInfo()

ScreensInfo

Per-screen info

WmHints

WM_HINTS fields returned by getWmHints()

WmNormalHints

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:

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)