Python implementation of Extended Window Manager Hints for querying and controlling EWMH-compliant window managers
npx @tessl/cli install tessl/pypi-ewmh@0.1.0A Python implementation of Extended Window Manager Hints (EWMH) specification for querying and controlling EWMH-compliant window managers on Linux/Unix systems. Built on top of python-xlib, it enables programmatic interaction with modern window managers to retrieve window information, manipulate window properties, manage desktops and workspaces, and perform window management operations.
pip install ewmhfrom ewmh import EWMHAlternative import pattern:
import ewmh
ewmh_instance = ewmh.EWMH()from ewmh import EWMH
# Initialize EWMH connection (uses default display and root window)
ewmh = EWMH()
# Get current active window
active_window = ewmh.getActiveWindow()
if active_window:
print(f"Active window: {ewmh.getWmName(active_window)}")
# Get list of all managed windows
windows = ewmh.getClientList()
print(f"Total windows: {len(windows)}")
# Get desktop information
num_desktops = ewmh.getNumberOfDesktops()
current_desktop = ewmh.getCurrentDesktop()
print(f"Desktop {current_desktop + 1} of {num_desktops}")
# Set a window as active
if windows:
ewmh.setActiveWindow(windows[0])
ewmh.display.flush() # Important: flush to send the request
# Clean up
ewmh.display.close()EWMH provides a comprehensive interface to the Extended Window Manager Hints specification through a single EWMH class. The architecture is organized around five main capability areas:
The implementation builds on python-xlib for low-level X11 communication and provides both specific methods (e.g., getActiveWindow()) and generic property access (e.g., getProperty('_NET_ACTIVE_WINDOW')) for maximum flexibility. All operations require explicit display flushing to ensure commands are sent to the window manager.
Main class providing access to all EWMH window manager functionality through getter and setter methods.
class EWMH:
"""
Implementation of Extended Window Manager Hints specification.
Args:
_display: X11 display connection (optional, defaults to Xlib.display.Display())
root: Root window object (optional, defaults to display.screen().root)
"""
def __init__(self, _display=None, root=None): ...Methods for querying and controlling desktop/workspace properties.
def getClientList(self):
"""
Get list of windows maintained by the window manager.
Returns:
list: List of Window objects
"""
def getClientListStacking(self):
"""
Get list of windows in bottom-to-top stacking order.
Returns:
list: List of Window objects in stacking order
"""
def getNumberOfDesktops(self):
"""
Get number of virtual desktops.
Returns:
int: Number of desktops
"""
def getDesktopGeometry(self):
"""
Get desktop geometry as width and height.
Returns:
list: [width, height] in pixels
"""
def getDesktopViewPort(self):
"""
Get viewport positions for each desktop.
Returns:
list: List of [x, y] coordinates for each desktop viewport
"""
def getCurrentDesktop(self):
"""
Get current desktop number (0-indexed).
Returns:
int: Current desktop index
"""
def getActiveWindow(self):
"""
Get currently active window.
Returns:
Window or None: Active window object or None if no active window
"""
def getWorkArea(self):
"""
Get work area geometry for each desktop.
Returns:
list: List of [x, y, width, height] for each desktop work area
"""
def getShowingDesktop(self):
"""
Get desktop showing mode status.
Returns:
int: 1 if showing desktop mode active, 0 otherwise
"""Methods for controlling desktop/workspace properties and state.
def setNumberOfDesktops(self, nb):
"""
Set number of virtual desktops.
Args:
nb (int): Desired number of desktops
"""
def setDesktopGeometry(self, w, h):
"""
Set desktop geometry.
Args:
w (int): Desktop width in pixels
h (int): Desktop height in pixels
"""
def setDesktopViewport(self, w, h):
"""
Set desktop viewport size.
Args:
w (int): Viewport width in pixels
h (int): Viewport height in pixels
"""
def setCurrentDesktop(self, i):
"""
Switch to specified desktop.
Args:
i (int): Desktop index (0-indexed)
"""
def setActiveWindow(self, win):
"""
Set specified window as active.
Args:
win (Window): Window object to activate
"""
def setShowingDesktop(self, show):
"""
Enable or disable desktop showing mode.
Args:
show (int): 1 to show desktop, 0 to hide
"""Methods for querying individual window properties.
def getWmName(self, win):
"""
Get window name/title.
Args:
win (Window): Window object
Returns:
str: Window name
"""
def getWmVisibleName(self, win):
"""
Get window visible name (may differ from WM_NAME).
Args:
win (Window): Window object
Returns:
str: Window visible name
"""
def getWmDesktop(self, win):
"""
Get desktop number that window belongs to.
Args:
win (Window): Window object
Returns:
int: Desktop index (0-indexed)
"""
def getWmWindowType(self, win, str=False):
"""
Get window type(s).
Args:
win (Window): Window object
str (bool): If True, return string names instead of atom IDs
Returns:
list: List of window type atoms (int) or names (str)
"""
def getWmState(self, win, str=False):
"""
Get window state(s).
Args:
win (Window): Window object
str (bool): If True, return string names instead of atom IDs
Returns:
list: List of window state atoms (int) or names (str)
"""
def getWmAllowedActions(self, win, str=False):
"""
Get allowed actions for window.
Args:
win (Window): Window object
str (bool): If True, return string names instead of atom IDs
Returns:
list: List of allowed action atoms (int) or names (str)
"""
def getWmPid(self, win):
"""
Get process ID of window's application.
Args:
win (Window): Window object
Returns:
int: Process ID
"""Methods for controlling individual window properties and actions.
def setCloseWindow(self, win):
"""
Request window closure.
Args:
win (Window): Window object to close
"""
def setWmName(self, win, name):
"""
Set window name/title.
Args:
win (Window): Window object
name (str): New window name
"""
def setWmVisibleName(self, win, name):
"""
Set window visible name.
Args:
win (Window): Window object
name (str): New visible name
"""
def setWmDesktop(self, win, i):
"""
Move window to specified desktop.
Args:
win (Window): Window object
i (int): Target desktop index (0-indexed)
"""
def setMoveResizeWindow(self, win, gravity=0, x=None, y=None, w=None, h=None):
"""
Move and/or resize window.
Args:
win (Window): Window object
gravity (int): Gravity hint (Xlib.X.*Gravity constant or 0)
x (int, optional): New X coordinate
y (int, optional): New Y coordinate
w (int, optional): New width
h (int, optional): New height
"""
def setWmState(self, win, action, state, state2=0):
"""
Set or unset window state(s).
Args:
win (Window): Window object
action (int): 0=remove, 1=add, 2=toggle
state (int or str): Window state atom ID or name
state2 (int or str, optional): Second state atom ID or name
"""Methods for accessing EWMH properties by name using generic interfaces.
def getReadableProperties(self):
"""
Get list of all readable EWMH property names.
Returns:
list: Property names that can be used with getProperty()
"""
def getProperty(self, prop, *args, **kwargs):
"""
Get EWMH property value by name.
Args:
prop (str): Property name (e.g., '_NET_ACTIVE_WINDOW')
*args: Arguments passed to specific getter method
**kwargs: Keyword arguments passed to specific getter method
Returns:
Property value (type depends on property)
Raises:
KeyError: If property name is not recognized
"""
def getWritableProperties(self):
"""
Get list of all writable EWMH property names.
Returns:
list: Property names that can be used with setProperty()
"""
def setProperty(self, prop, *args, **kwargs):
"""
Set EWMH property value by name.
Args:
prop (str): Property name (e.g., '_NET_ACTIVE_WINDOW')
*args: Arguments passed to specific setter method
**kwargs: Keyword arguments passed to specific setter method
Raises:
KeyError: If property name is not recognized
"""NET_WM_WINDOW_TYPES = (
'_NET_WM_WINDOW_TYPE_DESKTOP',
'_NET_WM_WINDOW_TYPE_DOCK',
'_NET_WM_WINDOW_TYPE_TOOLBAR',
'_NET_WM_WINDOW_TYPE_MENU',
'_NET_WM_WINDOW_TYPE_UTILITY',
'_NET_WM_WINDOW_TYPE_SPLASH',
'_NET_WM_WINDOW_TYPE_DIALOG',
'_NET_WM_WINDOW_TYPE_DROPDOWN_MENU',
'_NET_WM_WINDOW_TYPE_POPUP_MENU',
'_NET_WM_WINDOW_TYPE_NOTIFICATION',
'_NET_WM_WINDOW_TYPE_COMBO',
'_NET_WM_WINDOW_TYPE_DND',
'_NET_WM_WINDOW_TYPE_NORMAL'
)NET_WM_ACTIONS = (
'_NET_WM_ACTION_MOVE',
'_NET_WM_ACTION_RESIZE',
'_NET_WM_ACTION_MINIMIZE',
'_NET_WM_ACTION_SHADE',
'_NET_WM_ACTION_STICK',
'_NET_WM_ACTION_MAXIMIZE_HORZ',
'_NET_WM_ACTION_MAXIMIZE_VERT',
'_NET_WM_ACTION_FULLSCREEN',
'_NET_WM_ACTION_CHANGE_DESKTOP',
'_NET_WM_ACTION_CLOSE',
'_NET_WM_ACTION_ABOVE',
'_NET_WM_ACTION_BELOW'
)NET_WM_STATES = (
'_NET_WM_STATE_MODAL',
'_NET_WM_STATE_STICKY',
'_NET_WM_STATE_MAXIMIZED_VERT',
'_NET_WM_STATE_MAXIMIZED_HORZ',
'_NET_WM_STATE_SHADED',
'_NET_WM_STATE_SKIP_TASKBAR',
'_NET_WM_STATE_SKIP_PAGER',
'_NET_WM_STATE_HIDDEN',
'_NET_WM_STATE_FULLSCREEN',
'_NET_WM_STATE_ABOVE',
'_NET_WM_STATE_BELOW',
'_NET_WM_STATE_DEMANDS_ATTENTION'
)# Xlib types used by EWMH methods
from Xlib.display import Display
from Xlib import Window
Window = Window # X11 window object from python-xlib
Display = Display # X11 display connection object from python-xlibAfter calling setter methods, you must flush the display connection to ensure changes are sent to the window manager:
ewmh = EWMH()
ewmh.setActiveWindow(some_window)
ewmh.display.flush() # Required to send the requestMethods may raise exceptions from the underlying Xlib operations. Common patterns:
Remember to close the display connection when done:
ewmh = EWMH()
# ... use ewmh methods ...
ewmh.display.close()Or use context management patterns for automatic cleanup.