Skip to content

Kalmat/EWMHlib

Repository files navigation

EWMHlib

CI PyPI version Documentation Status Downloads Stars License

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. This module is likely not working 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)

About

Extended Window Manager Hints implementation in Python 3 which allows to easily query and control Window Managers which follow these standards

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages