def do_selection_request_event(self, event): log("do_selection_request_event(%s)", event) self._selection_request_events += 1 if not self._enabled or not self._can_receive: gtk.Invisible.do_selection_request_event(self, event) return # Black magic: the superclass default handler for this signal # implements all the hards parts of selection handling, occasionally # calling back to the do_selection_get handler (below) to actually get # the data to be sent. However, it only does this for targets that # have been registered ahead of time; other targets fall through to a # default implementation that cannot be overridden. So, we swoop in # ahead of time and add whatever target was requested to the list of # targets we want to handle! # # Special cases (magic targets defined by ICCCM): # TIMESTAMP: the remote side has a different timeline than us, so # sending TIMESTAMPS across the wire doesn't make any sense. We # ignore TIMESTAMP requests, and let them fall through to GTK+'s # default handler. # TARGET: GTK+ has default handling for this, but we don't want to # use it. Fortunately, if we tell GTK+ that we can handle TARGET # requests, then it will pass them on to us rather than fall # through to the default handler. # MULTIPLE: Ugh. To handle this properly, we need to go out # ourselves and fetch the magic property off the requesting window # (with proper error trapping and all), and interpret its # contents. Probably doable (FIXME), just a pain. # # Another special case is that if an app requests the contents of a # clipboard that it currently owns, then GTK+ will short-circuit the # normal logic and request the contents directly (i.e. it calls # gtk_selection_invoke_handler) -- without giving us a chance to # assert that we can handle the requested sort of target. Fortunately, # Xpra never needs to request the clipboard when it owns it, so that's # okay. assert str(event.selection) == self._selection, "selection does not match: expected %s but got %s" % (event.selection, self._selection) target = str(event.target) if target == "TIMESTAMP": pass elif target == "MULTIPLE": if not self.prop_get: log("MULTIPLE for property '%s' not handled due to missing xpra.x11.gtk_x11 bindings", event.property) gtk.Invisible.do_selection_request_event(self, event) return atoms = self.prop_get(event.window, event.property, ["multiple-conversion"]) log("MULTIPLE clipboard atoms: %r", atoms) if atoms: targets = atoms[::2] for t in targets: selection_add_target(self, self._selection, t, 0) else: log("target for %s: %r", self._selection, target) selection_add_target(self, self._selection, target, 0) log("do_selection_request_event(%s) target=%s, selection=%s", event, target, self._selection) gtk.Invisible.do_selection_request_event(self, event)
def got_token(self, targets, target_data, claim=True, synchronous_client=False): # We got the anti-token. self.cancel_emit_token() if not self._enabled: return self._got_token_events += 1 log( "got token, selection=%s, targets=%s, target data=%s, claim=%s, can-receive=%s", self._selection, targets, target_data, claim, self._can_receive) if self._greedy_client or CLIPBOARD_GREEDY: self._block_owner_change = True #re-enable the flag via idle_add so events like do_owner_changed #get a chance to run first. glib.idle_add(self.remove_block) if (CLIPBOARD_GREEDY or synchronous_client) and self._can_receive: if targets: for target in targets: selection_add_target(self, self._selection, target, 0) selection_owner_set(self, self._selection) if target_data: for text_target in TEXT_TARGETS: if text_target in target_data: text_data = target_data.get(text_target) log("clipboard %s set to '%s'", self._selection, repr_ellipsized(text_data)) set_clipboard_data(self._clipboard, text_data, text_target) if not claim: log("token packet without claim, not setting the token flag") #the other end is just telling us to send the token again next time something changes, #not that they want to own the clipboard selection return self._have_token = True if self._can_receive: if not self._block_owner_change: #if we don't claim the selection (can-receive=False), #we will have to send the token back on owner-change! self._block_owner_change = True glib.idle_add(self.remove_block) self.claim()