Ben Santora

Ben Santora, Linux Tools Developer
Linux Minimalists

Some of us never left the small, fast corner of Linux. We run CrunchBang++ or BunsenLabs, or the Debian base system with Openbox and a panel, and nothing else. No compositor effects, no animated menus, no fancy desktop environment using half a GB of RAM before our first task is even run. We like the terminal, and we like knowing what every process on the machine is doing.

We use plain Linux commands and flags, and we run small language models on our own hardware the same way, from the command line. Openbox and Thunar are all the desktop we need. Every CPU cycle goes to the work we actually need done - that's the whole point of keeping it small.

I often have multiple terminals open and all running different processess. The icons pile up in my tint2 panel and I wanted a way to differentiate them. I started with the idea that I'd give any terminal running Claude Code a green dot.

Four identical terminal icons in a panel, each with a question mark: which one is Claude?

Four Terminal Sessions Open - Which is Running Claude?

The Script:  mark is a 30-line script that puts a colored dot on a terminal's taskbar icon, so your Claude session, your build and your htop each look different without reading a single title.

The same four terminal icons, one with a green dot: oh, that one.

Watch the demo on YouTube - under a minute, no sound.

It talks straight to X11, with no daemon and no config. Add a one-line function to .bashrc and the dot appears when a program starts and clears when it exits. It takes two minutes to set up and works with Openbox and tint2.

It needs python3, Pillow and ImageMagick, and X11 only.

Code it:

terminal
mark limegreen    # green dot on this terminal's icon
mark orange       # any color name works
mark reset        # back to the normal icon

To get a dot automatically while a program runs, wrap it in a function in ~/.bashrc:

~/.bashrc
htop() {
    local w; w=$(xdotool getactivewindow 2>/dev/null)
    WINDOWID=$w ~/bin/mark orange 2>/dev/null
    command htop "$@"
    local rc=$?
    WINDOWID=$w ~/bin/mark reset 2>/dev/null
    return $rc
}

The script

Save this as ~/bin/mark and run chmod +x ~/bin/mark. Make sure ~/bin is in your PATH.

~/bin/mark
#!/usr/bin/env python3
# usage: mark [color]   -- puts a colored dot on this terminal's panel icon
import sys, os, subprocess
from PIL import Image, ImageDraw
col = sys.argv[1] if len(sys.argv) > 1 else "limegreen"
wid = os.environ.get("WINDOWID") or subprocess.check_output(["xdotool","getactivewindow"]).decode().strip()
reset = col == "reset"
svg = "/usr/share/icons/hicolor/scalable/apps/org.gnome.Terminal.svg"
png = "/tmp/mark_base.png"
if not reset: subprocess.check_call(["convert","-background","none","-density","600",svg,"-resize","64x64",png])
if not reset:
    im = Image.open(png).convert("RGBA").resize((64,64))
    d = ImageDraw.Draw(im)
    d.ellipse((30,30,62,62), fill=col, outline="black", width=3)
    vals = [64,64] + [(a<<24)|(r<<16)|(g<<8)|b for r,g,b,a in im.getdata()]
import ctypes
x = ctypes.CDLL("libX11.so.6")
x.XOpenDisplay.restype = ctypes.c_void_p
x.XInternAtom.restype = ctypes.c_ulong
x.XInternAtom.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_int]
x.XChangeProperty.argtypes = [ctypes.c_void_p, ctypes.c_ulong, ctypes.c_ulong, ctypes.c_ulong,
                              ctypes.c_int, ctypes.c_int, ctypes.c_void_p, ctypes.c_int]
dpy = x.XOpenDisplay(None)
if reset:
    x.XDeleteProperty.argtypes = [ctypes.c_void_p, ctypes.c_ulong, ctypes.c_ulong]
    x.XDeleteProperty(dpy, int(wid, 0), x.XInternAtom(dpy, b"_NET_WM_ICON", 0))
    x.XFlush(dpy); sys.exit(0)
arr = (ctypes.c_ulong * len(vals))(*vals)   # format 32 => array of C long
x.XChangeProperty(dpy, int(wid, 0), x.XInternAtom(dpy, b"_NET_WM_ICON", 0),
                  x.XInternAtom(dpy, b"CARDINAL", 0), 32, 0, arr, len(vals))
x.XFlush(dpy)

It also needs xdotool for finding the active window. The icon is drawn from the GNOME Terminal SVG, so change the svg path if you use a different terminal.

OK - I admit that implementing this is not very minimal! But it does qualify once it's in place. X11 only. Wayland users, you don't need this.