@@ -33,6 +33,9 @@ import time
33
33
34
34
import xcffib
35
35
from xcffib import xproto
36
+ from Xlib .display import Display
37
+ from Xlib import X
38
+ from Xlib .error import XError
36
39
37
40
ICON_MAX_SIZE = 256
38
41
@@ -49,6 +52,7 @@ class IconRetriever(object):
49
52
self .conn = xcffib .connect ()
50
53
self .setup = self .conn .get_setup ()
51
54
self .root = self .setup .roots [0 ].root
55
+ self .display = Display ()
52
56
53
57
# just created windows for which icon wasn't sent yet - should
54
58
# be send on MapNotifyEvent
@@ -60,6 +64,7 @@ class IconRetriever(object):
60
64
def disconnect (self ):
61
65
log .info ('disconnecting from X' )
62
66
self .conn .disconnect ()
67
+ self .display .close ()
63
68
64
69
def watch_window (self , w ):
65
70
self .conn .core .ChangeWindowAttributesChecked (
@@ -80,9 +85,102 @@ class IconRetriever(object):
80
85
except xproto .BadWindow :
81
86
# Window disappeared in the meantime
82
87
raise NoIconError ()
83
-
84
88
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
+
86
184
# convert it later to a proper int array
87
185
icon_data = icon .value .buf ()
88
186
if icon .bytes_after :
0 commit comments