Skip to content

Commit

Permalink
#966: extend translated-clipboard support to osx clients (that was HARD)
Browse files Browse the repository at this point in the history
git-svn-id: https://xpra.org/svn/Xpra/trunk@12059 3bb7dfac-3a0b-4e04-842a-767bc560f471
  • Loading branch information
totaam committed Feb 25, 2016
1 parent 48e2a49 commit fd942b8
Show file tree
Hide file tree
Showing 4 changed files with 186 additions and 66 deletions.
36 changes: 18 additions & 18 deletions src/xpra/client/gtk2/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,26 +207,16 @@ def make_clipboard_helper(self):
Try the various clipboard classes until we find one
that loads ok. (some platforms have more options than others)
"""
from xpra.platform.features import CLIPBOARDS
clipboard_options = self.get_clipboard_helper_classes()
clipboardlog("make_clipboard_helper() options=%s, server_clipboards=%s, local clipboards=%s", clipboard_options, self.server_clipboards, CLIPBOARDS)
#first add the platform specific one, (may be None):
kwargs= {"clipboards.local" : CLIPBOARDS, #all the local clipboards supported
"clipboards.remote" : self.server_clipboards} #all the remote clipboards supported
#only allow translation overrides if we have a way of telling the server about them:
if self.server_supports_clipboard_enable_selections:
kwargs.update({
"clipboard.local" : self.local_clipboard, #the local clipboard we want to sync to (with the translated clipboard only)
"clipboard.remote" : self.remote_clipboard}) #the remote clipboard we want to we sync to (with the translated clipboard only)
clipboardlog("make_clipboard_helper() clipboard_options=%s, kwargs=%s", clipboard_options, kwargs)
clipboardlog("make_clipboard_helper() options=%s", clipboard_options)
for classname in clipboard_options:
module_name, _class = classname.rsplit(".", 1)
c = self.try_load_clipboard_helper(module_name, _class, kwargs)
c = self.try_load_clipboard_helper(module_name, _class)
if c:
return c
return None

def try_load_clipboard_helper(self, module, classname, kwargs={}):
def try_load_clipboard_helper(self, module, classname):
try:
m = __import__(module, {}, {}, classname)
if m:
Expand All @@ -235,7 +225,7 @@ def try_load_clipboard_helper(self, module, classname, kwargs={}):
return None
c = getattr(m, classname)
if c:
return self.setup_clipboard_helper(c, **kwargs)
return self.setup_clipboard_helper(c)
except ImportError as e:
clipboardlog.error("Error: cannot load %s.%s:", module, classname)
clipboardlog.error(" %s", e)
Expand All @@ -246,8 +236,18 @@ def try_load_clipboard_helper(self, module, classname, kwargs={}):
clipboardlog.error("cannot load %s.%s", module, classname)
return None

def setup_clipboard_helper(self, helperClass, *args, **kwargs):
clipboardlog("setup_clipboard_helper(%s, %s, %s)", helperClass, args, kwargs)
def setup_clipboard_helper(self, helperClass):
clipboardlog("setup_clipboard_helper(%s)", helperClass)
#first add the platform specific one, (may be None):
from xpra.platform.features import CLIPBOARDS
kwargs= {"clipboards.local" : CLIPBOARDS, #all the local clipboards supported
"clipboards.remote" : self.server_clipboards} #all the remote clipboards supported
#only allow translation overrides if we have a way of telling the server about them:
if self.server_supports_clipboard_enable_selections:
kwargs.update({
"clipboard.local" : self.local_clipboard, #the local clipboard we want to sync to (with the translated clipboard only)
"clipboard.remote" : self.remote_clipboard}) #the remote clipboard we want to we sync to (with the translated clipboard only)
clipboardlog("setup_clipboard_helper() kwargs=%s", kwargs)
def clipboard_send(*parts):
if not self.clipboard_enabled:
clipboardlog("clipboard is disabled, not sending clipboard packet")
Expand All @@ -259,7 +259,7 @@ def clipboard_send(*parts):
if type(v)==Uncompressed:
#register the compressor which will fire in protocol.encode:
def compress_clipboard():
clipboardlog("compress_clipboard() compressing %s", args, v)
clipboardlog("compress_clipboard() compressing %s", v)
return self.compressed_wrapper(v.datatype, v.data)
v.compress = compress_clipboard
self.send(*packet)
Expand All @@ -285,7 +285,7 @@ def clipboard_toggled(*targs):
self.clipboard_notify(0)
self.connect("clipboard-toggled", clipboard_toggled)
self.after_handshake(register_clipboard_toggled)
return helperClass(clipboard_send, clipboard_progress, *args, **kwargs)
return helperClass(clipboard_send, clipboard_progress, **kwargs)

def clipboard_notify(self, n):
if not self.tray:
Expand Down
122 changes: 81 additions & 41 deletions src/xpra/client/gtk_base/gtk_tray_menu_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
from xpra.gtk_common.about import about, close_about
from xpra.codecs.loader import PREFERED_ENCODING_ORDER, ENCODINGS_HELP, ENCODINGS_TO_NAME
from xpra.platform.gui import get_icon_size
try:
from xpra.clipboard.translated_clipboard import TranslatedClipboardProtocolHelper
except:
TranslatedClipboardProtocolHelper = None

from xpra.log import Logger
log = Logger("menu")
Expand Down Expand Up @@ -56,6 +60,17 @@
SPEED_OPTIONS[1] = "Lowest Bandwidth"
SPEED_OPTIONS[100] = "Lowest Latency"

CLIPBOARD_LABELS = ["Disabled", "Clipboard", "Primary", "Secondary"]
CLIPBOARD_LABEL_TO_NAME = {"Disabled" : None,
"Clipboard" : "CLIPBOARD",
"Primary" : "PRIMARY",
"Secondary" : "SECONDARY"}

def ll(m):
try:
return "%s:%s" % (type(m), m.get_label())
except:
return str(m)

def set_sensitive(widget, sensitive):
if sys.platform.startswith("darwin"):
Expand Down Expand Up @@ -449,6 +464,64 @@ def clipboard_toggled(*args):
self.client.connect("clipboard-toggled", clipboard_toggled)
return self.clipboard_menuitem

def _can_handle_clipboard(self):
c = self.client
return c.server_supports_clipboard and c.client_supports_clipboard

def get_clipboard_helper_class(self):
return TranslatedClipboardProtocolHelper

def remote_clipboard_changed(self, item, clipboard_submenu):
if not self._can_handle_clipboard():
item = None
if item is None:
item = ([x for x in clipboard_submenu.get_children() if x.get_label()=="Disabled"]+[None])[0]
if item is None:
return
#prevent infinite recursion where ensure_item_selected
#ends up calling here again
ich = getattr(clipboard_submenu, "_in_change_handler_", False)
clipboardlog("remote_clipboard_changed%s already in change handler: %s, visible=%s", (ll(item), clipboard_submenu), ich, clipboard_submenu.get_visible())
if ich: # or not clipboard_submenu.get_visible():
return
try:
setattr(clipboard_submenu, "_in_change_handler_", True)
if False:
selected_item = ensure_item_selected(clipboard_submenu, item)
selected = selected_item.get_label()
remote_clipboard = CLIPBOARD_LABEL_TO_NAME.get(selected)
self.set_new_remote_clipboard(remote_clipboard)
finally:
setattr(clipboard_submenu, "_in_change_handler_", False)

def set_new_remote_clipboard(self, remote_clipboard):
clipboardlog("set_new_remote_clipboard(%s)", remote_clipboard)
old_state = self.client.clipboard_enabled
send_tokens = False
if remote_clipboard is not None:
#clipboard is not disabled
if self.client.clipboard_helper is None:
self.client.clipboard_helper = self.client.setup_clipboard_helper(self.get_clipboard_helper_class())
self.client.clipboard_helper.remote_clipboard = remote_clipboard
self.client.clipboard_helper.remote_clipboards = [remote_clipboard]
send_tokens = True
new_state = True
selections = [remote_clipboard]
else:
self.client.clipboard_helper = None
send_tokens = False
new_state = False
selections = []
#tell the server what to look for:
self.client.send_clipboard_selections(selections)
clipboardlog("set_new_remote_clipboard(%s) old_state=%s, new_state=%s", remote_clipboard, old_state, new_state)
if new_state!=old_state:
self.client.clipboard_enabled = new_state
self.client.emit("clipboard-toggled")
send_tokens = True
if send_tokens and self.client.clipboard_helper:
self.client.clipboard_helper.send_all_tokens()

def make_translatedclipboard_optionsmenuitem(self):
clipboardlog("make_translatedclipboard_optionsmenuitem()")
clipboard_menu = self.menuitem("Clipboard", "clipboard.png", "Choose which remote clipboard to connect to", None)
Expand All @@ -458,53 +531,20 @@ def set_clipboard_menu(*args):
clipboard_menu.set_submenu(clipboard_submenu)
self.popup_menu_workaround(clipboard_submenu)
c = self.client
can_clipboard = c.server_supports_clipboard and c.client_supports_clipboard and c.server_supports_clipboard
can_clipboard = self._can_handle_clipboard()
clipboardlog("set_clipboard_menu(%s) can_clipboard=%s, server=%s, client=%s", args, can_clipboard, c.server_supports_clipboard, c.client_supports_clipboard)
set_sensitive(clipboard_menu, can_clipboard)
LABEL_TO_NAME = {"Disabled" : None,
"Clipboard" : "CLIPBOARD",
"Primary" : "PRIMARY",
"Secondary" : "SECONDARY"}
from xpra.clipboard.translated_clipboard import TranslatedClipboardProtocolHelper
for label, remote_clipboard in LABEL_TO_NAME.items():
for label in CLIPBOARD_LABELS:
remote_clipboard = CLIPBOARD_LABEL_TO_NAME[label]
clipboard_item = CheckMenuItem(label)
def remote_clipboard_changed(item):
assert can_clipboard
ensure_item_selected(clipboard_submenu, item)
label = item.get_label()
remote_clipboard = LABEL_TO_NAME.get(label)
old_state = self.client.clipboard_enabled
clipboardlog("remote_clipboard_changed(%s) remote_clipboard=%s, old_state=%s", item, remote_clipboard, old_state)
send_tokens = False
if remote_clipboard is not None:
#clipboard is not disabled
if self.client.clipboard_helper is None:
self.client.setup_clipboard_helper(TranslatedClipboardProtocolHelper)
self.client.clipboard_helper.remote_clipboard = remote_clipboard
self.client.clipboard_helper.remote_clipboards = [remote_clipboard]
send_tokens = True
new_state = True
selections = [remote_clipboard]
else:
self.client.clipboard_helper = None
send_tokens = False
new_state = False
selections = []
#tell the server what to look for:
self.client.send_clipboard_selections(selections)
clipboardlog("remote_clipboard_changed(%s) label=%s, remote_clipboard=%s, old_state=%s, new_state=%s",
item, label, remote_clipboard, old_state, new_state)
if new_state!=old_state:
self.client.clipboard_enabled = new_state
self.client.emit("clipboard-toggled")
send_tokens = True
if send_tokens and self.client.clipboard_helper:
self.client.clipboard_helper.send_all_tokens()
active = isinstance(self.client.clipboard_helper, TranslatedClipboardProtocolHelper) \
and self.client.clipboard_helper.remote_clipboard==remote_clipboard
ch = self.client.clipboard_helper
active = ch and isinstance(ch, TranslatedClipboardProtocolHelper) \
and ch.remote_clipboard==remote_clipboard
clipboard_item.set_active(active)
set_sensitive(clipboard_item, can_clipboard)
clipboard_item.set_draw_as_radio(True)
def remote_clipboard_changed(item):
self.remote_clipboard_changed(item, clipboard_submenu)
clipboard_item.connect("toggled", remote_clipboard_changed)
clipboard_submenu.append(clipboard_item)
clipboard_submenu.show_all()
Expand Down
11 changes: 6 additions & 5 deletions src/xpra/platform/darwin/osx_clipboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from xpra.log import Logger
log = Logger("clipboard", "osx")

from xpra.clipboard.gdk_clipboard import GDKClipboardProtocolHelper
from xpra.clipboard.translated_clipboard import TranslatedClipboardProtocolHelper
from xpra.clipboard.clipboard_base import ClipboardProxy, TEXT_TARGETS

update_clipboard_change_count = None
Expand Down Expand Up @@ -117,7 +117,7 @@ def timer_clipboard_check():
return True


class OSXClipboardProtocolHelper(GDKClipboardProtocolHelper):
class OSXClipboardProtocolHelper(TranslatedClipboardProtocolHelper):
"""
Full of OSX quirks!
darwin/features.py should be set
Expand All @@ -127,8 +127,9 @@ class OSXClipboardProtocolHelper(GDKClipboardProtocolHelper):

def __init__(self, *args, **kwargs):
init_pasteboard()
kwargs["clipboards.remote"] = ["CLIPBOARD"]
GDKClipboardProtocolHelper.__init__(self, *args, **kwargs)
kwargs["clipboard.local"] = "CLIPBOARD"
kwargs["clipboards.local"] = ["CLIPBOARD"]
TranslatedClipboardProtocolHelper.__init__(self, *args, **kwargs)

def make_proxy(self, clipboard):
return OSXClipboardProxy(clipboard)
Expand All @@ -138,7 +139,7 @@ def _do_munge_raw_selection_to_wire(self, target, dtype, dformat, data):
if dformat == 0 and dtype=="NONE":
log("got 'NONE' data from clipboard")
return None, None
return GDKClipboardProtocolHelper._do_munge_raw_selection_to_wire(self, target, dtype, dformat, data)
return TranslatedClipboardProtocolHelper._do_munge_raw_selection_to_wire(self, target, dtype, dformat, data)

def _get_clipboard_from_remote_handler(self, proxy, selection, target):
#cannot work on osx, the nested mainloop doesn't run :(
Expand Down
Loading

0 comments on commit fd942b8

Please sign in to comment.