#!/usr/bin/python
import pygtk
import gtk
import warnings
from sys import argv, exit
from struct import pack, unpack
from optparse import OptionParser

parser = OptionParser()
parser.add_option("--graphic", "--img", "--image", type="string",
	dest="imagefile", metavar="FILE",
	help="Image file for icon")
parser.add_option("--id", type="int", dest="window_id",
	help="Window ID whose icon is being set")
parser.add_option("-v","--verbose")
parser.add_option("-s","--silent","-q","--quiet")
(options, args) = parser.parse_args()
if not options.imagefile and len(args):
	options.imagefile = args[0]
	args = args[1:]
if not options.window_id and len(args):
	options.window_id = int(args[0], 0)
	args = args[1:]

def err(msg=None):
	if msg and not options.silent:
		print msg
	exit(1)

pixmap = gtk.gdk.pixbuf_new_from_file(options.imagefile)
if not pixmap:
	err("Couldn't load pixmap")

warnings.filterwarnings('ignore','PyArray_FromDimsAndDataAndDescr',DeprecationWarning)
pixels = pixmap.get_pixels_array()

prop = []
prop += [len(pixels[0])]
prop += [len(pixels   )]

for row in pixels:
	for p in row:
		p = p.tolist()
		if len(p) < 4:
			p += [255]
		p = p[3:] + p[:-1]
		argb = int(0)
		for i in range(len(p)):
			argb += p[i] << (8 * (len(p)-i-1))
		prop += unpack("i",pack("I",argb))

window = gtk.gdk.window_foreign_new(options.window_id)
if not window:
	err("Couldn't open window")
window.property_delete("_NET_WM_ICON")
window.property_change("_NET_WM_ICON","CARDINAL",32,gtk.gdk.PROP_MODE_REPLACE,prop)

wmhints = window.property_get("WM_HINTS")
if wmhints:
	wmhints_type,wmhints_fmt,wmhints_tuple=wmhints
	wmhints_flags = wmhints_tuple[0]
	for member in [2,3,5]:
		if wmhints_flags & (1 << member):
			wmhints_flags ^= 1 << member
			wmhints_tuple[member+1] = 0
	window.property_change("WM_HINTS",wmhints_type,wmhints_fmt,gtk.gdk.PROP_MODE_REPLACE,wmhints_tuple)

@@ -15,20 +15,64 @@
 hon%0A
-import pygtk
+# simple program for setting a window's icon dynamically
 %0Aimp
@@ -189,16 +189,37 @@
 Parser%0A%0A
+# parse some options%0A
 parser =
@@ -586,16 +586,75 @@
 e_args()
+%0A%0A# allow image file and window ID to be given positionally
 %0Aif not 
@@ -827,16 +827,47 @@
 gs%5B1:%5D%0A%0A
+# really simple error handling%0A
 def err(
@@ -931,16 +931,74 @@
 xit(1)%0A%0A
+# use GDK to load a graphics file into an array of pixels%0A
 pixmap =
@@ -1090,17 +1090,16 @@
 ixmap%22)%0A
-%0A
 warnings
@@ -1217,68 +1217,242 @@
 ()%0A%0A
-prop = %5B%5D%0Aprop += %5Blen(pixels%5B0%5D)%5D%0Aprop += %5Blen(pixels   )%5D%0A
+# prop will contain an array of 32-bit integers%0A# starting with width, height, then pixel information%0Aprop = %5B%5D%0Aprop += %5Blen(pixels%5B0%5D)%5D%0Aprop += %5Blen(pixels   )%5D%0A%0A# pixel information is stored in the odd order: alpha, red, green, blue
 %0Afor
@@ -1669,16 +1669,86 @@
 argb))%0A%0A
+# grab a handle to the window, then delete and reset its _NET_WM_ICON%0A
 window =
@@ -1962,16 +1962,192 @@
 ,prop)%0A%0A
+# many window managers need a hint that the icon has changed%0A# bits 2, 3, and 5 of the WM_HINTS flags int are, respectively:%0A# IconPixmapHint, IconWindowHint, and IconMaskHint%0A
 wmhints 

add comments