@@ -33,6 +33,9 @@ import time
3333
3434import xcffib
3535from xcffib import xproto
36+ from Xlib .display import Display
37+ from Xlib import X
38+ from Xlib .error import XError
3639
3740ICON_MAX_SIZE = 256
3841
@@ -49,6 +52,7 @@ class IconRetriever(object):
4952 self .conn = xcffib .connect ()
5053 self .setup = self .conn .get_setup ()
5154 self .root = self .setup .roots [0 ].root
55+ self .display = Display ()
5256
5357 # just created windows for which icon wasn't sent yet - should
5458 # be send on MapNotifyEvent
@@ -60,6 +64,7 @@ class IconRetriever(object):
6064 def disconnect (self ):
6165 log .info ('disconnecting from X' )
6266 self .conn .disconnect ()
67+ self .display .close ()
6368
6469 def watch_window (self , w ):
6570 self .conn .core .ChangeWindowAttributesChecked (
@@ -80,9 +85,89 @@ class IconRetriever(object):
8085 except xproto .BadWindow :
8186 # Window disappeared in the meantime
8287 raise NoIconError ()
83-
8488 if icon .format == 0 :
85- raise NoIconError ()
89+ # Ancient X11 applications (Xterm, Xcalc, Xlogo, Xeyes, Xclock, ...)
90+ try :
91+ window = self .display .create_resource_object ('window' , w )
92+ wm_hints = window .get_wm_hints ()
93+ if not wm_hints :
94+ raise NoIconError ()
95+ geometry = wm_hints .icon_pixmap .get_geometry ()
96+ pixmap = wm_hints .icon_pixmap .get_image (
97+ 0 ,
98+ 0 ,
99+ geometry .width ,
100+ geometry .height ,
101+ X .ZPixmap ,
102+ 0xFFFFFFFF
103+ )
104+ pixmap_data = pixmap .data
105+ icons = {}
106+ try :
107+ mask = wm_hints .icon_mask .get_image (
108+ 0 ,
109+ 0 ,
110+ geometry .width ,
111+ geometry .height ,
112+ X .ZPixmap ,
113+ 0xFFFFFFFF
114+ )
115+ mask_data = mask .data
116+ except :
117+ # Icons without transparency
118+ mask = None
119+ mask_data = []
120+ except XError :
121+ raise NoIconError ()
122+
123+ # Finally we have the required data to construct icon
124+ if geometry .depth == 1 :
125+ # 1 bit per pixel icons (i.e. xlogo, xeyes, xcalc, ...)
126+ icon_data = []
127+ # There might be trailing bytes at the end of each row since
128+ # each row should be multiples of 32 bits
129+ row_width = int (len (pixmap_data ) / geometry .height )
130+ for y in range (0 , geometry .height ):
131+ offset = y * row_width
132+ for x in range (0 , geometry .width ):
133+ byte_offset = int (x / 8 ) + offset
134+ byte = int (pixmap_data [byte_offset ])
135+ byte = byte >> (x % 8 )
136+ bit = byte & 0x1
137+ if bit :
138+ # Can not decide the light/dark theme from vmside :/
139+ icon_data .append (0xff7f7f7f )
140+ else :
141+ icon_data .append (0x0 )
142+ elif geometry .depth == 24 :
143+ # 24 bit per pixel icons (i.e. Xterm)
144+ # Technically this could handle other programs as well
145+ icon_data = struct .unpack (
146+ "%dI" % (len (pixmap_data ) / 4 ),
147+ pixmap_data
148+ )
149+ icon_data = [d | 0xff000000 for d in icon_data ]
150+ else :
151+ # Could not find 8 bit icons of that era to work with
152+ raise NoIconError ()
153+ if mask_data :
154+ row_width = int (len (mask_data ) / geometry .height )
155+ for y in range (0 , geometry .height ):
156+ offset = y * row_width
157+ for x in range (0 , geometry .width ):
158+ byte_offset = int (x / 8 ) + offset
159+ byte = int (mask_data [byte_offset ])
160+ byte = byte >> (x % 8 )
161+ bit = byte & 0x1
162+ pixel = x + y * geometry .height
163+ if bit :
164+ icon_data [pixel ] = icon_data [pixel ] & 0xffffffff
165+ else :
166+ icon_data [pixel ] = icon_data [pixel ] & 0x00ffffff
167+ size = (geometry .width , geometry .height )
168+ icons [size ] = icon_data
169+ return icons
170+
86171 # convert it later to a proper int array
87172 icon_data = icon .value .buf ()
88173 if icon .bytes_after :
0 commit comments