Skip to content

Instantly share code, notes, and snippets.

@jaap-karssenberg
Last active April 25, 2020 15:53
Show Gist options
  • Save jaap-karssenberg/4f7d3dba7d5bc725dacf28a382893d69 to your computer and use it in GitHub Desktop.
Save jaap-karssenberg/4f7d3dba7d5bc725dacf28a382893d69 to your computer and use it in GitHub Desktop.
Attempt to get clipboard "set_with_data()" to work using ctypes
import ctypes
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('GIRepository', '2.0')
from gi.repository import GIRepository
from gi.repository import Gtk
# Load the libraries we need via GIRepository
def _get_shared_library(n):
repo = GIRepository.Repository.get_default()
return repo.get_shared_library(n).split(',')[0]
libgtk = ctypes.CDLL(_get_shared_library('Gtk'))
libgdk = ctypes.CDLL(_get_shared_library('Gdk'))
def c_func(dll, name, args, res=None):
fn = getattr(dll, name)
fn.restype = res
fn.argtypes = args
return fn
def atom_p(name):
return libgdk.gdk_atom_intern_static_string(name, False)
print("---- clipboard ----")
clipboard = libgtk.gtk_clipboard_get(atom_p(b'CLIPBOARD'))
assert clipboard != 0
# Test passing clipboard pointer works
gtk_clipboard_set_text = c_func(libgtk, 'gtk_clipboard_set_text',
(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_uint))
gtk_clipboard_wait_for_text = c_func(libgtk, 'gtk_clipboard_wait_for_text',
(), res=ctypes.c_char_p)
gtk_clipboard_set_text(clipboard, b'Test 123', len(b'Test 123'))
print("Set text")
text = gtk_clipboard_wait_for_text(clipboard)
print("TEXT>", text)
assert text == b'Test 123'
print("----- target entry list ----")
targetentrylist = libgtk.gtk_target_list_new(None, 0)
libgtk.gtk_target_list_add(
targetentrylist,
atom_p(b'text/plain'),
0,
9,
)
gtk_target_list_find = c_func(libgtk, 'gtk_target_list_find',
(ctypes.c_void_p, ctypes.c_void_p), res=ctypes.c_bool)
ok = gtk_target_list_find(targetentrylist, atom_p(b'text/plain'), None)
print("FOUND TARGET:", ok)
assert ok
print("---- functions and set_with_data ----")
GetFuncType = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p)
def get_func(clipboard, selection, info, data):
print("GET %r" % ((clipboard, selection, info, data),))
libgtk.gtk_selection_data_set_text(selection, b'Test 456', len(b'Test 456'))
ClearFuncType = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_void_p)
def clear_func(clipboard, data):
print("CLEAR %r" % ((clipboard, data),))
c_get_func = GetFuncType(get_func)
c_clear_func = ClearFuncType(clear_func)
gtk_clipboard_set_with_data = c_func(libgtk, 'gtk_clipboard_set_with_data',
(
ctypes.c_void_p,
ctypes.c_void_p,
ctypes.c_uint,
ctypes.c_void_p, #ctypes.POINTER(GetFuncType),
ctypes.c_void_p, #ctypes.POINTER(ClearFuncType),
ctypes.c_void_p
),
res=ctypes.c_bool)
user_data = "userdata 123"
ok = gtk_clipboard_set_with_data(
clipboard,
targetentrylist,
1,
c_get_func,
c_clear_func,
user_data
)
print(">>>", ok)
assert ok
gtk_clipboard_wait_for_contents = c_func(libgtk, 'gtk_clipboard_wait_for_contents',
(ctypes.c_void_p, ctypes.c_void_p), res=ctypes.c_void_p)
data = gtk_clipboard_wait_for_contents(clipboard, atom_p(b'text/plain'))
print(">>>>", data)
text = gtk_clipboard_wait_for_text(clipboard)
print("TEXT>", text)
assert text == b'Test 456'
# PS need global cache for data so it will not get garbage collected before "clear"
@MeanEYE
Copy link

MeanEYE commented Apr 24, 2020

Hey, did you end up having any luck with this?

@jaap-karssenberg
Copy link
Author

jaap-karssenberg commented Apr 25, 2020 via email

@MeanEYE
Copy link

MeanEYE commented Apr 25, 2020

Well, it's an important fix but GTK+ developers decided to give up on every language other than those with direct access to C library. Reason they stated doesn't help either, they just said "The GtkClipboard api is gone." Whatever that meant.

Anyway, I tried many different approaches with your code. From single atom target, to all kinds of variations. I tried getting clipboard for current display but that failed as well. What I found really odd is that even simple set_text makes copied data unavailable to other programs, which led me to believe that getting clipboard does something wrong, even though atom value is correct.

Do you have some insights of your own which might aid me in pursuing this issue?

@jaap-karssenberg
Copy link
Author

jaap-karssenberg commented Apr 25, 2020 via email

@MeanEYE
Copy link

MeanEYE commented Apr 25, 2020

That's what I wanted to avoid. My application is entirely written in Python, which means it's architecture agnostic. I was really hoping I could upgrade your code to functional.

That said, I know Polari is doing the C function approach, since application is written in GJS and they are suffering from same stupid decision made by GTK developers. We should check what they are doing and perhaps take their solution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment