@@ -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,102 @@ 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_geometry = wm_hints .icon_mask .get_geometry ()
116+ mask_data = mask .data
117+ except :
118+ # Icons without transparency
119+ mask = None
120+ mask_geometry = None
121+ mask_data = []
122+ except XError :
123+ raise NoIconError ()
124+
125+ # Finally we have the required data to construct icon
126+ if geometry .depth == 1 :
127+ # 1 bit per pixel icons (i.e. xlogo, xeyes, xcalc, ...)
128+ icon_data = []
129+ # There might be trailing bytes at the end of each row since
130+ # each row should be multiples of 32 bits
131+ row_width = int (len (pixmap_data ) / geometry .height )
132+ for y in range (0 , geometry .height ):
133+ offset = y * row_width
134+ for x in range (0 , geometry .width ):
135+ byte_offset = int (x / 8 ) + offset
136+ byte = int (pixmap_data [byte_offset ])
137+ byte = byte >> (x % 8 )
138+ bit = byte & 0x1
139+ if bit :
140+ # Can not decide the light/dark theme from vmside :/
141+ icon_data .append (0xff7f7f7f )
142+ else :
143+ icon_data .append (0x0 )
144+ elif geometry .depth == 24 :
145+ # 24 bit per pixel icons (i.e. Xterm)
146+ # Technically this could handle other programs as well
147+ icon_data = struct .unpack (
148+ "%dI" % (len (pixmap_data ) / 4 ),
149+ pixmap_data
150+ )
151+ icon_data = [d | 0xff000000 for d in icon_data ]
152+ else :
153+ # Could not find 8 bit icons of that era to work with
154+ raise NoIconError ()
155+ if mask_data and mask_geometry .depth == 1 :
156+ # Even Xterm uses 1 bit/pixel mask. I do not know why
157+ row_width = int (len (mask_data ) / geometry .height )
158+ for y in range (0 , geometry .height ):
159+ offset = y * row_width
160+ for x in range (0 , geometry .width ):
161+ byte_offset = int (x / 8 ) + offset
162+ byte = int (mask_data [byte_offset ])
163+ byte = byte >> (x % 8 )
164+ bit = byte & 0x1
165+ pixel = x + y * geometry .height
166+ if bit :
167+ icon_data [pixel ] = icon_data [pixel ] & 0xffffffff
168+ else :
169+ icon_data [pixel ] = icon_data [pixel ] & 0x00ffffff
170+ elif mask_data and mask_geometry .depth == 8 :
171+ # Technically this is not tested (No X prog uses 8bit/pix mask)
172+ # At least not on Qubes OS 4.3 & default Xfwm4
173+ for y in range (0 , geometry .height ):
174+ offset = y * row_width
175+ for x in range (0 , geometry .width ):
176+ byte_offset = x + offset
177+ byte = int (mask_data [byte_offset ])
178+ pixmask = (byte < 24 ) | 0x00ffffff
179+ icon_data [pixel ] = icon_data [pixel ] & pixmask
180+ size = (geometry .width , geometry .height )
181+ icons [size ] = icon_data
182+ return icons
183+
86184 # convert it later to a proper int array
87185 icon_data = icon .value .buf ()
88186 if icon .bytes_after :
0 commit comments