Xpra: Ticket #318: clipboard client-to-server support on OSX

As of r3105, we support clipboard copy server-to-client with OSX. To be able to support client-to-server, we would need to know when the selection owner changes so we could send the token back to the server.


Unfortunately, none of the usual GTK events fire on osx, which may be because there is no support for any of them in OSX to begin with..
So we may have to resort to some native API calls, here are the pointers (mostly copied from #11 - but trimmed):

And in any case, there does not seem to be any way of getting notified through it.



Thu, 11 Jul 2013 16:37:31 GMT - Antoine Martin: status changed

There is no way to get asynchronous clipboard change notification, only polling! I am not the first one to be astonished by this:

Now, you would think that a simple polling loop would do the job.. But no, it's a lot more complicated than that: gtk-osx doesn't seem to like the nested main loop, etc..

In any case, here's how we could poll the changeCount:

import AppKit.NSPasteboard      #@UnresolvedImport
self.pasteboard = AppKit.NSPasteboard.generalPasteboard()
count = self.pasteboard.changeCount()

Tue, 16 Jul 2013 06:10:55 GMT - Antoine Martin: milestone changed


Thu, 19 Sep 2013 22:32:28 GMT - alas:

Will it be of any use/utility to have clipboard test data for this, server/client? (Is there an osx version of gtk_keyboard_test? I seem to remember the /lib/python2.7/xpra path in the linux server xpra, but no sign of anything there in mac client...)


Mon, 14 Oct 2013 13:59:51 GMT - Antoine Martin: owner, status changed

Will it be of any use/utility to have clipboard test data for this, server/client?

Not really, the OSX API is incomplete, so there is no data to look at!

Is there an osx version of gtk_keyboard_test?

Yes with r4492 it is now bundled in the disk image so you can run it like so (assuming you have a terminal in the directory containing Xpra.app):

Xpra.app/Contents/Helpers/Python

With previous versions, the file gtk_view_clipboard.py was not bundled in the xpra disk image, so in that case you have to download it first.

(this is just for reference, I don't think using the tool is going to help us much unfortunately..)


I've added most of what is needed in r4490, refactoring the ugly UI thread loop code in the process so we can re-use its thread and loop rather than adding yet another thread for doing the clipboard polling. r4491 also increases the frequency of the polling loop to run every 500ms, which is a little bit too fast for my liking, but should ensure that the clipboard contents are synced fast enough in most cases (average case will be 250ms). I can copy from the client to the server, and it's quite hard to manage to fire the paste server side before the osx clipboard contents have been synced.

Sadly, I cannot copy from server to client (anymore?), I've tried going back to 0.10.x and I have the same problem there. I am pretty sure that it used to work though, I had tested the code in r3105... Can you help me figure out if this ever worked, with which versions/builds?


Sat, 19 Oct 2013 14:32:17 GMT - Antoine Martin: attachment set

this patch makes the clipboard work in both directions but crashes the server in some cases!


Sat, 19 Oct 2013 14:41:59 GMT - Antoine Martin:

The patch above makes the server crash simply by coping some text with gedit, seemingly because of what we set the TARGET to (ATOMs) in do_selection_get.

Server log shows:

2013-10-19 21:33:16,581 do_selection_clear_event(<gtk.gdk.Event at 0x2e9fa80: GDK_SELECTION_CLEAR selection=CLIPBOARD, target=, property=>) have_token=True, block_owner_change=False selection=CLIPBOARD
2013-10-19 21:33:16,581 send clipboard token: CLIPBOARD
2013-10-19 21:33:16,581 get_contents(TARGETS,<function got_targets at 0x3c291b8>) selection=CLIPBOARD
2013-10-19 21:33:16,581 do_owner_changed((<gtk.Clipboard object at 0x167c0f0 (GtkClipboard at 0x1701f00)>, <gtk.gdk.Event at 0x1c5aa80: GDK_OWNER_CHANGE reason=GDK_OWNER_CHANGE_NEW_OWNER, selection=CLIPBOARD>)) greedy_client=True, block_owner_change=False
2013-10-19 21:33:16,582 send clipboard token: CLIPBOARD
2013-10-19 21:33:16,582 get_contents(TARGETS,<function got_targets at 0x3c29320>) selection=CLIPBOARD
2013-10-19 21:33:16,595 got_targets(<gtk.Clipboard object at 0x167c0f0 (GtkClipboard at 0x1701f00)>, ('TIMESTAMP', 'TARGETS', 'MULTIPLE', 'SAVE_TARGETS', 'GTK_TEXT_BUFFER_CONTENTS', 'application/x-gtk-text-buffer-rich-text', 'UTF8_STRING', 'COMPOUND_TEXT', 'TEXT', 'STRING', 'text/plain;charset=utf-8', 'text/plain'), (None,))
2013-10-19 21:33:16,595 got_targets for selection CLIPBOARD: ATOM, 32, ('TIMESTAMP', 'TARGETS', 'MULTIPLE', 'SAVE_TARGETS', 'GTK_TEXT_BUFFER_CONTENTS', 'application/x-gtk-text-buffer-rich-text', 'UTF8_STRING', 'COMPOUND_TEXT', 'TEXT', 'STRING', 'text/plain;charset=utf-8', 'text/plain')
2013-10-19 21:33:16,595 get_contents(UTF8_STRING,<function got_contents at 0x3c292a8>) selection=CLIPBOARD
2013-10-19 21:33:16,597 got_targets(<gtk.Clipboard object at 0x167c0f0 (GtkClipboard at 0x1701f00)>, ('TIMESTAMP', 'TARGETS', 'MULTIPLE', 'SAVE_TARGETS', 'GTK_TEXT_BUFFER_CONTENTS', 'application/x-gtk-text-buffer-rich-text', 'UTF8_STRING', 'COMPOUND_TEXT', 'TEXT', 'STRING', 'text/plain;charset=utf-8', 'text/plain'), (None,))
2013-10-19 21:33:16,598 got_targets for selection CLIPBOARD: ATOM, 32, ('TIMESTAMP', 'TARGETS', 'MULTIPLE', 'SAVE_TARGETS', 'GTK_TEXT_BUFFER_CONTENTS', 'application/x-gtk-text-buffer-rich-text', 'UTF8_STRING', 'COMPOUND_TEXT', 'TEXT', 'STRING', 'text/plain;charset=utf-8', 'text/plain')
2013-10-19 21:33:16,598 get_contents(UTF8_STRING,<function got_contents at 0x3c291b8>) selection=CLIPBOARD
2013-10-19 21:33:16,600 unpack <gtk.Clipboard object at 0x167c0f0 (GtkClipboard at 0x1701f00)>: <type 'gtk.SelectionData'>
2013-10-19 21:33:16,600 unpack: <GtkSelectionData at 0x19b6140>
2013-10-19 21:33:16,601 unpack(..) type=UTF8_STRING, format=8, data=<type 'str'>:11
2013-10-19 21:33:16,601 got_contents for selection CLIPBOARD: UTF8_STRING, 8, hello-world
2013-10-19 21:33:16,601 _do_munge_raw_selection_to_wire(UTF8_STRING, UTF8_STRING, 8, <type 'str'>:11)
2013-10-19 21:33:16,602 sending token with target data: {'UTF8_STRING': ('UTF8_STRING', 8, 'bytes', 'hello-world')}
2013-10-19 21:33:16,610 unpack <gtk.Clipboard object at 0x167c0f0 (GtkClipboard at 0x1701f00)>: <type 'gtk.SelectionData'>
2013-10-19 21:33:16,611 unpack: <GtkSelectionData at 0x19b6180>
2013-10-19 21:33:16,611 unpack(..) type=UTF8_STRING, format=8, data=<type 'str'>:11
2013-10-19 21:33:16,611 got_contents for selection CLIPBOARD: UTF8_STRING, 8, hello-world
2013-10-19 21:33:16,612 _do_munge_raw_selection_to_wire(UTF8_STRING, UTF8_STRING, 8, <type 'str'>:11)
2013-10-19 21:33:16,612 sending token with target data: {'UTF8_STRING': ('UTF8_STRING', 8, 'bytes', 'hello-world')}
2013-10-19 21:33:16,644 process clipboard packet type=clipboard-token
2013-10-19 21:33:16,644 process clipboard token selection=CLIPBOARD, local clipboard name=CLIPBOARD, proxy=ClipboardProxy(CLIPBOARD)
2013-10-19 21:33:16,644 got token, selection=CLIPBOARD, targets=None, target_data=None
2013-10-19 21:33:16,644 do_owner_changed((<gtk.Clipboard object at 0x167c0f0 (GtkClipboard at 0x1701f00)>, <gtk.gdk.Event at 0x1c5aa80: GDK_OWNER_CHANGE reason=GDK_OWNER_CHANGE_NEW_OWNER, selection=CLIPBOARD>)) greedy_client=True, block_owner_change=True
2013-10-19 21:33:16,644 process clipboard packet type=clipboard-token
2013-10-19 21:33:16,645 process clipboard token selection=CLIPBOARD, local clipboard name=CLIPBOARD, proxy=ClipboardProxy(CLIPBOARD)
2013-10-19 21:33:16,645 got token, selection=CLIPBOARD, targets=None, target_data=None
2013-10-19 21:33:16,645 do_owner_changed((<gtk.Clipboard object at 0x167c0f0 (GtkClipboard at 0x1701f00)>, <gtk.gdk.Event at 0x1c5aa80: GDK_OWNER_CHANGE reason=GDK_OWNER_CHANGE_NEW_OWNER, selection=CLIPBOARD>)) greedy_client=True, block_owner_change=False
2013-10-19 21:33:16,645 send clipboard token: CLIPBOARD
2013-10-19 21:33:16,645 get_contents(TARGETS,<function got_targets at 0x3c291b8>) selection=CLIPBOARD
2013-10-19 21:33:16,645 do_selection_get(<GtkSelectionData at 0x7fffb7a66dc0>, 0, 39616166) selection=CLIPBOARD
2013-10-19 21:33:16,645 get clipboard from remote handler id=4
2013-10-19 21:33:16,646 do_selection_request_event(<gtk.gdk.Event at 0x16299b8: GDK_SELECTION_REQUEST selection=CLIPBOARD, target=TARGETS, property=GDK_SELECTION>)
2013-10-19 21:33:16,646 target for CLIPBOARD: 'TARGETS'
2013-10-19 21:33:16,647 do_selection_request_event(<gtk.gdk.Event at 0x16299b8: GDK_SELECTION_REQUEST selection=CLIPBOARD, target=TARGETS, property=GDK_SELECTION>) target=TARGETS, selection=CLIPBOARD
2013-10-19 21:33:16,647 do_selection_get(<GtkSelectionData at 0x7fffb7a64ad0>, 0, 39616167) selection=CLIPBOARD
2013-10-19 21:33:16,647 get clipboard from remote handler id=5
2013-10-19 21:33:16,647 do_selection_request_event(<gtk.gdk.Event at 0x1629940: GDK_SELECTION_REQUEST selection=CLIPBOARD, target=TARGETS, property=GDK_SELECTION>)
2013-10-19 21:33:16,647 target for CLIPBOARD: 'TARGETS'
2013-10-19 21:33:16,647 do_selection_request_event(<gtk.gdk.Event at 0x1629940: GDK_SELECTION_REQUEST selection=CLIPBOARD, target=TARGETS, property=GDK_SELECTION>) target=TARGETS, selection=CLIPBOARD
2013-10-19 21:33:16,647 do_selection_get(<GtkSelectionData at 0x7fffb7a627a0>, 0, 39616167) selection=CLIPBOARD
2013-10-19 21:33:16,647 get clipboard from remote handler id=6
2013-10-19 21:33:16,674 process clipboard packet type=clipboard-contents
2013-10-19 21:33:16,675 process clipboard contents, selection=CLIPBOARD, type=ATOM, format=32
2013-10-19 21:33:16,675 _munge_wire_selection_to_raw(atoms, ATOM, 32, <type 'tuple'>:2:['UTF8_STRING', 'public.utf8-plain-text'])
2013-10-19 21:33:16,675 _munge_wire_selection_to_raw(atoms, ATOM, 32, <type 'tuple'>:2)=[<GdkAtom 0x47 = 'UTF8_STRING'>, <GdkAtom 0xa5 = 'public.utf8-plain-text'>]=[71L, 165L]=['G', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\xa5', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00']
2013-10-19 21:33:16,675 clipboard wire -> raw: ('ATOM', 32, 'atoms', ('UTF8_STRING', 'public.utf8-plain-text')) -> 'G\x00\x00\x00\x00\x00\x00\x00\xa5\x00\x00\x00\x00\x00\x00\x00'
2013-10-19 21:33:16,676 got clipboard contents for id=4 len=16, loop=<xpra.gtk_common.nested_main.NestedMainLoop object at 0x4aa7cd0> (type=ATOM, format=32)
2013-10-19 21:33:16,714 process clipboard packet type=clipboard-contents
2013-10-19 21:33:16,714 process clipboard contents, selection=CLIPBOARD, type=ATOM, format=32
2013-10-19 21:33:16,714 _munge_wire_selection_to_raw(atoms, ATOM, 32, <type 'tuple'>:2:['UTF8_STRING', 'public.utf8-plain-text'])
2013-10-19 21:33:16,715 _munge_wire_selection_to_raw(atoms, ATOM, 32, <type 'tuple'>:2)=[<GdkAtom 0x47 = 'UTF8_STRING'>, <GdkAtom 0xa5 = 'public.utf8-plain-text'>]=[71L, 165L]=['G', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\xa5', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00']
2013-10-19 21:33:16,715 clipboard wire -> raw: ('ATOM', 32, 'atoms', ('UTF8_STRING', 'public.utf8-plain-text')) -> 'G\x00\x00\x00\x00\x00\x00\x00\xa5\x00\x00\x00\x00\x00\x00\x00'
2013-10-19 21:33:16,715 got clipboard contents for id=5 len=16, loop=<xpra.gtk_common.nested_main.NestedMainLoop object at 0x4aa7d10> (type=ATOM, format=32)
2013-10-19 21:33:16,715 process clipboard packet type=clipboard-contents
2013-10-19 21:33:16,715 process clipboard contents, selection=CLIPBOARD, type=ATOM, format=32
2013-10-19 21:33:16,715 _munge_wire_selection_to_raw(atoms, ATOM, 32, <type 'tuple'>:2:['UTF8_STRING', 'public.utf8-plain-text'])
2013-10-19 21:33:16,715 _munge_wire_selection_to_raw(atoms, ATOM, 32, <type 'tuple'>:2)=[<GdkAtom 0x47 = 'UTF8_STRING'>, <GdkAtom 0xa5 = 'public.utf8-plain-text'>]=[71L, 165L]=['G', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\xa5', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00']
2013-10-19 21:33:16,716 clipboard wire -> raw: ('ATOM', 32, 'atoms', ('UTF8_STRING', 'public.utf8-plain-text')) -> 'G\x00\x00\x00\x00\x00\x00\x00\xa5\x00\x00\x00\x00\x00\x00\x00'
2013-10-19 21:33:16,716 got clipboard contents for id=6 len=16, loop=<xpra.gtk_common.nested_main.NestedMainLoop object at 0x4aa78d0> (type=ATOM, format=32)
2013-10-19 21:33:16,716 get clipboard from remote result(6)={'data': 'G\x00\x00\x00\x00\x00\x00\x00\xa5\x00\x00\x00\x00\x00\x00\x00', 'type': 'ATOM', 'format': 32}
2013-10-19 21:33:16,716 do_selection_get(<GtkSelectionData at 0x7fffb7a627a0>,0,39616167) calling selection_data.set(ATOM, 32, <type 'str'>:16)
2013-10-19 21:33:16,717 get clipboard from remote result(5)={'data': 'G\x00\x00\x00\x00\x00\x00\x00\xa5\x00\x00\x00\x00\x00\x00\x00', 'type': 'ATOM', 'format': 32}
2013-10-19 21:33:16,718 do_selection_get(<GtkSelectionData at 0x7fffb7a64ad0>,0,39616167) calling selection_data.set(ATOM, 32, <type 'str'>:16)
2013-10-19 21:33:16,719 get clipboard from remote result(4)={'data': 'G\x00\x00\x00\x00\x00\x00\x00\xa5\x00\x00\x00\x00\x00\x00\x00', 'type': 'ATOM', 'format': 32}
2013-10-19 21:33:16,719 do_selection_get(<GtkSelectionData at 0x7fffb7a66dc0>,0,39616166) calling selection_data.set(ATOM, 32, <type 'str'>:16)
2013-10-19 21:33:16,719 got_targets(<gtk.Clipboard object at 0x167c0f0 (GtkClipboard at 0x1701f00)>, ('UTF8_STRING', 'public.utf8-plain-text'), (None,))
2013-10-19 21:33:16,720 got_targets for selection CLIPBOARD: ATOM, 32, ('UTF8_STRING', 'public.utf8-plain-text')
2013-10-19 21:33:16,720 get_contents(UTF8_STRING,<function got_contents at 0x3c290c8>) selection=CLIPBOARD
2013-10-19 21:33:16,720 unpack <gtk.Clipboard object at 0x167c0f0 (GtkClipboard at 0x1701f00)>: <type 'gtk.SelectionData'>
2013-10-19 21:33:16,720 unpack: <GtkSelectionData at 0x19b61c0>

Which can be summarized as:

do_selection_clear_event -> get targets -> got targets -> get contents (UTF8_STRING) -> unpack -> got contents -> munge -> sending token with target data

And repeated as we fire the same sequence of events after a do_owner_changed. (needs blocking)
The client sends the token back immediately (needs blocking - we should ignore the contents we have just set, sadly the changecount seems to increase *after* we set the contents?):

clipboard-token -> do_selection_request_event (TARGETS) -> do_selection_get -> get clipboard from remote handler

We request the TARGETS (three times?) and blow up on:

got contents -> do_selection_get -> selection_data.set (xxx) -> got_targets -> got_contents -> unpack

And gdb gives us this beautiful backtrace:

(gdb) bt
#0  0x0000003934b39bcf in __strlen_sse42 () from /lib64/libc.so.6
#1  0x0000003def495cf0 in PyString_FromString () from /lib64/libpython2.7.so.1.0
#2  0x00007f54c99cf6de in _wrap_gtk_selection_data__get_type ()
   from /usr/lib64/python2.7/site-packages/gtk-2.0/gtk/_gtk.so
#3  0x0000003def45e830 in ?? () from /lib64/libpython2.7.so.1.0
#4  0x0000003def4847e1 in _PyObject_GenericGetAttrWithDict () from /lib64/libpython2.7.so.1.0
#5  0x0000003def4da839 in PyEval_EvalFrameEx () from /lib64/libpython2.7.so.1.0
#6  0x0000003def4dec7d in PyEval_EvalCodeEx () from /lib64/libpython2.7.so.1.0
#7  0x0000003def46dca0 in ?? () from /lib64/libpython2.7.so.1.0
#8  0x0000003def449dd3 in PyObject_Call () from /lib64/libpython2.7.so.1.0
#9  0x0000003def4d8af7 in PyEval_CallObjectWithKeywords () from /lib64/libpython2.7.so.1.0
#10 0x00007f54c99e75fb in clipboard_request_contents_cb () from /usr/lib64/python2.7/site-packages/gtk-2.0/gtk/_gtk.so
#11 0x00000039a5683710 in selection_received () from /lib64/libgtk-x11-2.0.so.0
#12 0x000000393860f9a2 in g_closure_invoke () from /lib64/libgobject-2.0.so.0
#13 0x0000003938620a3d in signal_emit_unlocked_R () from /lib64/libgobject-2.0.so.0
#14 0x0000003938628829 in g_signal_emit_valist () from /lib64/libgobject-2.0.so.0
#15 0x0000003938628fa2 in g_signal_emit_by_name () from /lib64/libgobject-2.0.so.0
#16 0x00000039a55ad343 in gtk_selection_retrieval_report () from /lib64/libgtk-x11-2.0.so.0
#17 0x00000039a55aedb8 in gtk_selection_convert () from /lib64/libgtk-x11-2.0.so.0
#18 0x00007f54c99e7e5f in _wrap_gtk_clipboard_request_contents ()
   from /usr/lib64/python2.7/site-packages/gtk-2.0/gtk/_gtk.so
#19 0x0000003def4ddcee in PyEval_EvalFrameEx () from /lib64/libpython2.7.so.1.0
#20 0x0000003def4dec7d in PyEval_EvalCodeEx () from /lib64/libpython2.7.so.1.0
#21 0x0000003def4dd769 in PyEval_EvalFrameEx () from /lib64/libpython2.7.so.1.0
#22 0x0000003def4dec7d in PyEval_EvalCodeEx () from /lib64/libpython2.7.so.1.0
#23 0x0000003def4dd769 in PyEval_EvalFrameEx () from /lib64/libpython2.7.so.1.0
#24 0x0000003def4dec7d in PyEval_EvalCodeEx () from /lib64/libpython2.7.so.1.0
#25 0x0000003def46dca0 in ?? () from /lib64/libpython2.7.so.1.0
#26 0x0000003def449dd3 in PyObject_Call () from /lib64/libpython2.7.so.1.0
#27 0x0000003def4d8af7 in PyEval_CallObjectWithKeywords () from /lib64/libpython2.7.so.1.0
#28 0x00007f54c99d1175 in clipboard_request_targets_cb () from /usr/lib64/python2.7/site-packages/gtk-2.0/gtk/_gtk.so
#29 0x00000039a5683511 in request_targets_received_func () from /lib64/libgtk-x11-2.0.so.0
#30 0x00000039a5683710 in selection_received () from /lib64/libgtk-x11-2.0.so.0
#31 0x000000393860fa28 in g_closure_invoke () from /lib64/libgobject-2.0.so.0
#32 0x0000003938620a3d in signal_emit_unlocked_R () from /lib64/libgobject-2.0.so.0
#33 0x0000003938628829 in g_signal_emit_valist () from /lib64/libgobject-2.0.so.0
#34 0x0000003938628fa2 in g_signal_emit_by_name () from /lib64/libgobject-2.0.so.0
#35 0x00000039a55ad343 in gtk_selection_retrieval_report () from /lib64/libgtk-x11-2.0.so.0
#36 0x00000039a55aedb8 in gtk_selection_convert () from /lib64/libgtk-x11-2.0.so.0
#37 0x00007f54c99e7cae in _wrap_gtk_clipboard_request_targets ()
   from /usr/lib64/python2.7/site-packages/gtk-2.0/gtk/_gtk.so
#38 0x0000003def4ddcee in PyEval_EvalFrameEx () from /lib64/libpython2.7.so.1.0
#39 0x0000003def4dec7d in PyEval_EvalCodeEx () from /lib64/libpython2.7.so.1.0
#40 0x0000003def4dd769 in PyEval_EvalFrameEx () from /lib64/libpython2.7.so.1.0
#41 0x0000003def4dec7d in PyEval_EvalCodeEx () from /lib64/libpython2.7.so.1.0
#42 0x0000003def46dca0 in ?? () from /lib64/libpython2.7.so.1.0
#43 0x0000003def449dd3 in PyObject_Call () from /lib64/libpython2.7.so.1.0
#44 0x0000003def458555 in ?? () from /lib64/libpython2.7.so.1.0
#45 0x0000003def449dd3 in PyObject_Call () from /lib64/libpython2.7.so.1.0
#46 0x0000003def4d8af7 in PyEval_CallObjectWithKeywords () from /lib64/libpython2.7.so.1.0
#47 0x00007f54c9dd7e59 in pyg_closure_marshal () from /usr/lib64/python2.7/site-packages/gobject/_gobject.so
#48 0x000000393860fa28 in g_closure_invoke () from /lib64/libgobject-2.0.so.0
#49 0x0000003938620a3d in signal_emit_unlocked_R () from /lib64/libgobject-2.0.so.0
#50 0x00000039386278d1 in g_signal_emitv () from /lib64/libgobject-2.0.so.0
#51 0x00007f54c9dd12cc in pygobject_emit () from /usr/lib64/python2.7/site-packages/gobject/_gobject.so
#52 0x0000003def4ddcee in PyEval_EvalFrameEx () from /lib64/libpython2.7.so.1.0
#53 0x0000003def4dec7d in PyEval_EvalCodeEx () from /lib64/libpython2.7.so.1.0
#54 0x0000003def46dca0 in ?? () from /lib64/libpython2.7.so.1.0
#55 0x0000003def449dd3 in PyObject_Call () from /lib64/libpython2.7.so.1.0
#56 0x0000003def458555 in ?? () from /lib64/libpython2.7.so.1.0
#57 0x0000003def449dd3 in PyObject_Call () from /lib64/libpython2.7.so.1.0
#58 0x0000003def4d8af7 in PyEval_CallObjectWithKeywords () from /lib64/libpython2.7.so.1.0
#59 0x00007f54c9dd7e59 in pyg_closure_marshal () from /usr/lib64/python2.7/site-packages/gobject/_gobject.so
#60 0x000000393860fa28 in g_closure_invoke () from /lib64/libgobject-2.0.so.0
#61 0x0000003938620a3d in signal_emit_unlocked_R () from /lib64/libgobject-2.0.so.0
#62 0x0000003938628829 in g_signal_emit_valist () from /lib64/libgobject-2.0.so.0
#63 0x0000003938628a72 in g_signal_emit () from /lib64/libgobject-2.0.so.0
#64 0x00000039a5540d9d in gtk_main_do_event () from /lib64/libgtk-x11-2.0.so.0
#65 0x00000039a5c5e2dc in gdk_event_dispatch () from /lib64/libgdk-x11-2.0.so.0
#66 0x0000003936e47e06 in g_main_context_dispatch () from /lib64/libglib-2.0.so.0
#67 0x0000003936e48158 in g_main_context_iterate.isra.22 () from /lib64/libglib-2.0.so.0
#68 0x0000003936e4855a in g_main_loop_run () from /lib64/libglib-2.0.so.0
#69 0x00000039a553fdb7 in gtk_main () from /lib64/libgtk-x11-2.0.so.0

Better summarized as:

__strlen_sse42 ()
PyString_FromString ()
_wrap_gtk_selection_data__get_type ()
clipboard_request_contents_cb ()
selection_received ()
gtk_selection_retrieval_report ()
gtk_selection_convert ()
_wrap_gtk_clipboard_request_contents ()
clipboard_request_targets_cb ()
request_targets_received_func ()
selection_received ()
gtk_selection_retrieval_report ()
gtk_selection_convert ()
_wrap_gtk_clipboard_request_targets ()
signal_emit_unlocked_R ()
gtk_main ()

When we unpack, we print the selection_data.type and that seems to trigger the crash.


Sun, 20 Oct 2013 09:00:43 GMT - Antoine Martin:

That was HARD, GTK on OSX does not support nested main loops... so we have to be careful not to use them.

The relevant changes are in:


Please test thoroughly, I had seen an instance of the server getting stuck in the nested main loop (see XPRA_NESTED_DEBUG above) but I have not been able to reproduce it since despite adding code to try to trigger the same problem (r4576). In particular:


Tue, 22 Oct 2013 20:19:37 GMT - alas:

Using osx xpra 0.10.8 r4595 client-side and 0.10.8-1.fc19.x86_64.rpm server-side the clipboard seems to be behaving exactly as I recall it behaving before.

Copying from local desktop and pasting to the xpra session works repeatedly as expected.

Copying within the xpra session produces the following message in the client-side terminal on the first attempt:

Users/alex/Desktop/xpra.app/Contents/Resources/lib/python/xpra/clipboard/clipboard_base.py:522: Warning: gsignal.c:3320: value location for `gboolean' passed as NULL
2013-10-22 12:54:24,702 unhandled format 0 for clipboard data type NONE

The second and subsequent attempts to copy within the xpra session produces the following (shorter) message in client-side terminal:

unhandled format 0 for clipboard data type NONE

Nothing copied within the xpra session was able to be pasted either within the xpra session or to the local desktop applications.

The first (failed) paste attempt produced the following message in client-side terminal:

/Users/alex/Desktop/xpra.app/Contents/Resources/lib/python/xpra/clipboard/clipboard_base.py:522: Warning: gsignal.c:3320: value location for `gboolean' passed as NULL
2013-10-22 12:54:24,702 unhandled format 0 for clipboard data type NONE

All subsequent (failed) paste attempts produce the same output message as the copy attempts, though usually significantly more than one line:

2013-10-22 12:55:21,800 unhandled format 0 for clipboard data type NONE
2013-10-22 12:55:21,851 unhandled format 0 for clipboard data type NONE
2013-10-22 12:55:21,924 unhandled format 0 for clipboard data type NONE

I couldn't find any sign of correlation of the number of iterations of the output message with the length or other composition aspects of the (expected) contents of the clipboard, however.


Wed, 23 Oct 2013 00:31:44 GMT - Antoine Martin:

That's because the 0.10.x stable branch does not, and more than likely will not, contain those intrusive changes.

Please try trunk.


Wed, 23 Oct 2013 03:47:00 GMT - Antoine Martin:

Updated beta builds for most platforms are in the beta area (inc win32, osx and rpms, no debs)


Wed, 23 Oct 2013 22:17:45 GMT - alas:

Currently the only osx builds I see in http://xpra.org/beta/osx/x86/ are the 0.11.0 r4592 (which produces results as above) and the xpra.dmg with a date of 10/17 which, when installed, shows 0.11.0 r4542 in the info tab and which, also, produces the results above when the clipboard is tested.

Going into the xpra.org/dists/osx/x86/ directory I find an xpra.dmg which is dated 22-Oct and which shows as xpra 0.10.8 r4595 when installed and the info is checked.

Testing with that build I was able to copy within the xpra session and paste somewhere else within the xpra session successfully, but only once. A second attempt to copy and paste failed (clipboard was apparently cleared, as nothing pasted) and, once again, produced the unhandled format error as above.

Copying from local desktop and pasting to the xpra session works consistently.

Copying something from the local desktop & successfully pasting to the xpra session, followed by copying from the xpra session and trying to paste to the local desktop (.txt file) outputs nothing, so the copy attempt within the xpra is interacting with, and apparently clearing, the clipboard.

Oddly, disconnecting the client from the server, then re-connecting, does not enable a new & successful single copying and pasting, neither with the clipboard used while the client is disconnected nor with the clipboard not used while disconnected. (In other words, I was able to copy once, and only once. Inexplicably, neither restarting client nor both server and client allowed me to copy & paste successfully within xpra session a second time.)


Thu, 24 Oct 2013 00:38:17 GMT - Antoine Martin:

The last beta is indeed 0.11.0-r4592 which is dated yesterday. If this does not allow you to copy, your server is probably not up to date.

As per comment:8, there is no point in testing the 0.10.x branch which does not and will not have the code in it.

Each directory contains an "Xpra.dmg" symlink for people to download directly. You can see it is the same file as the latest release in that directory as it has the same size and time. (sadly, apache does not show us that it is a symlink)


Thu, 24 Oct 2013 20:44:58 GMT - alas:

Ok, now that I've gotten hold of the right build to test, I have to applaud your work.

Works as expected in both directions. Works as expected with xclip (once I figured out how that tool worked) and with gedit. Works when swapped back and forth with a windows to osx client. (I don't have a handy linux machine to test on I'm afraid.)

A few notes though.

First whenever copying on the local desktop the Xpra icon "hopped" in the dock (oddly, only before the session was swapped back and forth with the windows client).

Second, whenever pasting into the xpra session I continue to get the warning: 2013-10-24 13:40:08,059 unhandled format 0 for clipboard data type NONE - the clipboard works as expected though.

Finally, whenever copying locally while clipboard has a value previously copied from within an xpra session, I get the following warning: Xpra[10085:3203] *** __NSAutoreleaseNoPool(): Object 0x1d1c90b0 of class __NSFastEnumerationEnumerator autoreleased with no pool in place - just leaking - again though, the clipboard behaves as expected.

I doubt the warnings are important, but I thought they might be interesting to note.


Fri, 25 Oct 2013 01:20:46 GMT - Antoine Martin:

Works as expected with xclip (once I figured out how that tool worked)


Using xclip is documented here: wiki/Clipboard


whenever copying on the local desktop the Xpra icon "hopped" in the dock


This is #275 - assigned to afarr for 7 months.. ;)


only before the session was swapped back and forth with the windows client


Are you saying that the dock icon does not bounce after re-connecting? It should. Re-connecting should not make any difference. The problem with this bouncing dock feature on OSX is that the clipboard is now synchronous in the server-to-client direction, so a little redundant in that case. (probably still useful for client-to-server direction)



Finally, those warnings:

unhandled format 0 for clipboard data type NONE
__NSAutoreleaseNoPool(): Object 0x1d1c90b0 of class __NSFastEnumerationEnumerator autoreleased with no pool in place - just leaking

Will need to be investigated to make sure we aren't leaking memory. They're in gtk-osx code, not xpra's.


Works as expected in both directions.


Good, will close this ticket once the warnings are investigated. Please re-assign to me after answering the question above - (and assuming my answers make sense).


Sat, 26 Oct 2013 01:54:56 GMT - alas:

I tested again, xpra 0.11.0 r4592, again... with an osx 10.6.8 machine, the xpra icon did indeed stop bouncing after reconnecting.

Oddly, with an osx 10.7.5 machine the icon didn't bounce at all, but instead displayed the keyboard icon referred to in #275.

(In our particular case, odd 10.6.8 behavior is not an issue. If you would like me to further test I'd be interested to hear what you think I should look for. I saw no output to suggest why it would be behaving differently after re-connecting than before.)


Tue, 29 Oct 2013 13:38:52 GMT - Antoine Martin:

Closing, will follow up in #275 which is the right ticket for the "bouncing clipboard notification" feature.

FYI: there was some confusion over "re-connecting" I think. I thought that you meant that the "bouncing" only occurred on the first connection and not subsequent ones, but I now think that you meant that re-connecting stopped the bouncing if it was already happening - not that it prevented new bouncing from happening once re-connected. Right? (please add to #275, bouncing should occur - though not for very long, maybe not long enough to notice... in which case the logging is the only way to know for sure)

Looks done, closing.


Tue, 29 Oct 2013 13:39:31 GMT - Antoine Martin: status changed; resolution set


Fri, 14 Jul 2017 12:42:16 GMT - Antoine Martin:

Similar clibpoard crash backtrace: #1579.


Thu, 20 Jul 2017 09:32:58 GMT - Antoine Martin: status, milestone changed; resolution deleted

This should be fixed on osx by #1588


Tue, 08 Aug 2017 00:13:05 GMT - alas: status changed; resolution set

Interesting to note. Tested some with 2.2 r16657 osx client against the 2.2. r16657 fedora 25 server - does indeed seem fixed.

At one point, because of the old title of this ticket, I misunderstood and tried to set, client-side, the clipboard to just client-to-server, but got the syntax wrong... which, interestingly, caused the clipboard behavior to become incredibly erratic (--clipboard=to-server produced the warning: Warning: no clipboard types matching 'to-server', but as I tried to copy/paste it felt like I was getting almost random selections from the page when I tried to paste).

Let me know if you want a ticket to handle misbehavior triggered by invalid configure flags (as if you don't have enough to deal with making everything behave when it's properly configured). In the meantime, I think I'm going to re-close this.


Tue, 08 Aug 2017 06:35:50 GMT - Antoine Martin:

When using invalid values with the --clipboard= switch, clipboard synchronization will be disabled, r16659 makes that clearer.


Sat, 23 Jan 2021 04:51:32 GMT - migration script:

this ticket has been moved to: https://github.com/Xpra-org/xpra/issues/318