Ticket #190: xpra-compoundtext-hooks.patch
File xpra-compoundtext-hooks.patch, 15.4 KB (added by , 9 years ago) |
---|
-
wimpiggy/lowlevel/bindings.pyx
474 479 pass 475 480 class NoSuchProperty(PropertyError): 476 481 pass 477 def XGetWindowProperty(pywindow, property, req_type ):482 def XGetWindowProperty(pywindow, property, req_types): 478 483 # NB: Accepts req_type == 0 for AnyPropertyType 479 484 # "64k is enough for anybody" 480 485 # (Except, I've found window icons that are strictly larger, hence the … … 485 490 cdef unsigned long nitems = 0, bytes_after = 0 486 491 cdef unsigned char * prop = <unsigned char*> 0 487 492 cdef Status status 488 xreq_type = get_xatom(req_type) 493 xreq_type = 0 494 if len(req_types)>0: 495 xreq_type = get_xatom(req_types[0]) 489 496 # This is the most bloody awful API I have ever seen. You will probably 490 497 # not be able to understand this code fully without reading 491 498 # XGetWindowProperty's man page at least 3 times, slowly. … … 500 507 xreq_type, &xactual_type, 501 508 &actual_format, &nitems, &bytes_after, &prop) 502 509 if status != Success: 503 raise PropertyError , "no such window"510 raise PropertyError("no such window for %s" % pywindow) 504 511 if xactual_type == XNone: 505 raise NoSuchProperty, property 506 if xreq_type and xreq_type != xactual_type: 507 raise BadPropertyType, xactual_type 512 raise NoSuchProperty("%s on %s" % (property, pywindow)) 513 actual_type = get_pyatom(pywindow, xactual_type) 514 if req_types and actual_type not in req_types: 515 raise BadPropertyType("expected %s but found %s for %s" % (req_types, actual_type, property)) 508 516 # This should only occur for bad property types: 509 517 assert not (bytes_after and not nitems) 510 518 # actual_format is in (8, 16, 32), and is the number of bits in a logical … … 527 535 data = PyString_FromStringAndSize(<char *>prop, nbytes) 528 536 XFree(prop) 529 537 if actual_format == 32: 530 return _munge_packed_longs_to_ints(data)538 return actual_type, _munge_packed_longs_to_ints(data) 531 539 else: 532 return data540 return actual_type, data 533 541 534 542 def XDeleteProperty(pywindow, property): 535 543 cXDeleteProperty(get_xdisplay_for(pywindow), -
wimpiggy/prop.py
51 51 return data[:length] 52 52 53 53 class WMSizeHints(object): 54 def __init__(self, disp, data):54 def __init__(self, disp, t, data): 55 55 # pre-ICCCM size is 15 56 56 data = _force_length("WM_SIZE_HINTS", data, 18*4, noerror_length=15*4) 57 57 (flags, … … 92 92 self.min_aspect_ratio, self.max_aspect_ratio = (None, None) 93 93 94 94 class WMHints(object): 95 def __init__(self, disp, data):95 def __init__(self, disp, t, data): 96 96 data = _force_length("WM_HINTS", data, 9 * 4) 97 97 (flags, _input, initial_state, #@UnusedVariable 98 98 icon_pixmap, icon_window, #@UnusedVariable … … 115 115 self.input = None 116 116 117 117 class NetWMStrut(object): 118 def __init__(self, disp, data):118 def __init__(self, disp, t, data): 119 119 # This eats both _NET_WM_STRUT and _NET_WM_STRUT_PARTIAL. If we are 120 120 # given a _NET_WM_STRUT instead of a _NET_WM_STRUT_PARTIAL, then it 121 121 # will be only length 4 instead of 12, but _force_length will zero-pad … … 129 129 self.bottom_start_x, self.bottom_stop_x, 130 130 ) = struct.unpack("=" + "I" * 12, data) 131 131 132 def _read_image(disp, stream):132 def _read_image(disp, t, stream): 133 133 try: 134 134 header = stream.read(2 * 4) 135 135 if not header: … … 163 163 164 164 # This returns a cairo ImageSurface which contains the largest icon defined in 165 165 # a _NET_WM_ICON property. 166 def NetWMIcons(disp, data):166 def NetWMIcons(disp, t, data): 167 167 icons = [] 168 168 stream = StringIO(data) 169 169 while True: 170 size_image = _read_image(disp, stream)170 size_image = _read_image(disp, t, stream) 171 171 if size_image is None: 172 172 break 173 173 icons.append(size_image) … … 176 176 icons.sort() 177 177 return icons[-1][1] 178 178 179 def _get_atom(disp, d):179 def _get_atom(disp, t, d): 180 180 unpacked = struct.unpack("@I", d)[0] 181 181 pyatom = get_pyatom(disp, unpacked) 182 182 if not pyatom: … … 184 184 return None 185 185 return str(pyatom) 186 186 187 def _get_multiple(disp, d):187 def _get_multiple(disp, t, d): 188 188 uint_struct = struct.Struct("@I") 189 189 log("get_multiple struct size=%s, len(%s)=%s", uint_struct.size, d, len(d)) 190 190 if len(d)!=uint_struct.size and False: 191 191 log.info("get_multiple value is not an atom: %s", d) 192 192 return str(d) 193 return _get_atom(disp, d)193 return _get_atom(disp, t, d) 194 194 195 195 196 196 def set_xsettings_format(use_tuple=True): 197 197 log("set_xsettings_format(%s)", use_tuple) 198 198 global _prop_types 199 199 if use_tuple: 200 _prop_types["xsettings-settings"] = (tuple, "_XSETTINGS_SETTINGS", 8,200 _prop_types["xsettings-settings"] = (tuple, ["_XSETTINGS_SETTINGS"], 8, 201 201 set_settings, 202 202 get_settings, 203 203 None) 204 204 else: 205 205 #for old clients that rely on the old raw string format: 206 _prop_types["xsettings-settings"] = (str, "_XSETTINGS_SETTINGS", 8,206 _prop_types["xsettings-settings"] = (str, ["_XSETTINGS_SETTINGS"], 8, 207 207 lambda disp, c: c, 208 208 lambda disp, d: d, 209 209 None) 210 210 211 def encode_latin1(disp, t, u): 212 log.info("encode_latin1(%s,%s,%s)", disp, t, u) 213 return u.encode("latin1") 214 def decode_latin1(disp, t, d): 215 log.info("decode_latin1(%s,%s,%s)", disp, t, d) 216 if t=="STRING": 217 return d.decode("latin1") 218 elif t=="COMPOUND_TEXT": 219 return "VBLA" 220 else: 221 raise Exception("invalid encoding type: %s" % t) 211 222 223 212 224 _prop_types = { 213 225 # Python type, X type Atom, formatbits, serializer, deserializer, list 214 226 # terminator 215 "utf8": (unicode, "UTF8_STRING", 8,216 lambda disp, u: u.encode("UTF-8"),217 lambda disp, d: d.decode("UTF-8"),218 "\0"),227 "utf8": (unicode, ["UTF8_STRING"], 8, 228 lambda disp, t, u: u.encode("UTF-8"), 229 lambda disp, t, d: d.decode("UTF-8"), 230 "\0"), 219 231 # In theory, there should be something clever about COMPOUND_TEXT here. I 220 232 # am not sufficiently clever to deal with COMPOUNT_TEXT. Even knowing 221 233 # that Xutf8TextPropertyToTextList exists. 222 "latin1": (unicode, "STRING", 8,223 lambda disp, u: u.encode("latin1"),224 lambda disp, d: d.decode("latin1"),225 "\0"),226 "atom": (str, "ATOM", 32,227 lambda disp, a: struct.pack("@I", get_xatom(a)),228 _get_atom,229 ""),230 "u32": ((int, long), "CARDINAL", 32,231 lambda disp, c: struct.pack("=I", c),232 lambda disp, d: struct.unpack("=I", d)[0],233 ""),234 "window": (gtk.gdk.Window, "WINDOW", 32,235 lambda disp, c: struct.pack("=I", get_xwindow(c)),236 lambda disp, d: get_pywindow(disp, struct.unpack("=I", d)[0]),237 ""),238 "wm-size-hints": (WMSizeHints, "WM_SIZE_HINTS", 32,234 "latin1": (unicode, ["STRING", "COMPOUND_TEXT"], 8, 235 encode_latin1, 236 decode_latin1, 237 "\0"), 238 "atom": (str, ["ATOM"], 32, 239 lambda disp, t, a: struct.pack("@I", get_xatom(a)), 240 _get_atom, 241 ""), 242 "u32": ((int, long), ["CARDINAL"], 32, 243 lambda disp, t, c: struct.pack("=I", c), 244 lambda disp, t, d: struct.unpack("=I", d)[0], 245 ""), 246 "window": (gtk.gdk.Window, ["WINDOW"], 32, 247 lambda disp, t, c: struct.pack("=I", get_xwindow(c)), 248 lambda disp, t, d: get_pywindow(disp, struct.unpack("=I", d)[0]), 249 ""), 250 "wm-size-hints": (WMSizeHints, ["WM_SIZE_HINTS"], 32, 239 251 unsupported, 240 252 WMSizeHints, 241 253 None), 242 "wm-hints": (WMHints, "WM_HINTS", 32, 243 unsupported, 244 WMHints, 245 None), 246 "strut": (NetWMStrut, "CARDINAL", 32, 247 unsupported, NetWMStrut, None), 248 "strut-partial": (NetWMStrut, "CARDINAL", 32, 249 unsupported, NetWMStrut, None), 250 "icon": (cairo.ImageSurface, "CARDINAL", 32, 251 unsupported, NetWMIcons, None), 252 "xsettings-settings": (tuple, "_XSETTINGS_SETTINGS", 8, 253 set_settings, 254 get_settings, 255 None), 254 "wm-hints": (WMHints, ["WM_HINTS"], 32, 255 unsupported, 256 WMHints, 257 None), 258 "strut": (NetWMStrut, ["CARDINAL"], 32, 259 unsupported, 260 NetWMStrut, 261 None), 262 "strut-partial": (NetWMStrut, ["CARDINAL"], 32, 263 unsupported, 264 NetWMStrut, 265 None), 266 "icon": (cairo.ImageSurface, ["CARDINAL"], 32, 267 unsupported, 268 NetWMIcons, 269 None), 270 "xsettings-settings": (tuple, ["_XSETTINGS_SETTINGS"], 8, 271 set_settings, 272 get_settings, 273 None), 256 274 # For uploading ad-hoc instances of the above complex structures to the 257 275 # server, so we can test reading them out again: 258 "debug-CARDINAL": (str, "CARDINAL", 32,259 lambda disp, c: c,260 lambda disp, d: d,261 276 "debug-CARDINAL":(str, ["CARDINAL"], 32, 277 lambda disp, t, c: c, 278 lambda disp, t, d: d, 279 None), 262 280 # For fetching the extra information on a MULTIPLE clipboard conversion 263 281 # request. The exciting thing about MULTIPLE is that it's not actually 264 282 # specified what 'type' one should use; you just fetch with 265 283 # AnyPropertyType and assume that what you get is a bunch of pairs of 266 284 # atoms. 267 "multiple-conversion": (str, 0, 32, unsupported, _get_multiple, None), 285 "multiple-conversion": (str, [0], 32, 286 unsupported, 287 _get_multiple, 288 None), 268 289 } 269 290 270 291 def _prop_encode(disp, etype, value): … … 274 295 return _prop_encode_scalar(disp, etype, value) 275 296 276 297 def _prop_encode_scalar(disp, etype, value): 277 (pytype, atom, formatbits, serialize, _, _)= _prop_types[etype]278 assert isinstance(value, pytype), "value for atom %s is not a %s: %s" % (atom, pytype, type(value))279 return (atom, formatbits, serialize(disp, value))298 pytype, atoms, formatbits, serialize, _, _ = _prop_types[etype] 299 assert isinstance(value, pytype), "value for atoms %s is not a %s: %s" % (atoms, pytype, type(value)) 300 return atoms[0], formatbits, serialize(disp, atoms[0], value) 280 301 281 302 def _prop_encode_list(disp, etype, value): 282 (_, atom, formatbits, _, _, terminator)= _prop_types[etype]303 _, atoms, formatbits, _, _, terminator = _prop_types[etype] 283 304 value = list(value) 284 305 serialized = [_prop_encode_scalar(disp, etype, v)[2] for v in value] 285 306 no_none = [x for x in serialized if x is not None] 286 307 # Strings in X really are null-separated, not null-terminated (ICCCM 287 308 # 2.7.1, see also note in 4.1.2.5) 288 return (atom, formatbits, terminator.join(no_none))309 return atoms[0], formatbits, terminator.join(no_none) 289 310 290 311 291 312 def prop_set(target, key, etype, value): 292 trap.call_unsynced(XChangeProperty, target, key, 293 _prop_encode(target, etype, value)) 313 enc = _prop_encode(target, etype, value) 314 log.info("prop_set(%s,%s,%s,%s) enc=%s", target, key, etype, value, enc) 315 trap.call_unsynced(XChangeProperty, target, key, enc) 294 316 295 def _prop_decode(disp, etype, data):317 def _prop_decode(disp, actual_type, etype, data): 296 318 if isinstance(etype, list): 297 return _prop_decode_list(disp, etype[0], data)319 return _prop_decode_list(disp, actual_type, etype[0], data) 298 320 else: 299 return _prop_decode_scalar(disp, etype, data)321 return _prop_decode_scalar(disp, actual_type, etype, data) 300 322 301 def _prop_decode_scalar(disp, etype, data):302 (pytype, _, _, _, deserialize, _)= _prop_types[etype]303 value = deserialize(disp, data)323 def _prop_decode_scalar(disp, actual_type, etype, data): 324 pytype, _, _, _, deserialize, _ = _prop_types[etype] 325 value = deserialize(disp, actual_type, data) 304 326 assert value is None or isinstance(value, pytype), "expected a %s but value is a %s" % (pytype, type(value)) 305 327 return value 306 328 307 def _prop_decode_list(disp, etype, data):308 (_, _, formatbits, _, _, terminator)= _prop_types[etype]329 def _prop_decode_list(disp, actual_type, etype, data): 330 _, _, formatbits, _, _, terminator = _prop_types[etype] 309 331 if terminator: 310 332 datums = data.split(terminator) 311 333 else: … … 314 336 while data: 315 337 datums.append(data[:nbytes]) 316 338 data = data[nbytes:] 317 props = [_prop_decode_scalar(disp, etype, datum) for datum in datums]339 props = [_prop_decode_scalar(disp, actual_type, etype, datum) for datum in datums] 318 340 #assert None not in props 319 341 return [x for x in props if x is not None] 320 342 … … 324 346 scalar_type = etype[0] 325 347 else: 326 348 scalar_type = etype 327 (_, atom, _, _, _, _) = _prop_types[scalar_type] 349 log.info("prop_get(%s, %s, %s, %s) scalar_type=%s", target, key, etype, ignore_errors, scalar_type) 350 _, atoms, _, _, _, _ = _prop_types[scalar_type] 328 351 try: 329 352 #print(atom) 330 data = trap.call_synced(XGetWindowProperty, target, key, atom)353 actual_type, data = trap.call_synced(XGetWindowProperty, target, key, atoms) 331 354 #print(atom, repr(data[:100])) 332 355 except NoSuchProperty: 333 356 log.debug("Missing property %s (%s)", key, etype) … … 338 361 traceback.print_exc() 339 362 return None 340 363 try: 341 return _prop_decode(target, etype, data) 364 log.info("prop_get(..) actual_type=%s, data=%s", actual_type, data) 365 return _prop_decode(target, actual_type, etype, data) 342 366 except: 343 367 log.warn("Error parsing property %s (type %s); this may be a" 344 368 + " misbehaving application, or bug in Wimpiggy\n" -
wimpiggy/xsettings_prop.py
35 35 XSettingsTypeColor = 2 36 36 37 37 38 def get_settings(disp, d):38 def get_settings(disp, t, d): 39 39 #parse xsettings according to 40 40 #http://standards.freedesktop.org/xsettings-spec/xsettings-spec-0.5.html 41 41 assert len(d)>=12, "_XSETTINGS_SETTINGS property is too small: %s" % len(d) … … 77 77 debug("parse_xsettings(..) settings=%s", settings) 78 78 return serial, settings 79 79 80 def set_settings(disp, d):80 def set_settings(disp, t, d): 81 81 #TODO: detect old clients 82 82 assert len(d)==2, "invalid format for XSETTINGS: %s" % str(d) 83 83 serial, settings = d