xpra icon
Bug tracker and wiki

Ticket #190: xpra-compoundtext-hooks.patch

File xpra-compoundtext-hooks.patch, 15.4 KB (added by Antoine Martin, 8 years ago)

adds hooks to property get/set so we allow more than one encoding type and pass the actual encoding type used to the decoding functions

  • wimpiggy/lowlevel/bindings.pyx

     
    474479    pass
    475480class NoSuchProperty(PropertyError):
    476481    pass
    477 def XGetWindowProperty(pywindow, property, req_type):
     482def XGetWindowProperty(pywindow, property, req_types):
    478483    # NB: Accepts req_type == 0 for AnyPropertyType
    479484    # "64k is enough for anybody"
    480485    # (Except, I've found window icons that are strictly larger, hence the
     
    485490    cdef unsigned long nitems = 0, bytes_after = 0
    486491    cdef unsigned char * prop = <unsigned char*> 0
    487492    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])
    489496    # This is the most bloody awful API I have ever seen.  You will probably
    490497    # not be able to understand this code fully without reading
    491498    # XGetWindowProperty's man page at least 3 times, slowly.
     
    500507                                 xreq_type, &xactual_type,
    501508                                 &actual_format, &nitems, &bytes_after, &prop)
    502509    if status != Success:
    503         raise PropertyError, "no such window"
     510        raise PropertyError("no such window for %s" % pywindow)
    504511    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))
    508516    # This should only occur for bad property types:
    509517    assert not (bytes_after and not nitems)
    510518    # actual_format is in (8, 16, 32), and is the number of bits in a logical
     
    527535    data = PyString_FromStringAndSize(<char *>prop, nbytes)
    528536    XFree(prop)
    529537    if actual_format == 32:
    530         return _munge_packed_longs_to_ints(data)
     538        return actual_type, _munge_packed_longs_to_ints(data)
    531539    else:
    532         return data
     540        return actual_type, data
    533541
    534542def XDeleteProperty(pywindow, property):
    535543    cXDeleteProperty(get_xdisplay_for(pywindow),
  • wimpiggy/prop.py

     
    5151    return data[:length]
    5252
    5353class WMSizeHints(object):
    54     def __init__(self, disp, data):
     54    def __init__(self, disp, t, data):
    5555        # pre-ICCCM size is 15
    5656        data = _force_length("WM_SIZE_HINTS", data, 18*4, noerror_length=15*4)
    5757        (flags,
     
    9292            self.min_aspect_ratio, self.max_aspect_ratio = (None, None)
    9393
    9494class WMHints(object):
    95     def __init__(self, disp, data):
     95    def __init__(self, disp, t, data):
    9696        data = _force_length("WM_HINTS", data, 9 * 4)
    9797        (flags, _input, initial_state,  #@UnusedVariable
    9898         icon_pixmap, icon_window,      #@UnusedVariable
     
    115115            self.input = None
    116116
    117117class NetWMStrut(object):
    118     def __init__(self, disp, data):
     118    def __init__(self, disp, t, data):
    119119        # This eats both _NET_WM_STRUT and _NET_WM_STRUT_PARTIAL.  If we are
    120120        # given a _NET_WM_STRUT instead of a _NET_WM_STRUT_PARTIAL, then it
    121121        # will be only length 4 instead of 12, but _force_length will zero-pad
     
    129129         self.bottom_start_x, self.bottom_stop_x,
    130130         ) = struct.unpack("=" + "I" * 12, data)
    131131
    132 def _read_image(disp, stream):
     132def _read_image(disp, t, stream):
    133133    try:
    134134        header = stream.read(2 * 4)
    135135        if not header:
     
    163163
    164164# This returns a cairo ImageSurface which contains the largest icon defined in
    165165# a _NET_WM_ICON property.
    166 def NetWMIcons(disp, data):
     166def NetWMIcons(disp, t, data):
    167167    icons = []
    168168    stream = StringIO(data)
    169169    while True:
    170         size_image = _read_image(disp, stream)
     170        size_image = _read_image(disp, t, stream)
    171171        if size_image is None:
    172172            break
    173173        icons.append(size_image)
     
    176176    icons.sort()
    177177    return icons[-1][1]
    178178
    179 def _get_atom(disp, d):
     179def _get_atom(disp, t, d):
    180180    unpacked = struct.unpack("@I", d)[0]
    181181    pyatom = get_pyatom(disp, unpacked)
    182182    if not pyatom:
     
    184184        return  None
    185185    return str(pyatom)
    186186
    187 def _get_multiple(disp, d):
     187def _get_multiple(disp, t, d):
    188188    uint_struct = struct.Struct("@I")
    189189    log("get_multiple struct size=%s, len(%s)=%s", uint_struct.size, d, len(d))
    190190    if len(d)!=uint_struct.size and False:
    191191        log.info("get_multiple value is not an atom: %s", d)
    192192        return  str(d)
    193     return _get_atom(disp, d)
     193    return _get_atom(disp, t, d)
    194194
    195195
    196196def set_xsettings_format(use_tuple=True):
    197197    log("set_xsettings_format(%s)", use_tuple)
    198198    global _prop_types
    199199    if use_tuple:
    200         _prop_types["xsettings-settings"] = (tuple, "_XSETTINGS_SETTINGS", 8,
     200        _prop_types["xsettings-settings"] = (tuple, ["_XSETTINGS_SETTINGS"], 8,
    201201                           set_settings,
    202202                           get_settings,
    203203                           None)
    204204    else:
    205205        #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,
    207207                           lambda disp, c: c,
    208208                           lambda disp, d: d,
    209209                           None)
    210210
     211def encode_latin1(disp, t, u):
     212    log.info("encode_latin1(%s,%s,%s)", disp, t, u)
     213    return u.encode("latin1")
     214def 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)
    211222
     223
    212224_prop_types = {
    213225    # Python type, X type Atom, formatbits, serializer, deserializer, list
    214226    # 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"),
    219231    # In theory, there should be something clever about COMPOUND_TEXT here.  I
    220232    # am not sufficiently clever to deal with COMPOUNT_TEXT.  Even knowing
    221233    # 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,
    239251                      unsupported,
    240252                      WMSizeHints,
    241253                      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),
    256274    # For uploading ad-hoc instances of the above complex structures to the
    257275    # 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                        None),
     276    "debug-CARDINAL":(str, ["CARDINAL"], 32,
     277                      lambda disp, t, c: c,
     278                      lambda disp, t, d: d,
     279                      None),
    262280    # For fetching the extra information on a MULTIPLE clipboard conversion
    263281    # request. The exciting thing about MULTIPLE is that it's not actually
    264282    # specified what 'type' one should use; you just fetch with
    265283    # AnyPropertyType and assume that what you get is a bunch of pairs of
    266284    # atoms.
    267     "multiple-conversion": (str, 0, 32, unsupported, _get_multiple, None),
     285    "multiple-conversion": (str, [0], 32,
     286                      unsupported,
     287                      _get_multiple,
     288                      None),
    268289    }
    269290
    270291def _prop_encode(disp, etype, value):
     
    274295        return _prop_encode_scalar(disp, etype, value)
    275296
    276297def _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)
    280301
    281302def _prop_encode_list(disp, etype, value):
    282     (_, atom, formatbits, _, _, terminator) = _prop_types[etype]
     303    _, atoms, formatbits, _, _, terminator = _prop_types[etype]
    283304    value = list(value)
    284305    serialized = [_prop_encode_scalar(disp, etype, v)[2] for v in value]
    285306    no_none = [x for x in serialized if x is not None]
    286307    # Strings in X really are null-separated, not null-terminated (ICCCM
    287308    # 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)
    289310
    290311
    291312def 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)
    294316
    295 def _prop_decode(disp, etype, data):
     317def _prop_decode(disp, actual_type, etype, data):
    296318    if isinstance(etype, list):
    297         return _prop_decode_list(disp, etype[0], data)
     319        return _prop_decode_list(disp, actual_type, etype[0], data)
    298320    else:
    299         return _prop_decode_scalar(disp, etype, data)
     321        return _prop_decode_scalar(disp, actual_type, etype, data)
    300322
    301 def _prop_decode_scalar(disp, etype, data):
    302     (pytype, _, _, _, deserialize, _) = _prop_types[etype]
    303     value = deserialize(disp, data)
     323def _prop_decode_scalar(disp, actual_type, etype, data):
     324    pytype, _, _, _, deserialize, _ = _prop_types[etype]
     325    value = deserialize(disp, actual_type, data)
    304326    assert value is None or isinstance(value, pytype), "expected a %s but value is a %s" % (pytype, type(value))
    305327    return value
    306328
    307 def _prop_decode_list(disp, etype, data):
    308     (_, _, formatbits, _, _, terminator) = _prop_types[etype]
     329def _prop_decode_list(disp, actual_type, etype, data):
     330    _, _, formatbits, _, _, terminator = _prop_types[etype]
    309331    if terminator:
    310332        datums = data.split(terminator)
    311333    else:
     
    314336        while data:
    315337            datums.append(data[:nbytes])
    316338            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]
    318340    #assert None not in props
    319341    return [x for x in props if x is not None]
    320342
     
    324346        scalar_type = etype[0]
    325347    else:
    326348        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]
    328351    try:
    329352        #print(atom)
    330         data = trap.call_synced(XGetWindowProperty, target, key, atom)
     353        actual_type, data = trap.call_synced(XGetWindowProperty, target, key, atoms)
    331354        #print(atom, repr(data[:100]))
    332355    except NoSuchProperty:
    333356        log.debug("Missing property %s (%s)", key, etype)
     
    338361            traceback.print_exc()
    339362        return None
    340363    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)
    342366    except:
    343367        log.warn("Error parsing property %s (type %s); this may be a"
    344368                 + " misbehaving application, or bug in Wimpiggy\n"
  • wimpiggy/xsettings_prop.py

     
    3535XSettingsTypeColor = 2
    3636
    3737
    38 def get_settings(disp, d):
     38def get_settings(disp, t, d):
    3939    #parse xsettings according to
    4040    #http://standards.freedesktop.org/xsettings-spec/xsettings-spec-0.5.html
    4141    assert len(d)>=12, "_XSETTINGS_SETTINGS property is too small: %s" % len(d)
     
    7777    debug("parse_xsettings(..) settings=%s", settings)
    7878    return  serial, settings
    7979
    80 def set_settings(disp, d):
     80def set_settings(disp, t, d):
    8181    #TODO: detect old clients
    8282    assert len(d)==2, "invalid format for XSETTINGS: %s" % str(d)
    8383    serial, settings = d