how can I execute mouse clicks in windows from Python code that is running in WSL?
I tried using PyAutoGUI, however, I get the following error:
...
File "/usr/lib/python3.6/os.py", line 669, in __getitem__
raise KeyError(key) from None
KeyError: 'DISPLAY'
Because I am using wsl, it decides that my platform is linux. If I hardcode it to windows, ctypes do have attribute win.dll in dc = ctypes.windll.user32.GetDC(0)
if sys.platform == 'win32':
import ctypes
if _PILLOW_INSTALLED:
from PIL import ImageGrab
# Makes this process aware of monitor scaling so the screenshots are correctly sized:
try:
ctypes.windll.user32.SetProcessDPIAware()
except AttributeError:
pass # Windows XP doesn't support this, so just do nothing.
dc = ctypes.windll.user32.GetDC(0)
class POINT(ctypes.Structure):
_fields_ = [('x', ctypes.c_long),
('y', ctypes.c_long)]
def _winPosition():
cursor = POINT()
ctypes.windll.user32.GetCursorPos(ctypes.byref(cursor))
return (cursor.x, cursor.y)
position = _winPosition
def _winScreenshot(filename=None):
# TODO - Use the winapi to get a screenshot, and compare performance with ImageGrab.grab()
# https://stackoverflow.com/a/3586280/1893164
try:
im = ImageGrab.grab()
if filename is not None:
im.save(filename)
except NameError:
raise ImportError('Pillow module must be installed to use screenshot functions on Windows.')
return im
screenshot = _winScreenshot
def _winSize():
return (ctypes.windll.user32.GetSystemMetrics(0), ctypes.windll.user32.GetSystemMetrics(1))
size = _winSize
def _winGetPixel(x, y):
colorRef = ctypes.windll.gdi32.GetPixel(dc, x, y) # A COLORREF value as 0x00bbggrr. See https://learn.microsoft.com/en-us/windows/win32/gdi/colorref
red = colorRef % 256
colorRef //= 256
green = colorRef % 256
colorRef //= 256
blue = colorRef
return (red, green, blue)
getPixel = _winGetPixel
elif platform.system() == 'Linux':
from Xlib.display import Display
import errno
scrotExists = False
try:
whichProc = subprocess.Popen(
['which', 'scrot'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
scrotExists = whichProc.wait() == 0
except OSError as ex:
if ex.errno == errno.ENOENT:
# if there is no "which" program to find scrot, then assume there
# is no scrot.
pass
else:
raise
_display = Display(os.environ['DISPLAY'])
def _linuxPosition():
coord = _display.screen().root.query_pointer()._data
return coord["root_x"], coord["root_y"]
position = _linuxPosition
def _linuxScreenshot(filename=None):
if not scrotExists:
raise NotImplementedError('"scrot" must be installed to use screenshot functions in Linux. Run: sudo apt-get install scrot')
if filename is not None:
tmpFilename = filename
else:
tmpFilename = '.screenshot%s.png' % (datetime.datetime.now().strftime('%Y-%m%d_%H-%M-%S-%f'))
if scrotExists:
subprocess.call(['scrot', '-z', tmpFilename])
im = Image.open(tmpFilename)
# force loading before unlinking, Image.open() is lazy
im.load()
if filename is None:
os.unlink(tmpFilename)
return im
else:
raise Exception('The scrot program must be installed to take a screenshot with PyScreeze on Linux. Run: sudo apt-get install scrot')
screenshot = _linuxScreenshot
def _linuxSize():
return _display.screen().width_in_pixels, _display.screen().height_in_pixels
size = _linuxSize
def _linuxGetPixel(x, y):
rgbValue = screenshot().getpixel((x, y))
return rgbValue[0], rgbValue[1], rgbValue[2]
getPixel = _linuxGetPixel
Does anyone know how to solve this problem?
Just to clarify... pyautogui will not work with WSL? Based on what I have read elsewhere this has to do with the lack of an X-server in the linux subsystem. Similar to how pyautogui will not work over a remote or headless terminal session.
So, try this with just plain old windows and it will work!
you could try adding this line AFTER importing pyautogui.
import pyautogui._pyautogui_win as platformModule
This should reassign / override the platformModule variable to the Windows version..
(In theory)
Related
How do I change the screen brightness in windows using python, without using any extra python packages?
I'd also like a way of getting what the screen brightness is currently at.
I'm thinking that will need to be by using ctypes, but I can't find any information on how to do it.
It annoys me the way the screen brightness buttons on windows adjust the screen brightness in very large increments, so I made a python tkinter program to display the current screen brightness on the taskbar and when I scroll the mouse wheel on the tkinter window it changes the screen brightness:
import subprocess
from tkinter import Canvas, Tk
win = Tk()
win.overrideredirect(True)
canvas = Canvas(win, bg = "#101010", highlightthickness = 0)
canvas.pack(fill = "both", expand = True)
def trim(string, l = None, r = None, include = False, flip = False):
string = str(string)
if l and l in string:
string = string[(string.rindex(l) if flip else string.index(l)) + (0 if include else len(l)):]
if r and r in string:
string = string[:(string.index(r) if flip else string.rindex(r)) + (len(r) if include else 0)]
return string
def set_screen_brightness():
subprocess.run(
["powershell",
f"(Get-WmiObject -Namespace root/WMI -Class WmiMonitorBrightnessMethods).WmiSetBrightness(1,{current_brightness})"],
stdout = subprocess.PIPE, stderr = subprocess.STDOUT, shell = True)
def get_screen_brightness():
value = subprocess.check_output(
["powershell", f"Get-Ciminstance -Namespace root/WMI -ClassName WmiMonitorBrightness"],
shell = True)
return int(trim(value, l = "CurrentBrightness : ", r = r"\r\nInstanceName : "))
current_brightness = get_screen_brightness()
brightness_text = canvas.create_text(30, 25, text = current_brightness,
font = ("Segue ui", 14, "bold"), fill = "White", anchor = "w")
sw, sh = win.winfo_screenwidth(), win.winfo_screenheight()
win.geometry(f"69x50+{sw - 505}+{sh - 49}")
def mouse_wheel_screen_brightness(scroll):
global current_brightness, mouse_wheel_screen_brightness_wa
if "mouse_wheel_screen_brightness_wa" in globals():
win.after_cancel(mouse_wheel_screen_brightness_wa)
current_brightness += 2 if scroll else -2
if current_brightness < 0: current_brightness = 0
if current_brightness > 100: current_brightness = 100
canvas.itemconfig(brightness_text, text = current_brightness)
mouse_wheel_screen_brightness_wa = win.after(100, lambda: set_screen_brightness())
win.bind("<MouseWheel>", lambda e: mouse_wheel_screen_brightness(e.delta > 0))
def topmost():
win.attributes("-topmost", True)
win.after(100, topmost)
topmost()
win.mainloop()
I'm sure there must be a fast way of changing the screen brightness in ctypes, without any extra python packages. With using a subprocess call to Powershell the screen brightness text keeps freezing.
I want to do it without using any extra python packages because when I try to install a python package it says that there was a problem confirming the ssl certificate. I googled it and tried to fix it, but I can't get it to work.
The another way to do it without any library is:
import subprocess
def run(cmd):
completed = subprocess.run(["powershell", "-Command", cmd], capture_output=True)
return completed
if __name__ == '__main__':
take_brightness = input("Please enter the brightness level: ")
command = "(Get-WmiObject -Namespace root/WMI -Class WmiMonitorBrightnessMethods).WmiSetBrightness(1," + take_brightness + ")"
hello_command = f"Write-Host {command}"
hello_info = run(hello_command)
by the help of PowerShell commands we can easily increase or decrease the brightness of windows without any external packages
WmiSetBrightness(1,<brightness % goes here>)
import subprocess
subprocess.run(["powershell", "(Get-WmiObject -Namespace root/WMI -Class WmiMonitorBrightnessMethods).WmiSetBrightness(1,40)"])
the subprocess is the internal library that comes with python.
just run the above code,
hope this will work.
Install screen brightness control using pip
Windows: pip install screen-brightness-control
Mac/Linux: pip3 install screen-brightness-control I guess
Then use this code
import screen_brightness_control as sbc
sbc.set_brightness(25) # Number will be any number between 0 and hundred
# Sets screen brightness to 25%
I found info on the screen brightness module here
I used the WMI library and it really worked fine. Here is the code, but this is for Windows. I think it is OS specific so if it doesn't work for you, you should look for a better solution.
import wmi
brightness = 40 # percentage [0-100] For changing thee screen
c = wmi.WMI(namespace='wmi')
methods = c.WmiMonitorBrightnessMethods()[0]
methods.WmiSetBrightness(brightness, 0)
Please upvote and accept the answer if you like it.
I'm working with Anaconda3 and can only use Default Libraries. Is there a way to access the already installed UIAutomationCore.dll thats standard with the Windows OS now and/or other 'Standard' DLL's and import them or wrap them to create my own custom modules in Python? I'm currently using Python 3.7 on Windows 10.
I found my own answer to this question. You CAN Import the entire UIAutomationCore.dll Library and any other Native DLL or Library by using the 'comtypes.client' and the 'ctypes' modules. Here is a link to an Already wrapped and fully functional Import for those who do not have the Import/pip Install restrictions and security concerns my team has. If like me you need to ONLY use 'Native' Imports and Libraries that come with the Anacoda3 install by Default. Then I suggest using this code to start you off for wrapping it yourself, as I have done for my specific needs. Hope this helps everyone else in there endeavors to use UIAutomationCore.
Link: [https://github.com/yinkaisheng/Python-UIAutomation-for-Windows/blob/master/uiautomation/uiautomation.py][1]
Code:
import os
import sys
from typing import (Any, Callable, Iterable, Tuple, List, Dict) # need pip install typing for Python3.4 or lower
import ctypes
import ctypes.wintypes
import comtypes
import comtypes.client
TreeNode = Any
METRO_WINDOW_CLASS_NAME = 'Windows.UI.Core.CoreWindow' # for Windows 8 and 8.1
SEARCH_INTERVAL = 0.5 # search control interval seconds
MAX_MOVE_SECOND = 0.5 # simulate mouse move or drag max seconds
TIME_OUT_SECOND = 20
OPERATION_WAIT_TIME = 0.5
MAX_PATH = 260
DEBUG_SEARCH_TIME = False
DEBUG_EXIST_DISAPPEAR = False
S_OK = 0
IsNT6orHigher = os.sys.getwindowsversion().major >= 6
ProcessTime = time.perf_counter #this returns nearly 0 when first call it if python version <= 3.6
ProcessTime() # need to call it once if python version <= 3.6
class _AutomationClient:
_instance = None
#classmethod
def instance(cls) -> '_AutomationClient':
"""Singleton instance (this prevents com creation on import)."""
if cls._instance is None:
cls._instance = cls()
return cls._instance
def __init__(self):
try:
self.UIAutomationCore = comtypes.client.GetModule("UIAutomationCore.dll")
self.IUIAutomation = comtypes.client.CreateObject("{ff48dba4-60ef-4201-aa87-54103eef594e}", interface=self.UIAutomationCore.IUIAutomation)
self.ViewWalker = self.IUIAutomation.RawViewWalker
#self.ViewWalker = self.IUIAutomation.ControlViewWalker
except OSError as ex:
Logger.WriteLine('Can not load UIAutomationCore.dll.\nYou may need to install Windows Update KB971513.\nhttps://github.com/yinkaisheng/WindowsUpdateKB971513ForIUIAutomation', ConsoleColor.Yellow)
raise ex
#Windows dll
ctypes.windll.user32.GetClipboardData.restype = ctypes.c_void_p
ctypes.windll.user32.GetWindowDC.restype = ctypes.c_void_p
ctypes.windll.user32.OpenDesktopW.restype = ctypes.c_void_p
ctypes.windll.user32.WindowFromPoint.restype = ctypes.c_void_p
ctypes.windll.user32.SendMessageW.restype = ctypes.wintypes.LONG
ctypes.windll.user32.GetForegroundWindow.restype = ctypes.c_void_p
ctypes.windll.user32.GetWindowLongW.restype = ctypes.wintypes.LONG
ctypes.windll.kernel32.GlobalLock.restype = ctypes.c_void_p
ctypes.windll.kernel32.GlobalAlloc.restype = ctypes.c_void_p
ctypes.windll.kernel32.GetStdHandle.restype = ctypes.c_void_p
ctypes.windll.kernel32.OpenProcess.restype = ctypes.c_void_p
ctypes.windll.kernel32.CreateToolhelp32Snapshot.restype = ctypes.c_void_p
class _DllClient:
_instance = None
#classmethod
def instance(cls) -> '_DllClient':
"""Singleton instance (this prevents com creation on import)."""
if cls._instance is None:
cls._instance = cls()
return cls._instance
def __init__(self):
binPath = os.path.join(os.path.dirname(os.path.abspath(__file__)), "bin")
os.environ["PATH"] = binPath + os.pathsep + os.environ["PATH"]
load = False
if sys.maxsize > 0xFFFFFFFF:
try:
self.dll = ctypes.cdll.UIAutomationClient_VC140_X64
load = True
except OSError as ex:
pass
else:
try:
self.dll = ctypes.cdll.UIAutomationClient_VC140_X86
load = True
except OSError as ex:
pass
if load:
self.dll.BitmapCreate.restype = ctypes.c_size_t
self.dll.BitmapFromWindow.argtypes = (ctypes.c_size_t, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int)
self.dll.BitmapFromWindow.restype = ctypes.c_size_t
self.dll.BitmapFromFile.argtypes = (ctypes.c_wchar_p, )
self.dll.BitmapFromFile.restype = ctypes.c_size_t
self.dll.BitmapToFile.argtypes = (ctypes.c_size_t, ctypes.c_wchar_p, ctypes.c_wchar_p)
self.dll.BitmapRelease.argtypes = (ctypes.c_size_t, )
self.dll.BitmapGetWidthAndHeight.argtypes = (ctypes.c_size_t, )
self.dll.BitmapGetPixel.argtypes = (ctypes.c_size_t, ctypes.c_int, ctypes.c_int)
self.dll.BitmapSetPixel.argtypes = (ctypes.c_size_t, ctypes.c_int, ctypes.c_int, ctypes.c_uint)
self.dll.Initialize()
else:
self.dll = None
Logger.WriteLine('Can not load dll.\nFunctionalities related to Bitmap are not available.\nYou may need to install Microsoft Visual C++ 2010/2015 Redistributable Package.', ConsoleColor.Yellow)
def __del__(self):
if self.dll:
self.dll.Uninitialize()
In a sense.
First of all, install the Python module from the command prompt using:
pip install uiautomation
You can install it for everyone by right-clicking on the Windows icon and selecting 'Command Prompt (Admin)' if you wish.
To use this this product within Python the most statement to use would be:
import uiautomation
This makes all items in the module available to your script. For instance, you can access
uiautomation.CalendarControl
using the usual Python syntax.
I am trying to write a python script to rotate screen in Windows.
I have clues of doing it with Win32api.
What are the other possibilities or commands to achieve so(Win32api included).
you can use below code for screen rotation in any angle, I changed the code given by Mxl above.
import win32api as win32
import win32con
import sys
import re
x = 0
args=sys.argv[1].lower()
rotation_val=0
m = re.search("(?<=^-rotate=)\S+", args) # Use non-white character wildcard instead of d decimal
if (m != None):
print m.group(0)
if ((m.group(0) == "180")):
rotation_val=win32con.DMDO_180
elif((m.group(0) == "90")):
rotation_val=win32con.DMDO_270
elif ((m.group(0) == "270")):
rotation_val=win32con.DMDO_90
else:
rotation_val=win32con.DMDO_DEFAULT
device = win32.EnumDisplayDevices(None,x)
dm = win32.EnumDisplaySettings(device.DeviceName,win32con.ENUM_CURRENT_SETTINGS)
if((dm.DisplayOrientation + rotation_val)%2==1):
dm.PelsWidth, dm.PelsHeight = dm.PelsHeight, dm.PelsWidth
dm.DisplayOrientation = rotation_val
win32.ChangeDisplaySettingsEx(device.DeviceName,dm)
for running this script you will need to give the following command:-
filename.py -rotate=180
filename.py -rotate=0
filename.py -rotate=90
filename.py -rotate=270
you can simply use rotate-screen library to do screen rotations. (Only support for Windows right now)
import rotatescreen
screen = rotatescreen.get_primary_display()
screen.rotate_to(90) # rotate to 90 degrees
This is (a sligtly modified version of) the code that worked for me from the answer given above prvided by lbenini. Possible screen rotation values are win32con.DMDO_DEFAULT (0°), win32con.DMDO_90, win32con.DMDO_180 and win32con.DMDO_270 (one can obtain a list by typing help(win32con))
import win32api as win32
import win32con
def printAllScreen():
i = 0
while True:
try:
device = win32.EnumDisplayDevices(None,i);
print("[%d] %s (%s)"%(i,device.DeviceString,device.DeviceName));
i = i+1;
except:
break;
return i
screen_count=printAllScreen()
x = int(input("\nEnter a display number [0-%d]: "%screen_count))
device = win32.EnumDisplayDevices(None,x);
print("Rotate device %s (%s)"%(device.DeviceString,device.DeviceName));
dm = win32.EnumDisplaySettings(device.DeviceName,win32con.ENUM_CURRENT_SETTINGS)
dm.DisplayOrientation = win32con.DMDO_180
dm.PelsWidth, dm.PelsHeight = dm.PelsHeight, dm.PelsWidth
dm.Fields = dm.Fields & win32con.DM_DISPLAYORIENTATION
win32.ChangeDisplaySettingsEx(device.DeviceName,dm)
If you have the rotate shortcut active in windows (CTRL+ALT+ARROW KEY) you can use pyautogui.hotkey function.
I am using PySDL2 and I am coding a little script that load a image on a windows but I am getting this error message "ctypes.ArgumentError: argument 4: : expected LP_c_int instance instead of int" when i use this function "SDL_QueryTexture". This is my code:
"""Simple example for using sdl2 directly."""
import os
import sys
import ctypes
from sdl2 import *
def run():
SDL_Init(SDL_INIT_VIDEO)
window = SDL_CreateWindow(b"Hello World",
SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED,
459, 536, SDL_WINDOW_SHOWN)
render = SDL_CreateRenderer(window, -1, 0)
fname = os.path.join(os.path.dirname(os.path.abspath(__file__)),
"resources", "self-control.bmp")
imageSurface = SDL_LoadBMP(fname.encode("utf-8"))
imageTexture = SDL_CreateTextureFromSurface(render, imageSurface)
SDL_FreeSurface(imageSurface)
sourceRectangle = SDL_Rect()
destinationRectangle = SDL_Rect()
SDL_QueryTexture(imageTexture, None, None, sourceRectangle.w, sourceRectangle.h)
destinationRectangle.x = sourceRectangle.x = 0
destinationRectangle.y = sourceRectangle.y = 0
destinationRectangle.w = sourceRectangle.w
destinationRectangle.h = sourceRectangle.h
SDL_RenderCopy(render, imageTexture, sourceRectangle, destinationRectangle)
running = True
event = sdl2.SDL_Event()
while running:
while SDL_PollEvent(ctypes.byref(event)) != 0:
if event.type == sdl2.SDL_QUIT:
running = False
break
SDL_Delay(10)
SDL_DestroyWindow(window)
SDL_Quit()
return 0
if __name__ == "__main__":
sys.exit(run())
I know is something related to ctypes, i hope someone can help me.
SDL_QueryTexture gets pointers to ints to write result to, you cannot simply pass int here. Workaround would be something like
w = ctypes.c_int()
h = ctypes.c_int()
SDL_QueryTexture(imageTexture, None, None, w, h)
And then getting result from w.value and h.value.
However you already have a surface, why not just read width and height from it?
imageSurface.contents.w, imageSurface.contents.h
I want to detect applications window name when changing focus event occurs with python xlib, so in the first step I use this code:
#!/usr/bin/env python
#-*- coding:utf-8 -*-
import Xlib.display
import time
display = Xlib.display.Display()
while True:
window = display.get_input_focus().focus
wmname = window.get_wm_name()
wmclass = window.get_wm_class()
if wmclass is None and wmname is None:
window = window.query_tree().parent
wmname = window.get_wm_name()
print "WM Name: %s" % ( wmname, )
time.sleep(3)
But I want a correct way, then I research about xlib events and find Input Focus Events and write this code:
#!/usr/bin/env python
#-*- coding:utf-8 -*-
import Xlib.display
from Xlib import X
def main():
display = Xlib.display.Display(':0')
root = display.screen().root
root.change_attributes(event_mask=Xlib.X.FocusChangeMask)
while True:
event = root.display.next_event()
#if event.type == X.FocusIn or event.type == X.FocusOut:
if event.type == X.FocusOut :
window = display.get_input_focus().focus
wmname = window.get_wm_name()
wmclass = window.get_wm_class()
if wmclass is None and wmname is None:
window = window.query_tree().parent
wmname = window.get_wm_name()
print "WM Name: %s" % ( wmname, )
if __name__ == "__main__":
main()
Sadly it's not work correctly especially in tabbed browsing on google chrome and firefox, so Is there a correct way for this situation?
Your code is almost right, but it misses two things:
rather than listening only to focus changes, it should also listen to window property events which include changes of WM_NAME property, that also happen when you cycle tabs in your browser.
rather than listening only in root window, it should listen to every window (that gets focused). You can attach the event handler the same way as you do with the root window.
That being said, here is a working sample:
#!/usr/bin/python3
import Xlib
import Xlib.display
disp = Xlib.display.Display()
root = disp.screen().root
NET_WM_NAME = disp.intern_atom('_NET_WM_NAME')
NET_ACTIVE_WINDOW = disp.intern_atom('_NET_ACTIVE_WINDOW')
root.change_attributes(event_mask=Xlib.X.FocusChangeMask)
while True:
try:
window_id = root.get_full_property(NET_ACTIVE_WINDOW, Xlib.X.AnyPropertyType).value[0]
window = disp.create_resource_object('window', window_id)
window.change_attributes(event_mask=Xlib.X.PropertyChangeMask)
window_name = window.get_full_property(NET_WM_NAME, 0).value
except Xlib.error.XError: #simplify dealing with BadWindow
window_name = None
print(window_name)
event = disp.next_event()
#rr- As I just corrected elsewhere, you'll want to query both the current _NET_WM_NAME (UTF-8) and the legacy WM_NAME (non-UTF8) properties or the default xterm configuration will return no title.
I just posted a complete working example over on your Unix & Linux StackExchange question.
To avoid sending people on a cross-reference hunt, here's a copy of the code I posted there:
#!/usr/bin/python
from contextlib import contextmanager
import Xlib
import Xlib.display
disp = Xlib.display.Display()
root = disp.screen().root
NET_ACTIVE_WINDOW = disp.intern_atom('_NET_ACTIVE_WINDOW')
NET_WM_NAME = disp.intern_atom('_NET_WM_NAME') # UTF-8
WM_NAME = disp.intern_atom('WM_NAME') # Legacy encoding
last_seen = { 'xid': None, 'title': None }
#contextmanager
def window_obj(win_id):
"""Simplify dealing with BadWindow (make it either valid or None)"""
window_obj = None
if win_id:
try:
window_obj = disp.create_resource_object('window', win_id)
except Xlib.error.XError:
pass
yield window_obj
def get_active_window():
win_id = root.get_full_property(NET_ACTIVE_WINDOW,
Xlib.X.AnyPropertyType).value[0]
focus_changed = (win_id != last_seen['xid'])
if focus_changed:
with window_obj(last_seen['xid']) as old_win:
if old_win:
old_win.change_attributes(event_mask=Xlib.X.NoEventMask)
last_seen['xid'] = win_id
with window_obj(win_id) as new_win:
if new_win:
new_win.change_attributes(event_mask=Xlib.X.PropertyChangeMask)
return win_id, focus_changed
def _get_window_name_inner(win_obj):
"""Simplify dealing with _NET_WM_NAME (UTF-8) vs. WM_NAME (legacy)"""
for atom in (NET_WM_NAME, WM_NAME):
try:
window_name = win_obj.get_full_property(atom, 0)
except UnicodeDecodeError: # Apparently a Debian distro package bug
title = "<could not decode characters>"
else:
if window_name:
win_name = window_name.value
if isinstance(win_name, bytes):
# Apparently COMPOUND_TEXT is so arcane that this is how
# tools like xprop deal with receiving it these days
win_name = win_name.decode('latin1', 'replace')
return win_name
else:
title = "<unnamed window>"
return "{} (XID: {})".format(title, win_obj.id)
def get_window_name(win_id):
if not win_id:
last_seen['title'] = "<no window id>"
return last_seen['title']
title_changed = False
with window_obj(win_id) as wobj:
if wobj:
win_title = _get_window_name_inner(wobj)
title_changed = (win_title != last_seen['title'])
last_seen['title'] = win_title
return last_seen['title'], title_changed
def handle_xevent(event):
if event.type != Xlib.X.PropertyNotify:
return
changed = False
if event.atom == NET_ACTIVE_WINDOW:
if get_active_window()[1]:
changed = changed or get_window_name(last_seen['xid'])[1]
elif event.atom in (NET_WM_NAME, WM_NAME):
changed = changed or get_window_name(last_seen['xid'])[1]
if changed:
handle_change(last_seen)
def handle_change(new_state):
"""Replace this with whatever you want to actually do"""
print(new_state)
if __name__ == '__main__':
root.change_attributes(event_mask=Xlib.X.PropertyChangeMask)
get_window_name(get_active_window()[0])
handle_change(last_seen)
while True: # next_event() sleeps until we get an event
handle_xevent(disp.next_event())
There's also a more heavily commented version in this GitHub Gist.