After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 656312 - gtk_clipboard_set_with_data/set_with_owner is binding-unfriendly
gtk_clipboard_set_with_data/set_with_owner is binding-unfriendly
Status: RESOLVED OBSOLETE
Product: gtk+
Classification: Platform
Component: Language Bindings
3.1.x
Other All
: Normal normal
: ---
Assigned To: gtk-bugs
gtk-bugs
: 682604 743054 (view as bug list)
Depends on:
Blocks: 782595
 
 
Reported: 2011-08-10 21:40 UTC by Pavel Holejsovsky
Modified: 2018-05-02 15:10 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
New function gtk_clipboard_set_with_closures() (8.25 KB, patch)
2011-08-10 21:45 UTC, Pavel Holejsovsky
none Details | Review
add annotations to existing functions (2.59 KB, patch)
2012-12-01 14:42 UTC, Daniel Drake
none Details | Review
Gtk2 testcase (1.33 KB, text/x-python)
2012-12-05 12:30 UTC, Simon Schampijer
  Details
Gtk3 testcase (1.51 KB, text/x-python)
2012-12-05 12:31 UTC, Simon Schampijer
  Details
Gtk3 example using gtk_clipboard_set_with_closures (1.44 KB, text/x-python)
2012-12-12 16:28 UTC, Simon Schampijer
  Details
The same sample written in Lua (1.39 KB, text/x-lua)
2012-12-13 20:47 UTC, Pavel Holejsovsky
  Details
introspection patch, partially fixes issue (1.72 KB, patch)
2012-12-14 17:29 UTC, Carlos Garnacho
needs-work Details | Review
Add binding-friendly gtk_clipboard_set_with_closures() (8.27 KB, patch)
2012-12-16 13:25 UTC, Pavel Holejsovsky
none Details | Review
pygobject patch (2.34 KB, patch)
2012-12-17 15:46 UTC, Carlos Garnacho
none Details | Review
working python testcase (1.61 KB, text/plain)
2012-12-17 15:48 UTC, Carlos Garnacho
  Details
updated patch (2.02 KB, patch)
2012-12-17 18:48 UTC, Carlos Garnacho
committed Details | Review
testcase adapted to python3 for reference (1.65 KB, application/octet-stream)
2012-12-20 17:01 UTC, Carlos Garnacho
  Details
Same patch to add annotation, but with a little change to apply on master (2.60 KB, patch)
2014-05-29 13:06 UTC, Gonzalo Odiard
reviewed Details | Review
Add gtk_clipboard_set_targets() (7.90 KB, patch)
2017-11-18 22:44 UTC, makepost
none Details | Review
Add gtk_clipboard_set_targets() with brace indent fix (8.15 KB, patch)
2017-11-23 00:25 UTC, makepost
none Details | Review
Add gtk_clipboard_set_targets() with brace indent fix and no quote (7.94 KB, patch)
2017-11-23 00:51 UTC, makepost
none Details | Review

Description Pavel Holejsovsky 2011-08-10 21:40:58 UTC
These two methods are not bindable through gobject-introspection because they contain two callbacks, moreover with strange lifetime rules.
Comment 1 Pavel Holejsovsky 2011-08-10 21:45:28 UTC
Created attachment 193602 [details] [review]
New function gtk_clipboard_set_with_closures()

Built on top of existing callbacks, no changes to clipboard internals were needed.
Comment 2 Tomeu Vizoso 2011-08-30 07:43:52 UTC
+ * @owner: (allow-none): an object that "owns" the data. This object
+ *     will be passed to the callbacks when called.

Why is it needed if GClosures already can have a "data" attribute?

Not that it will matter to most languages, as they support closures natively...
Comment 3 Pavel Holejsovsky 2011-08-30 08:34:18 UTC
(In reply to comment #2)
> + * @owner: (allow-none): an object that "owns" the data. This object
> + *     will be passed to the callbacks when called.
> 
> Why is it needed if GClosures already can have a "data" attribute?
> 
> Not that it will matter to most languages, as they support closures natively...

Passing owner does more than merely providing another argument in the callback.  See gtk_clipboard_get_owner() and have_owner flag usage in gtkclipboard.c.  I must admit that I do not fully understand all acrobatics the GtkClipboard does with have_owner; clipboard stores itself into owner's g_object_data, refs the owner in strange places etc., so I better added 'owner' argument functionality to _with_closures() too.
Comment 4 Gonzalo Odiard 2012-11-06 19:20:23 UTC
gtk_clipboard_set_with_data was override in the old pygtk bindings http://git.gnome.org/browse/pygtk/tree/gtk/gtk.override#n677 can the old code be ported to the new bindings?

This bug is a blocker for us, any idea about what should be the right solution is welcome.
Comment 5 Gonzalo Odiard 2012-11-09 21:13:45 UTC
(In reply to comment #1)
> Created an attachment (id=193602) [details] [review]
> New function gtk_clipboard_set_with_closures()
> 
> Built on top of existing callbacks, no changes to clipboard internals were
> needed.

Pavel,
I am trying to test this patch, because I need the functionality using python.
Can you provide one example of use?
Thanks!
Comment 6 Daniel Drake 2012-11-30 17:44:02 UTC
For that patch, pass owner as None to mimic the behaviour of gtk_clipboard_set_with_data(). The rest should be obvious; the API is otherwise the same.

I'd be interested in knowing why gtk_clipboard_set_with_data() itself isn't introspectable; is it because the same user_data is passed to two independent callbacks? I'll be investigating that a little later.
Comment 7 Daniel Drake 2012-12-01 14:42:31 UTC
Created attachment 230385 [details] [review]
add annotations to existing functions

I can't see why these functions were marked as non-introspectable. Here is a patch which adds suitable annotations and makes them available in the gir.

Gonzalo, here is a temporary build you can use for testing this: http://arm.koji.fedoraproject.org/koji/taskinfo?taskID=1273675
Comment 8 Pavel Holejsovsky 2012-12-01 15:35:13 UTC
(In reply to comment #7)
> Created an attachment (id=230385) [details] [review]
> add annotations to existing functions
> 
> I can't see why these functions were marked as non-introspectable. Here is a
> patch which adds suitable annotations and makes them available in the gir.
> 
> Gonzalo, here is a temporary build you can use for testing this:
> http://arm.koji.fedoraproject.org/koji/taskinfo?taskID=1273675

Generated gir is probably 'fine', but last time I looked, no binding was able to handle multiple closures bound to single user_data field.  Therefore I originally marked the function as (skip).
Comment 9 Pavel Holejsovsky 2012-12-01 15:38:18 UTC
(In reply to comment #8)
> (In reply to comment #7)
> > Created an attachment (id=230385) [details] [review] [details] [review]
> > add annotations to existing functions
> > 
> > I can't see why these functions were marked as non-introspectable. Here is a
> > patch which adds suitable annotations and makes them available in the gir.
> > 
> > Gonzalo, here is a temporary build you can use for testing this:
> > http://arm.koji.fedoraproject.org/koji/taskinfo?taskID=1273675
> 
> Generated gir is probably 'fine', but last time I looked, no binding was able
> to handle multiple closures bound to single user_data field.  Therefore I
> originally marked the function as (skip).

Also, I'm not sure whether (scope async) annotation is right one for get_func; can't get_func() really be called multiple times, not only once as (scope async) expects?
Comment 10 Simon Schampijer 2012-12-05 12:30:17 UTC
Created attachment 230752 [details]
Gtk2 testcase
Comment 11 Simon Schampijer 2012-12-05 12:31:20 UTC
Created attachment 230753 [details]
Gtk3 testcase
Comment 12 Simon Schampijer 2012-12-05 12:34:07 UTC
I did compare a GTK2 and GTK3 python test case using the rpm Daniel provided. In the GTK3 test case get_func() gets never called. Meaning that GtkClipboardReceivedFunc [1] does not get the data meant to be copied.

[1] http://developer.gnome.org/gtk3/3.4/gtk3-Clipboards.html#GtkClipboardReceivedFunc
Comment 13 Simon Schampijer 2012-12-05 17:35:49 UTC
(In reply to comment #9)
> (In reply to comment #8)
> > (In reply to comment #7)
> > > Created an attachment (id=230385) [details] [review] [details] [review] [details] [review]
> > > add annotations to existing functions
> > > 
> > > I can't see why these functions were marked as non-introspectable. Here is a
> > > patch which adds suitable annotations and makes them available in the gir.
> > > 
> > > Gonzalo, here is a temporary build you can use for testing this:
> > > http://arm.koji.fedoraproject.org/koji/taskinfo?taskID=1273675
> > 
> > Generated gir is probably 'fine', but last time I looked, no binding was able
> > to handle multiple closures bound to single user_data field.  Therefore I
> > originally marked the function as (skip).
> 
> Also, I'm not sure whether (scope async) annotation is right one for get_func;
> can't get_func() really be called multiple times, not only once as (scope
> async) expects?

Yes, agreed, it should be able to call get_func several times (several pastes of one copy). clear_func should be only called once (when a new copy is made the clear_func of the previous copy is called), so here the scope looks correct.

https://live.gnome.org/GObjectIntrospection/Annotations#Callbacks_Scope_Types

I discussed with Daniel a bit more, the annotation for set_with_data is tricky: 2 callbacks, both of which share the same user_data (we have to check if that is handled by pygobject3 correctly) and I don't think there is a scope annotation which describes the behaviour and circumstances under which this callback will be called, so the callback can be freed up again.

So it looks like, either we can get the annotation correct or we need new API, either using GDestroyNotify (and "notified" annotation) or a GClosure.
Comment 14 Simon Schampijer 2012-12-12 16:28:26 UTC
Created attachment 231391 [details]
Gtk3 example using gtk_clipboard_set_with_closures
Comment 15 Simon Schampijer 2012-12-12 16:30:50 UTC
Pavel, I did modify my GTK3 example to use your gtk_clipboard_set_with_closures. When I run the code, get_func is never called and therefore the data is not passed. Do you see anything wrong with my test case? Or do you have a testcase using your patch?

I did some print outs in gtk itself and "closures_get_func" seem to not be called at all.
Comment 16 Pavel Holejsovsky 2012-12-13 20:45:44 UTC
(In reply to comment #15)
> Pavel, I did modify my GTK3 example to use your
> gtk_clipboard_set_with_closures. When I run the code, get_func is never called
> and therefore the data is not passed. Do you see anything wrong with my test
> case? Or do you have a testcase using your patch?
> 
> I did some print outs in gtk itself and "closures_get_func" seem to not be
> called at all.

Simon, this is strange.  I can confirm that running your sample does not invoke get_func() at all.  However, I tried to rewrite your test in Lua, and there get_func() and clear_func() are clearly invoked correctly.  However, selectiondata does not seem to carry data payload correctly, as seen from the transcript below, but this might also be bug in lgi Lua binding.

I'll investigate more during the next weekend (hopefully).

* clipboard_get_func	lgi.obj 0x166bb80:Gtk.Clipboard(GtkClipboard)	lgi.rec 0x1669a40:Gtk.SelectionData	0
* __paste_contents_received:	lgi.obj 0x166bb80:Gtk.Clipboard(GtkClipboard)	lgi.rec 0x7fff6bb46450:Gtk.SelectionData	userdata: 0x7f93123ae240
  --data=	table: 0x167f1e0
  --format=	0
  --target=	STRING
Comment 17 Pavel Holejsovsky 2012-12-13 20:47:50 UTC
Created attachment 231512 [details]
The same sample written in Lua

Rewrite of pygobject previos test sample in Lua (using lgi binding)
Comment 18 Carlos Garnacho 2012-12-14 17:29:25 UTC
Created attachment 231578 [details] [review]
introspection patch, partially fixes issue

AFAICS respectively setting notified/async scopes to get/clear_func is the right thing to do with those functions, there is still an unresolved issue though in the first GtkTargetEntry* argument though, as you can only do Gtk.TargetEntry.new() with python, and an array of those is actually a GtkTargetEntry** to the C side
Comment 19 Pavel Holejsovsky 2012-12-16 13:25:42 UTC
Created attachment 231644 [details] [review]
Add binding-friendly gtk_clipboard_set_with_closures()

New method covers functionality of gtk_clipboard_set_with_data() and
gtk_clipboard_set_with_owner(), but is gobject-introspection and
language bindings friendly.
Comment 20 Pavel Holejsovsky 2012-12-16 13:44:23 UTC
(In reply to comment #18)
> Created an attachment (id=231578) [details] [review]
> introspection patch, partially fixes issue
> 
> AFAICS respectively setting notified/async scopes to get/clear_func is the
> right thing to do with those functions,

Thanks! This is clever, and it works for me for Lua/lgi binding.  IMHO it is on the border of what bindings are supposed to support; two closures bound to single userdata, one of them marked as notified but no real GDestroyNotify handler is ever invoked, therefore it relying on resources for both closures freed by calling clear_func marked as async.

> there is still an unresolved issue
> though in the first GtkTargetEntry* argument though, as you can only do
> Gtk.TargetEntry.new() with python, and an array of those is actually a
> GtkTargetEntry** to the C side

This would explain why Simon's example for _set_with_closures() fails with pygobject but works fine with lgi.

The proposed patch also does not make _set_with_owner() work, but it most probably does not matter too much.  In case that _set_with_owner will be needed, I attached fixed version of my original patch.  Note that both approaches can be applied - Carlos annotation fix to make set_with_data() work and set_with_closures() to make set_with_owner() variant usable too.
Comment 21 Carlos Garnacho 2012-12-17 15:46:05 UTC
Created attachment 231733 [details] [review]
pygobject patch

The remaining issue with the GtkTargetEntry array was indeed a pygobject glitch, this patch fixes the marshalling of arrays of boxed objects when the C side is expecting an array of structs.
Comment 22 Carlos Garnacho 2012-12-17 15:48:47 UTC
Created attachment 231734 [details]
working python testcase
Comment 23 Carlos Garnacho 2012-12-17 18:48:07 UTC
Created attachment 231760 [details] [review]
updated patch

The other patch had bogus changes, my bad
Comment 24 Simon Schampijer 2012-12-17 20:12:48 UTC
Excellent work Carlos, with the GTK and Pygobject patch in place to test case passes fine now! Thanks!
Comment 25 Martin Pitt 2012-12-18 21:37:30 UTC
(In reply to comment #23)
> Created an attachment (id=231760) [details] [review]
> updated patch

+		    gboolean is_pointer = item_iface_cache->g_type == G_TYPE_POINTER;

This flag isn't used (and compilation fails due to it); is that some debugging leftover? I'm not sure whether a "const GtkTargetEntry *targets" array would ever have a POINTER gtype, shouldn't that be the proper gtype of GtkTargetEntry as this is a boxed struct?

I have some difficulties with coming up with a test case for this. The "working python test case" (https://bugzilla.gnome.org/attachment.cgi?id=231734) doesn't behave any different with and without this pygobject patch: in both cases __clipboard_clear_func_cb() is being called (according to the print), but __clipboard_get_func_cb() is never called. This might be due to the insufficient annotation patch (https://bugzilla.gnome.org/attachment.cgi?id=231578); this was already being discussed above, a function which shares user data between two callbacks is not introspectable; even more so, changing the scope to (notified) when the function doesn't actually take a GDestroyNotify can't work.

So I'm trying to reduce this to a test case which actually checks this case: passing an array of struct values as an argument to a method (not an array of pointers to structs). I thought  both cases were already covered by g-i's GIMarshallingTests, in gi_marshalling_tests_array_struct_in() (which is very similar to gtk_clipboard_set_with_data () wrt. the struct array) and
 gi_marshalling_tests_array_simple_struct_in() (which covers the "array of struct pointers" case). It seems we might need a third one for a value array of boxed structs, I'm trying that now.
Comment 26 Martin Pitt 2012-12-18 21:50:49 UTC
Comment on attachment 231760 [details] [review]
updated patch

OK, got it. I added the missing test API in

http://git.gnome.org/browse/gobject-introspection/commit/?id=2611eb1a69bfe4a098c60ab8efda32ec443c250c

and committed Carlos' patch with the is_pointer, tab/space and changelog cleanup, together with a test case:

http://git.gnome.org/browse/pygobject/commit/?id=9454c01f2b1b82d43eea0f72fe9a28ef50065fc9

Thank you!
Comment 27 Martin Pitt 2012-12-18 21:51:46 UTC
Comment on attachment 231578 [details] [review]
introspection patch, partially fixes issue

Setting the annotation patch to "needs work", as this doesn't really help.
Comment 28 Pavel Holejsovsky 2012-12-19 10:49:57 UTC
(In reply to comment #25)
> __clipboard_get_func_cb() is never called. This might be due to the
> insufficient annotation patch
> (https://bugzilla.gnome.org/attachment.cgi?id=231578); this was already being
> discussed above, a function which shares user data between two callbacks is not
> introspectable; even more so, changing the scope to (notified) when the
> function doesn't actually take a GDestroyNotify can't work.

Carlos' introspection patch actually can work, if the binding is ready for that; the point is that both closures are bound to single user_data and one of the closures marked as async scope should cleanup resources for both closures after being executed, therefore no real GDestroyNotify callback for get_func() is provided/needed.  This is how Lua/lgi implements it and thus it works fine here.  I thought that other bindings might have problems with this, but seeing success report from Simon (comment 24) somehow made me think that even pygobject does handle that case (i.e. handling userdata/closure with 1:N relation instead of 1:1) - the only question is whether pygobject does not leak resources related to get_func() when it cleans up resources for clear_func() after being called.

Of course, in case that it is decided that this is not the way to go, the original patch introducing set_with_closures() is still available, however I'd vote for including Carlos' annotation patch in any case.
Comment 29 Martin Pitt 2012-12-19 11:03:40 UTC
(In reply to comment #28)
> Carlos' introspection patch actually can work, if the binding is ready for
> that; the point is that both closures are bound to single user_data and one of
> the closures marked as async scope should cleanup resources for both closures
> after being executed, therefore no real GDestroyNotify callback for get_func()
> is provided/needed.

That would work if it's guaranteed that the (notified) callback only gets called before the single (async) one, which certainly works in this case.

But as I said, even with that annotation patch, the __clipboard_get_func_cb() callback never gets called, so I don't think pygobject handles this.

> Of course, in case that it is decided that this is not the way to go, the
> original patch introducing set_with_closures() is still available, however I'd
> vote for including Carlos' annotation patch in any case.

It doesn't hurt, but in order to work it has to assume that bindings actually handle this case which is a potential crasher or leak.

Thanks!
Comment 30 Carlos Garnacho 2012-12-20 14:40:01 UTC
(In reply to comment #29)
> But as I said, even with that annotation patch, the __clipboard_get_func_cb()
> callback never gets called, so I don't think pygobject handles this.

That indeed deserves investigation, the annotation patch doesn't directly affect how this function is called within GTK+, but the pygobject patch you pushed as 9454c01f2b helped pass the right memory pointers as GtkTargetEntries.

With pygobject from master and python 2.7.3, when I click copy, and then the paste button on the testcase in comment #22 , I get this on stdout

* __clipboard_get_func_cb data= hello clipboard
* __paste_contents_received:  <GtkSelectionData at 0x7fffb3e5eea0> None
   - data= 8
   - type= hello clipboard
   - format= STRING
   - target= foobar

So get_func seems to be called here when requested for a known target atom
Comment 31 Martin Pitt 2012-12-20 15:22:00 UTC
> with pygobject from master and python 2.7.3

I guess that's the difference. I tried this with Python 3.3, where the __clipboard_get_func_cb() never gets called.
Comment 32 Carlos Garnacho 2012-12-20 17:00:14 UTC
This is certainly weird, I've setup a python3 pygobject env, and the function is indeed called. I've tried with different variants of the introspection patch and just removing (skip) from gtk+ master as-is gets this function called, the (closure user_data) in the patch has an effect on the data being None or valid though.

Now, it seems to fail directly after, on the first argument of selectiondata.set():

  carlos@sacarino:~/Source/gnome/pygobject$ python3 ~/Downloads/clipi_gtk3.py
  * __clipboard_get_func_cb data= hello clipboard
  Traceback (most recent call last):
  • File "/home/carlos/Downloads/clipi_gtk3.py", line 18 in __clipboard_get_func_cb
    selectiondata.set(Gdk.Atom.intern('STRING', False), 8, data)
  • File "/home/carlos/Build/gnome/lib/python3.3/site-packages/gi/types.py", line 113 in function
    return info.invoke(*args, **kwargs)   TypeError: Item 0: Must be number or single byte string, not str
* __paste_contents_received: <GtkSelectionData at 0x7fff7d9842f0> None
     - data= 0
     - type= b''
     - format= None
     - target= foobar

It does look like the GdkAtom is being dealt with as a string there, maybe getting a bit unrelated to the original bug scope...
Comment 33 Carlos Garnacho 2012-12-20 17:01:47 UTC
Created attachment 232000 [details]
testcase adapted to python3 for reference
Comment 34 Daniel Drake 2012-12-20 17:07:33 UTC
I confirm Carlos's observations: with the pygobject patch applied, and the GTK+ annotations patch, the example works fine (__clipboard_get_func_cb is called) under python2 and python3, but under python3 there is the exception with the Atom.
Comment 35 Daniel Drake 2012-12-21 08:18:37 UTC
    selectiondata.set(Gdk.Atom.intern('STRING', False), 8, data)

The Python3-specific exception is related to the handling of the final parameter, "data". We pass this as a string from __copy_clicked_cb.

The corresponding C parameter is a guchar*.

In Python 2, when marshalling here, we hit the following code in 
_pygi_marshal_from_py_array():
    if (sequence_cache->item_cache->type_tag == GI_TYPE_TAG_UINT8 &&
        PYGLIB_PyBytes_Check (py_arg)) {
        memcpy(array_->data, PYGLIB_PyBytes_AsString (py_arg), length);
        [...]
        goto array_success;
    }

In Python 3, the same PYGLIB_PyBytes_Check fails, because a string is not directly equivalent to an array of bytes as it was before.

The exception can be avoided in Python 3 by setting data to b'hello clipboard'. Similarly the same problem can be reproduced under Python 2 by setting data to u'hello clipboard'.

Not sure if this is the intended behaviour or not. Anyway, it is a bit beside the point; I can confirm that the annotations and callbacks are working after the above patches, on both Python 2.7 and Python 3.3.
Comment 36 Gonzalo Odiard 2014-05-28 21:15:08 UTC
I am testing on F20 (gtk 3.10) and the patch add_annotations_to_existing_functions [1] never landed, in gtk-3-10 or master.

I recompiled gtk (3.10) with the patch applied, and the testcase provided by garnacho works ok, then all the other pieces landed.

Should I open another ticket?

[1] https://bug656312.bugzilla-attachments.gnome.org/attachment.cgi?id=230385
Comment 37 Gonzalo Odiard 2014-05-29 13:06:27 UTC
Created attachment 277454 [details] [review]
Same patch to add annotation, but with a little change to apply on master

The only difference is the replace of “owns” by "owns".
Comment 38 Emmanuele Bassi (:ebassi) 2014-05-29 13:21:36 UTC
Review of attachment 277454 [details] [review]:

the functions are skipped because they are not really (scope async).
Comment 39 Martin Abente Lahaye 2014-09-11 13:44:50 UTC
Hello everyone, my apologies for jumping in so late in the discussion, but I was wondering what would be missing to get any of these solutions upstream.

Is there anything missing with Carlos solution? Daniel Drake and Gonzalo Odiard comments suggest that the annotations patch works.

But, if there are fundamental problems with Carlos approach, why not falling back to the original closure approach?

I am particularly  interested in this issue, as this affects Sugar on newer platforms. Please let me know if there anything I can do to help.
Comment 40 Matthias Clasen 2014-11-03 05:30:17 UTC
*** Bug 682604 has been marked as a duplicate of this bug. ***
Comment 41 Simon Feltman 2015-01-24 08:31:26 UTC
*** Bug 743054 has been marked as a duplicate of this bug. ***
Comment 42 craig 2017-05-02 23:49:00 UTC
I've been keen to see this issue solved since August 2014. See this StackOverflow question:
http://stackoverflow.com/q/25151437/60075

I'd like to use the set_with_data() function to copy HTML (plus a plain text equivalent) to the clipboard. I'm interested in doing it first in Linux, but also cross-platform.
Comment 43 makepost 2017-11-12 00:27:40 UTC
Storing multiple UTF-8 strings with different static-string targets, is the goal we're trying to achieve with gtk_clipboard_set_with_data.

The method is low-level, so if it can't be made binding-friendly, maybe instead could someone please introduce a new helper method, set_targets?

Not very familiar with C, but the signature might be something like:

void
gtk_clipboard_set_targets (GtkClipboard *clipboard,
                           gchar **targets,
                           gint *n_targets,
                           gchar **texts,
                           gint *n_texts);

For example, it would let Craig above (#comment 42) write in Python:

targets = ["text/html", "text/plain;charset=utf-8"]
texts = ["<h1>Document title</h1>", "# Document title"]
clipboard.set_targets(targets, texts)

This is the call I'd like to make from Gjs:

const targets = ["x-special/gnome-copied-files", "text/plain;charset=utf-8"];
const texts = ["copy\n" + uris, uris];
clipboard.set_targets(targets, texts);

Under the hood, it could still use gtk_clipboard_set_with_data, with a get_func choosing one of the texts based on the target of selection_data.
Comment 44 makepost 2017-11-12 01:25:31 UTC
It would also be nice for request_contents to have a form that accepts a string instead of the atom, and calls gdk_atom_intern_static_string internally. Apparently Gjs can't work with GdkAtom.
Comment 45 makepost 2017-11-16 14:20:17 UTC
Well I implemented my suggestion, https://github.com/makepost/clippy lets one store multiple UTF-8 strings with different targets in GtkClipboard from Gjs.

One difference because I misunderstood atoms at first, target strings in this case are not static, with equality check by value (gdk_atom_intern) rather than reference (gdk_atom_intern_static_string).

To build and try demo, follow https://wiki.gnome.org/Newcomers/BuildProject with the repository link above.

Please suggest what I should change so methods currently provided by this library can be merged into GtkClipboard itself.
Comment 46 makepost 2017-11-18 22:44:57 UTC
Created attachment 363992 [details] [review]
Add gtk_clipboard_set_targets()

New methods provide binding-friendly copy&paste of x-special/gnome-copied-files, text/html etc. JavaScript demo works.

https://bugzilla.gnome.org/show_bug.cgi?id=656312#c45
Comment 47 makepost 2017-11-23 00:18:56 UTC
Comment on attachment 363992 [details] [review]
Add gtk_clipboard_set_targets()

>diff --git a/gtk/gtkclipboard.c b/gtk/gtkclipboard.c
>index c328baded4..5e959cbf3c 100644
>--- a/gtk/gtkclipboard.c
>+++ b/gtk/gtkclipboard.c
>@@ -110,6 +110,7 @@ typedef struct _RequestRichTextInfo RequestRichTextInfo;
> typedef struct _RequestImageInfo RequestImageInfo;
> typedef struct _RequestURIInfo RequestURIInfo;
> typedef struct _RequestTargetsInfo RequestTargetsInfo;
>+typedef struct _SetTargetsContents SetTargetsContents;
> 
> struct _RequestContentsInfo
> {
>@@ -150,6 +151,15 @@ struct _RequestTargetsInfo
>   gpointer user_data;
> };
> 
>+struct _SetTargetsContents
>+{
>+  gint n;
>+
>+  GtkTargetEntry *targets;
>+
>+  gchar **texts;
>+};
>+
> static void gtk_clipboard_finalize     (GObject             *object);
> static void gtk_clipboard_owner_change (GtkClipboard        *clipboard,
> 					GdkEventOwnerChange *event);
>@@ -378,6 +388,26 @@ gtk_clipboard_get_default (GdkDisplay *display)
>   return gtk_clipboard_get_for_display (display, GDK_SELECTION_CLIPBOARD);
> }
> 
>+/**
>+ * gtk_clipboard_get_for_string:
>+ * @selection: a string which identifies the clipboard to use
>+ *
>+ * Returns the clipboard object for the given selection.
>+ * See gtk_clipboard_get_for_display() for complete details.
>+ *
>+ * Returns: (transfer none): the appropriate clipboard object. If no clipboard
>+ *     already exists, a new one will be created. Once a clipboard
>+ *     object has been created, it is persistent and, since it is
>+ *     owned by GTK+, must not be freed or unreffed.
>+ *
>+ * Since: 3.94
>+ */
>+GtkClipboard *
>+gtk_clipboard_get_for_string (const gchar *selection)
>+{
>+  return gtk_clipboard_get (gdk_atom_intern (selection, FALSE));
>+}
>+
> static void 
> selection_get_cb (GtkWidget          *widget,
> 		  GtkSelectionData   *selection_data,
>@@ -890,6 +920,89 @@ gtk_clipboard_set_surface (GtkClipboard    *clipboard,
> 
> }
> 
>+static void
>+targets_get_func (GtkClipboard     *clipboard,
>+                  GtkSelectionData *selection_data,
>+                  guint             info,
>+                  gpointer          data)
>+{
>+  gint i;
>+  SetTargetsContents *contents = data;
>+  GdkAtom target = gtk_selection_data_get_target (selection_data);
>+
>+  for (i = 0; i < contents->n; i++)
>+    {
>+      if (target == gdk_atom_intern (contents->targets[i].target, FALSE))
>+        {
>+          gtk_selection_data_set (selection_data, target, 8,
>+                                  (guchar *) contents->texts[i],
>+                                  strlen (contents->texts[i]));
>+          break;
>+        }
>+    }
>+}
>+
>+static void
>+targets_clear_func (GtkClipboard *clipboard,
>+                    gpointer      data)
>+{
>+  SetTargetsContents *contents = data;
>+
>+  gtk_target_table_free (contents->targets, contents->n);
>+  g_strfreev (contents->texts);
>+  g_free (contents);
>+}
>+
>+/**
>+ * gtk_clipboard_set_targets:
>+ * @clipboard: a #GtkClipboard object
>+ * @targets:   (array length=n_targets): array containing information
>+ *     about the available forms for the clipboard data
>+ * @n_targets: number of elements in @targets
>+ * @texts:     (array length=n_texts): UTF-8 strings
>+ * @n_texts:   number of elements in @targets, must be equal to n_targets
>+ *
>+ * Sets the contents of the clipboard to the given UTF-8 strings, each
>+ * corresponding to a given data type.
>+ *
>+ * Since: 3.94
>+ **/
>+void
>+gtk_clipboard_set_targets (GtkClipboard  *clipboard,
>+                           gchar        **targets,
>+                           gint           n_targets,
>+                           gchar        **texts,
>+                           gint           n_texts)
>+{
>+  SetTargetsContents *contents;
>+  gint i;
>+  GtkTargetList *target_list;
>+
>+  g_return_if_fail (GTK_IS_CLIPBOARD (clipboard));
>+  g_return_if_fail (targets != NULL);
>+  g_return_if_fail (texts != NULL);
>+  g_return_if_fail (n_targets == n_texts);
>+
>+  contents = g_new (SetTargetsContents, 1);
>+  contents->texts = g_strdupv (texts);
>+  target_list = gtk_target_list_new (NULL, 0);
>+
>+  for (i = 0; i < n_targets; i++)
>+    gtk_target_list_add (target_list,
>+                         gdk_atom_intern (targets[i], FALSE), 0, 0);
>+
>+  contents->targets = gtk_target_table_new_from_list (target_list,
>+                                                      &contents->n);
>+
>+  gtk_clipboard_set_with_data (clipboard,
>+                               contents->targets, n_targets,
>+                               targets_get_func, targets_clear_func,
>+                               contents);
>+  gtk_clipboard_set_can_store (clipboard, NULL, 0);
>+
>+  gtk_target_list_unref (target_list);
>+}
>+
> static void
> set_request_contents_info (GtkWidget           *widget,
> 			   RequestContentsInfo *info)
>@@ -1338,6 +1451,34 @@ gtk_clipboard_request_targets (GtkClipboard                *clipboard,
> 				  info);
> }
> 
>+/**
>+ * gtk_clipboard_request_target:
>+ * @clipboard: a #GtkClipboard
>+ * @target:    information about the form into which the clipboard
>+ *     owner should convert the selection
>+ * @callback:  (scope async): A function to call when the results are received
>+ *     (or the retrieval fails). If the retrieval fails the length field of
>+ *     @selection_data will be negative.
>+ * @user_data: user data to pass to @callback
>+ *
>+ * Requests the contents of clipboard as the given target.
>+ * When the results of the result are later received the supplied callback
>+ * will be called.
>+ **/
>+void
>+gtk_clipboard_request_target (GtkClipboard             *clipboard,
>+                              const gchar              *target,
>+                              GtkClipboardReceivedFunc  callback,
>+                              gpointer                  user_data)
>+{
>+  g_return_if_fail (GTK_IS_CLIPBOARD (clipboard));
>+  g_return_if_fail (callback != NULL);
>+
>+  gtk_clipboard_request_contents (clipboard,
>+                                  gdk_atom_intern (target, FALSE),
>+                                  callback, user_data);
>+}
>+
> typedef struct
> {
>   GMainLoop *loop;
>diff --git a/gtk/gtkclipboard.h b/gtk/gtkclipboard.h
>index ecc49bee10..73f3443857 100644
>--- a/gtk/gtkclipboard.h
>+++ b/gtk/gtkclipboard.h
>@@ -184,9 +184,10 @@ GtkClipboard *gtk_clipboard_get_for_display (GdkDisplay   *display,
> 					     GdkAtom       selection);
> GDK_AVAILABLE_IN_ALL
> GtkClipboard *gtk_clipboard_get             (GdkAtom       selection);
>-
> GDK_AVAILABLE_IN_3_16
>-GtkClipboard *gtk_clipboard_get_default     (GdkDisplay    *display);
>+GtkClipboard *gtk_clipboard_get_default     (GdkDisplay   *display);
>+GDK_AVAILABLE_IN_3_94
>+GtkClipboard *gtk_clipboard_get_for_string  (const gchar  *selection);
> 
> GDK_AVAILABLE_IN_ALL
> GdkDisplay   *gtk_clipboard_get_display     (GtkClipboard *clipboard);
>@@ -218,6 +219,12 @@ void     gtk_clipboard_set_image      (GtkClipboard          *clipboard,
> GDK_AVAILABLE_IN_3_94
> void     gtk_clipboard_set_surface    (GtkClipboard    *clipboard,
>                                        cairo_surface_t *surface);
>+GDK_AVAILABLE_IN_3_94
>+void     gtk_clipboard_set_targets    (GtkClipboard  *clipboard,
>+                                       gchar        **targets,
>+                                       gint           n_targets,
>+                                       gchar        **texts,
>+                                       gint           n_texts);
> 
> GDK_AVAILABLE_IN_ALL
> void gtk_clipboard_request_contents  (GtkClipboard                     *clipboard,
>@@ -245,6 +252,11 @@ GDK_AVAILABLE_IN_ALL
> void gtk_clipboard_request_targets   (GtkClipboard                     *clipboard,
>                                       GtkClipboardTargetsReceivedFunc   callback,
>                                       gpointer                          user_data);
>+GDK_AVAILABLE_IN_3_94
>+void gtk_clipboard_request_target    (GtkClipboard                     *clipboard,
>+                                      const gchar                      *target,
>+                                      GtkClipboardReceivedFunc          callback,
>+                                      gpointer                          user_data);
> 
> GDK_AVAILABLE_IN_ALL
> GtkSelectionData *gtk_clipboard_wait_for_contents  (GtkClipboard  *clipboard,
Comment 48 makepost 2017-11-23 00:25:47 UTC
Created attachment 364244 [details] [review]
Add gtk_clipboard_set_targets() with brace indent fix

Sorry for the accidental comment. Thought the "Edit Attachment As Comment" button would "Edit Attachment", but the real keywords are "As Comment".
Comment 49 makepost 2017-11-23 00:51:55 UTC
Created attachment 364245 [details] [review]
Add gtk_clipboard_set_targets() with brace indent fix and no quote

Diff, not diff quote... Sorry for repeated noise.
Comment 50 GNOME Infrastructure Team 2018-05-02 15:10:41 UTC
-- GitLab Migration Automatic Message --

This bug has been migrated to GNOME's GitLab instance and has been closed from further activity.

You can subscribe and participate further through the new bug through this link to our GitLab instance: https://gitlab.gnome.org/GNOME/gtk/issues/364.