Skip to content

Commit

Permalink
#812: add support for incremental data
Browse files Browse the repository at this point in the history
git-svn-id: https://xpra.org/svn/Xpra/trunk@22824 3bb7dfac-3a0b-4e04-842a-767bc560f471
  • Loading branch information
totaam committed Jun 1, 2019
1 parent 6c9c1f3 commit 6d6c3c5
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 11 deletions.
10 changes: 5 additions & 5 deletions src/xpra/x11/bindings/window_bindings.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -940,7 +940,7 @@ cdef class _X11WindowBindings(_X11CoreBindings):
self.addXSelectInput(xwindow, FocusChangeMask)


def XGetWindowProperty(self, Window xwindow, property, req_type=None, etype=None, int buffer_size=64*1024):
def XGetWindowProperty(self, Window xwindow, property, req_type=None, etype=None, int buffer_size=64*1024, delete=False, incr=False):
# NB: Accepts req_type == 0 for AnyPropertyType
# "64k is enough for anybody"
# (Except, I've found window icons that are strictly larger)
Expand All @@ -963,7 +963,7 @@ cdef class _X11WindowBindings(_X11CoreBindings):
# This argument has to be divided by 4. Thus
# speaks the spec.
buffer_size // 4,
False,
delete,
xreq_type, &xactual_type,
&actual_format, &nitems, &bytes_after, &prop)
if status != Success:
Expand All @@ -974,7 +974,7 @@ cdef class _X11WindowBindings(_X11CoreBindings):
raise BadPropertyType("expected %s but got %s" % (req_type, self.XGetAtomName(xactual_type)))
# This should only occur for bad property types:
assert not (bytes_after and not nitems)
if bytes_after:
if bytes_after and not incr:
raise PropertyOverflow("reserved %i bytes for %s buffer, but data is bigger by %i bytes!" % (buffer_size, etype, bytes_after))
# actual_format is in (8, 16, 32), and is the number of bits in a logical
# element. However, this doesn't mean that each element is stored in that
Expand All @@ -996,7 +996,7 @@ cdef class _X11WindowBindings(_X11CoreBindings):
return data


def GetWindowPropertyType(self, Window xwindow, property):
def GetWindowPropertyType(self, Window xwindow, property, incr=False):
#as above, but for any property type
#and returns the type found
self.context_check()
Expand All @@ -1023,7 +1023,7 @@ cdef class _X11WindowBindings(_X11CoreBindings):
raise BadPropertyType("None type")
# This should only occur for bad property types:
assert not (bytes_after and not nitems)
if bytes_after:
if bytes_after and not incr:
raise BadPropertyType("incomplete data: %i bytes after" % bytes_after)
assert actual_format in (8, 16, 32)
XFree(prop)
Expand Down
67 changes: 61 additions & 6 deletions src/xpra/x11/gtk_x11/clipboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ def __init__(self, xid, selection="CLIPBOARD"):
self.local_request_counter = 0
self.targets = ()
self.target_data = {}
self.reset_incr_data()

def __repr__(self):
return "X11ClipboardProxy(%s)" % self._selection
Expand Down Expand Up @@ -420,8 +421,9 @@ def got_contents(self, target, dtype=None, dformat=None, data=None):
log("got_contents%s pending=%s",
(target, dtype, dformat, repr_ellipsized(str(data))), csv(pending))
for requestor, prop, time in pending:
log("setting response %s to property %s of window %s as %s",
repr_ellipsized(data), prop, self.get_wininfo(get_xwindow(requestor)), dtype)
if log.is_debug_enabled():
log("setting response %s to property %s of window %s as %s",
repr_ellipsized(str(data)), prop, self.get_wininfo(get_xwindow(requestor)), dtype)
self.set_selection_response(requestor, target, prop, dtype, dformat, data, time)


Expand Down Expand Up @@ -551,11 +553,40 @@ def do_property_notify(self, event):
target = parts[1] #ie: VALUE
try:
with xsync:
dtype, dformat = X11Window.GetWindowPropertyType(self.xid, event.atom)
dtype, dformat = X11Window.GetWindowPropertyType(self.xid, event.atom, self.incr_data_size>0)
dtype = bytestostr(dtype)
MAX_DATA_SIZE = 4*1024*1024
data = X11Window.XGetWindowProperty(self.xid, event.atom, dtype, None, MAX_DATA_SIZE)
X11Window.XDeleteProperty(self.xid, event.atom)
data = X11Window.XGetWindowProperty(self.xid, event.atom, dtype, None, MAX_DATA_SIZE, True)
#all the code below deals with INCRemental transfers:
if dtype=="INCR" and not self.incr_data_size:
#start of an incremental transfer, extract the size
assert dformat==32
self.incr_data_size = struct.unpack("@L", data)[0]
self.incr_data_chunks = []
self.incr_data_type = None
log("incremental clipboard data of size %s", self.incr_data_size)
self.reschedule_incr_data_timer()
return
if self.incr_data_size>0:
#incremental is now in progress:
if not self.incr_data_type:
self.incr_data_type = dtype
elif self.incr_data_type!=dtype:
log.error("Error: invalid change of data type")
log.error(" from %s to %s", self.incr_data_type, dtype)
self.reset_incr_data()
self.cancel_incr_data_timer()
return
if data:
log("got incremental data: %i bytes", len(data))
self.incr_data_chunks.append(data)
self.reschedule_incr_data_timer()
return
self.cancel_incr_data_timer()
data = b"".join(self.incr_data_chunks)
log("got incremental data termination, total size=%i bytes", len(data))
self.got_local_contents(target, dtype, dformat, data)
return
except PropertyError:
log("do_property_notify() property '%s' is gone?", event.atom, exc_info=True)
return
Expand All @@ -567,8 +598,32 @@ def do_property_notify(self, event):
def got_local_contents(self, target, dtype=None, dformat=None, data=None):
target_requests = self.local_requests.pop(target, {})
for timer, got_contents, time in target_requests.values():
log("got_local_contents: calling %s%s, time=%i", got_contents, (dtype, dformat, data), time)
if log.is_debug_enabled():
log("got_local_contents: calling %s%s, time=%i",
got_contents, (dtype, dformat, repr_ellipsized(str(data))), time)
glib.source_remove(timer)
got_contents(dtype, dformat, data)


def reschedule_incr_data_timer(self):
self.cancel_incr_data_timer()
self.incr_data_timer = glib.timeout_add(1*1000, self.incr_data_timeout)

def cancel_incr_data_timer(self):
idt = self.incr_data_timer
if idt:
self.incr_data_timer = None
glib.source_remove(idt)

def incr_data_timeout(self):
self.incr_data_timer = None
log.warn("Warning: incremental data timeout")
self.incr_data = None

def reset_incr_data(self):
self.incr_data_size = 0
self.incr_data_type = None
self.incr_data_chunks = None
self.incr_data_timer = None

gobject.type_register(ClipboardProxy)

0 comments on commit 6d6c3c5

Please sign in to comment.