Coverage for /home/antoine/projects/xpra-git/dist/python3/lib64/python/xpra/x11/prop_conv.py : 67%
Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# This file is part of Xpra.
2# Copyright (C) 2008, 2009 Nathaniel Smith <njs@pobox.com>
3# Copyright (C) 2012-2019 Antoine Martin <antoine@xpra.org>
4# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
5# later version. See the file COPYING for details.
7"""
8Functions for converting to and from X11 properties.
9 prop_encode
10 prop_decode
11"""
13import struct
14from io import BytesIO
16from xpra.os_util import hexstr
17from xpra.x11.bindings.window_bindings import constants #@UnresolvedImport
18from xpra.log import Logger
20log = Logger("x11", "window")
23USPosition = constants["USPosition"]
24PPosition = constants["PPosition"]
25PMaxSize = constants["PMaxSize"]
26PMinSize = constants["PMinSize"]
27PBaseSize = constants["PBaseSize"]
28PResizeInc = constants["PResizeInc"]
29PAspect = constants["PAspect"]
30PWinGravity = constants["PWinGravity"]
31XUrgencyHint = constants["XUrgencyHint"]
32WindowGroupHint = constants["WindowGroupHint"]
33StateHint = constants["StateHint"]
34IconicState = constants["IconicState"]
35InputHint = constants["InputHint"]
38def unsupported(*_args):
39 raise Exception("unsupported")
41def _force_length(name, data, length, noerror_length=None):
42 if len(data)==length:
43 return data
44 if len(data)!=noerror_length:
45 log.warn("Odd-lengthed property %s: wanted %s bytes, got %s: %r"
46 % (name, length, len(data), data))
47 # Zero-pad data
48 data += b"\0" * length
49 return data[:length]
52class NetWMStrut:
53 def __init__(self, _disp, data):
54 # This eats both _NET_WM_STRUT and _NET_WM_STRUT_PARTIAL. If we are
55 # given a _NET_WM_STRUT instead of a _NET_WM_STRUT_PARTIAL, then it
56 # will be only length 4 instead of 12, we just don't define the other values
57 # and let the client deal with it appropriately
58 if len(data)==16:
59 self.left, self.right, self.top, self.bottom = struct.unpack(b"@LLLL", data)
60 else:
61 data = _force_length("_NET_WM_STRUT or _NET_WM_STRUT_PARTIAL", data, 4 * 12)
62 (
63 self.left, self.right, self.top, self.bottom,
64 self.left_start_y, self.left_end_y,
65 self.right_start_y, self.right_end_y,
66 self.top_start_x, self.top_end_x,
67 self.bottom_start_x, self.bottom_stop_x,
68 ) = struct.unpack(b"@" + b"L" * 12, data)
70 def todict(self):
71 return self.__dict__
73 def __str__(self):
74 return "NetWMStrut(%s)" % self.todict()
77class MotifWMHints:
78 def __init__(self, _disp, data):
79 #some applications use the wrong size (ie: blender uses 16) so pad it:
80 sizeof_long = struct.calcsize(b"@L")
81 pdata = _force_length("_MOTIF_WM_HINTS", data, sizeof_long*5, sizeof_long*4)
82 self.flags, self.functions, self.decorations, self.input_mode, self.status = \
83 struct.unpack(b"@LLLlL", pdata)
84 log("MotifWMHints(%s)=%s", hexstr(data), self)
86 #found in mwmh.h:
87 # "flags":
88 FUNCTIONS_BIT = 0
89 DECORATIONS_BIT = 1
90 INPUT_MODE_BIT = 2
91 STATUS_BIT = 3
92 # "functions":
93 ALL_BIT = 0
94 RESIZE_BIT = 1
95 MOVE_BIT = 2 # like _NET_WM_ACTION_MOVE
96 MINIMIZE_BIT = 3 # like _NET_WM_ACTION_MINIMIZE
97 MAXIMIZE_BIT = 4 # like _NET_WM_ACTION_(FULLSCREEN|MAXIMIZE_(HORZ|VERT))
98 CLOSE_BIT = 5 # like _NET_WM_ACTION_CLOSE
99 SHADE_BIT = 6 # like _NET_WM_ACTION_SHADE
100 STICK_BIT = 7 # like _NET_WM_ACTION_STICK
101 FULLSCREEN_BIT = 8 # like _NET_WM_ACTION_FULLSCREEN
102 ABOVE_BIT = 9 # like _NET_WM_ACTION_ABOVE
103 BELOW_BIT = 10 # like _NET_WM_ACTION_BELOW
104 MAXIMUS_BIT = 11 # like _NET_WM_ACTION_MAXIMUS_(LEFT|RIGHT|TOP|BOTTOM)
105 # "decorations":
106 ALL_BIT = 0
107 BORDER_BIT = 1
108 RESIZEH_BIT = 2
109 TITLE_BIT = 3
110 MENU_BIT = 4
111 MINIMIZE_BIT = 5
112 MAXIMIZE_BIT = 6
113 #CLOSE_BIT # non-standard close button
114 #RESIZE_BIT # non-standard resize button
115 #SHADE_BIT, # non-standard shade button
116 #STICK_BIT, # non-standard stick button
117 #MAXIMUS_BIT # non-standard maxim
118 # "input":
119 MODELESS = 0
120 PRIMARY_APPLICATION_MODAL = 1
121 SYSTEM_MODAL = 2
122 FULL_APPLICATION_MODAL = 3
123 # "status":
124 TEAROFF_WINDOW = 0
126 FLAGS_STR = {
127 FUNCTIONS_BIT : "functions",
128 DECORATIONS_BIT : "decorations",
129 INPUT_MODE_BIT : "input",
130 STATUS_BIT : "status",
131 }
132 FUNCTIONS_STR = {
133 ALL_BIT : "all",
134 RESIZE_BIT : "resize",
135 MOVE_BIT : "move",
136 MINIMIZE_BIT : "minimize",
137 MAXIMIZE_BIT : "maximize",
138 CLOSE_BIT : "close",
139 SHADE_BIT : "shade",
140 STICK_BIT : "stick",
141 FULLSCREEN_BIT : "fullscreen",
142 ABOVE_BIT : "above",
143 BELOW_BIT : "below",
144 MAXIMUS_BIT : "maximus",
145 }
146 DECORATIONS_STR = {
147 ALL_BIT : "all",
148 BORDER_BIT : "border",
149 RESIZEH_BIT : "resizeh",
150 TITLE_BIT : "title",
151 MENU_BIT : "menu",
152 MINIMIZE_BIT : "minimize",
153 MAXIMIZE_BIT : "maximize",
154 }
155 INPUT_STR = {
156 MODELESS : "modeless",
157 PRIMARY_APPLICATION_MODAL : "primary-application-modal",
158 SYSTEM_MODAL : "system-modal",
159 FULL_APPLICATION_MODAL : "full-application-modal",
160 }
162 STATUS_STR = {
163 TEAROFF_WINDOW : "tearoff",
164 }
166 def bits_to_strs(self, int_val, flag_bit, dict_str):
167 if flag_bit and not self.flags & (2**flag_bit):
168 #the bit is not set, ignore this attribute
169 return ()
170 return tuple(v for k,v in dict_str.items() if int_val & (2**k))
171 def flags_strs(self):
172 return self.bits_to_strs(self.flags,
173 0,
174 MotifWMHints.FLAGS_STR)
175 def functions_strs(self):
176 return self.bits_to_strs(self.functions,
177 MotifWMHints.FUNCTIONS_BIT,
178 MotifWMHints.FUNCTIONS_STR)
179 def decorations_strs(self):
180 return self.bits_to_strs(self.decorations,
181 MotifWMHints.DECORATIONS_BIT,
182 MotifWMHints.DECORATIONS_STR)
183 def input_strs(self):
184 if self.flags & (2**MotifWMHints.INPUT_MODE_BIT):
185 return MotifWMHints.INPUT_STR.get(self.input_mode, "unknown mode: %i" % self.input_mode)
186 return "modeless"
187 def status_strs(self):
188 return self.bits_to_strs(self.input_mode,
189 MotifWMHints.STATUS_BIT,
190 MotifWMHints.STATUS_STR)
192 def __str__(self):
193 return "MotifWMHints(%s)" % {
194 "flags" : self.flags_strs(),
195 "functions" : self.functions_strs(),
196 "decorations" : self.decorations_strs(),
197 "input_mode" : self.input_strs(),
198 "status" : self.status_strs(),
199 }
202def _read_image(_disp, stream):
203 try:
204 int_size = struct.calcsize(b"@I")
205 long_size = struct.calcsize(b"@L")
206 header = stream.read(long_size*2)
207 if not header:
208 return None
209 width, height = struct.unpack(b"@LL", header)
210 data = stream.read(width * height * long_size)
211 expected = width * height * long_size
212 if len(data) < expected:
213 log.warn("Warning: corrupt _NET_WM_ICON, execpted %i bytes but got %i",
214 expected, len(data))
215 return None
216 if int_size!=long_size:
217 #long to ints (CARD32):
218 longs = struct.unpack(b"@"+b"l"*(width*height), data)
219 data = struct.pack(b"@"+b"i"*(width*height), *longs)
220 except Exception:
221 log.warn("Weird corruption in _NET_WM_ICON", exc_info=True)
222 return None
223 return width, height, "BGRA", data
225# This returns a list of icons from a _NET_WM_ICON property.
226# each icon is a tuple:
227# (width, height, fmt, data)
228def NetWMIcons(disp, data):
229 icons = []
230 stream = BytesIO(data)
231 while True:
232 icon = _read_image(disp, stream)
233 if icon is None:
234 break
235 icons.append(icon)
236 if not icons:
237 return None
238 return icons
241def _to_latin1(_disp, v):
242 return v.encode("latin1")
244def _from_latin1(_disp, v):
245 return v.decode("latin1")
247def _to_utf8(_disp, v):
248 return v.encode("UTF-8")
250def _from_utf8(_disp, v):
251 return v.decode("UTF-8")
254def _from_long(_disp, v):
255 return struct.unpack(b"@L", v)[0]
257def _to_long(_disp, v):
258 return struct.pack(b"@L", v)
261PROP_TYPES = {
262 # Python type, X type Atom, formatbits, serializer, deserializer, list
263 # terminator
264 "utf8": (str, "UTF8_STRING", 8, _to_utf8, _from_utf8, b"\0"),
265 # In theory, there should be something clever about COMPOUND_TEXT here. I
266 # am not sufficiently clever to deal with COMPOUNT_TEXT. Even knowing
267 # that Xutf8TextPropertyToTextList exists.
268 "latin1": (str, "STRING", 8, _to_latin1, _from_latin1, b"\0"),
269 "state": (int, "WM_STATE", 32, _to_long, _from_long, b""),
270 "u32": (int, "CARDINAL", 32, _to_long, _from_long, b""),
271 "integer": (int, "INTEGER", 32, _to_long, _from_long, b""),
272 "strut": (NetWMStrut, "CARDINAL", 32,
273 unsupported, NetWMStrut, None),
274 "strut-partial": (NetWMStrut, "CARDINAL", 32,
275 unsupported, NetWMStrut, None),
276 "motif-hints": (MotifWMHints, "_MOTIF_WM_HINTS", 32,
277 unsupported, MotifWMHints, None),
278 "icons": (list, "CARDINAL", 32,
279 unsupported, NetWMIcons, None),
280 }
282PROP_SIZES = {
283 "icons" : 4*1024*1024,
284 }
287def prop_encode(disp, etype, value):
288 if isinstance(etype, (list, tuple)):
289 return _prop_encode_list(disp, etype[0], value)
290 return _prop_encode_scalar(disp, etype, value)
292def _prop_encode_scalar(disp, etype, value):
293 pytype, atom, formatbits, serialize = PROP_TYPES[etype][:4]
294 assert isinstance(value, pytype), "value for atom %s is not a %s: %s" % (atom, pytype, type(value))
295 return (atom, formatbits, serialize(disp, value))
297def _prop_encode_list(disp, etype, value):
298 (_, atom, formatbits, _, _, terminator) = PROP_TYPES[etype]
299 value = tuple(value)
300 serialized = tuple(_prop_encode_scalar(disp, etype, v)[2] for v in value)
301 # Strings in X really are null-separated, not null-terminated (ICCCM
302 # 2.7.1, see also note in 4.1.2.5)
303 return (atom, formatbits, terminator.join(x for x in serialized if x is not None))
306def prop_decode(disp, etype, data):
307 if isinstance(etype, (list, tuple)):
308 return _prop_decode_list(disp, etype[0], data)
309 return _prop_decode_scalar(disp, etype, data)
311def _prop_decode_scalar(disp, etype, data):
312 (pytype, _, _, _, deserialize, _) = PROP_TYPES[etype]
313 value = deserialize(disp, data)
314 assert value is None or isinstance(value, pytype), "expected a %s but value is a %s" % (pytype, type(value))
315 return value
317def _prop_decode_list(disp, etype, data):
318 (_, _, formatbits, _, _, terminator) = PROP_TYPES[etype]
319 if terminator:
320 datums = data.split(terminator)
321 else:
322 datums = []
323 if formatbits==32:
324 nbytes = struct.calcsize("@L")
325 else:
326 nbytes = formatbits // 8
327 while data:
328 datums.append(data[:nbytes])
329 data = data[nbytes:]
330 props = (_prop_decode_scalar(disp, etype, datum) for datum in datums)
331 return [x for x in props if x is not None]