Skip to content

Instantly share code, notes, and snippets.

@gatopeich
Last active February 19, 2026 16:24
Show Gist options
  • Select an option

  • Save gatopeich/9d0c61cae0a4e01b23b0afc543c27fbb to your computer and use it in GitHub Desktop.

Select an option

Save gatopeich/9d0c61cae0a4e01b23b0afc543c27fbb to your computer and use it in GitHub Desktop.
xseticon.py - Set X11 window icon using ctypes and PIL (no extra deps)
#!/usr/bin/env python3
"""Set X11 window icon from a PNG file using ctypes (no dependencies beyond PIL).
https://gist.github.com/gatopeich/9d0c61cae0a4e01b23b0afc543c27fbb
Author: gatopeich @ github
"""
import ctypes
import ctypes.util
import sys
from PIL import Image
def set_icon(window_id, icon_path):
x11 = ctypes.cdll.LoadLibrary(ctypes.util.find_library("X11"))
display = x11.XOpenDisplay(None)
if not display:
sys.exit("Cannot open display")
img = Image.open(icon_path).convert("RGBA")
w, h = img.size
data = [w, h]
for r, g, b, a in zip(*[iter(img.tobytes())] * 4):
data.append((a << 24) | (r << 16) | (g << 8) | b)
atom = x11.XInternAtom(display, b"_NET_WM_ICON", False)
cardinal = x11.XInternAtom(display, b"CARDINAL", False)
arr = (ctypes.c_ulong * len(data))(*data)
x11.XChangeProperty(
display, window_id, atom, cardinal, 32,
0, # PropModeReplace
ctypes.cast(arr, ctypes.POINTER(ctypes.c_ubyte)),
len(data),
)
x11.XFlush(display)
x11.XCloseDisplay(display)
if __name__ == "__main__":
if len(sys.argv) != 3:
sys.exit(f"Usage: {sys.argv[0]} <window_id> <icon.png>")
set_icon(int(sys.argv[1]), sys.argv[2])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment