Drag-n-drop problems on Linux vs Windows

Some background: I’m working on VICE, the Commodore 8-bit emulator suite, and this specific issue relates to VSID, a SID file player we provide. Here’s a screenshot making it easier to explain what I’m doing:

I have three ‘panes’: the left one has controls and info on the current SID song, the middle displays extra information from an external database (this one gives me trouble), and the right pane contains a playlist.

The trouble is the middle pane: it’s a GtkGrid, which contains a GtkLabel at 0,0, and a GtkScrolledWindow at 0,1 containing the GtkTextView which doesn’t respond to drop events.

On Windows, connecting the ‘drag-data-received’ and ‘drag-drop’ events to signal handlers which play a new SID file works. On Linux dragging a SID file onto the label above the GtkTextView works, but even hooking up event handlers to the GtkTextView and/or GtkScrolledWindow on Linux doesn’t trigger anything (my event handlers use g_print() for ‘debugging’, nothing is printed).

It’s a little difficult to distil my code to the absolute minimum (but I’m prepared to do that), meanwhile, here’s some of the relevant code:

My drop targets:

/** \brief  List of drag targets for the drag-n-drop event handler
 *
 * It would appear different OS'es/WM's pass dropped files using various
 * mime-types.
 */
GtkTargetEntry ui_drag_targets[UI_DRAG_TARGETS_COUNT] = {
    { "text/plain",     0, DT_TEXT },   /* we get this on at least my Linux
                                           box with Mate */
    { "text/uri",       0, DT_URI },
    { "text/uri-list",  0, DT_URI_LIST }    /* we get this using Windows
                                               Explorer or macOS Finder */
};

And here I connect the signal handlers (the stil_widget is the GtkGrid containing the widgets in the middle pane):

    /* middle pane: STIL widget */
    gtk_drag_dest_set(
            stil_widget,
            GTK_DEST_DEFAULT_ALL,
            ui_drag_targets,
            UI_DRAG_TARGETS_COUNT,
            GDK_ACTION_COPY);
    g_signal_connect(stil_widget, "drag-data-received",
                     G_CALLBACK(on_drag_data_received), NULL);
    g_signal_connect(stil_widget, "drag-drop",
                     G_CALLBACK(on_drag_drop), NULL);

I’ve tried doing the same for the GtkScrolledWindow and the GtkTextView, but on Linux no event handler is triggered.

Should I wrap the GtkGrid in an event box and have that handle the drop events? I’m a little confused at the moment, especially since Windows seems to handle this 'correctly. I can even drag files from/to the VM and the playlist works, it’s the GtkTextView that only accepts drop events on Windows and not on Linux (on bare metal neither).

/Compyx

I’m not in the habit of bumping my own posts, but I have an additional question:

What are the valid values for the targetand the info members of the GtkTargetEntry struct? I’ve looked at the code for the GtkTargetEntry without much luck.

I believe the most commonly used target atoms are STRING, UTF8_STRING, and any MIME types such as text/html or text/uri-list, but I think technically it can be any string. As long as the drag source and destination can agree on one of the provided target types then the drag can happen.

If you’re writing an app that accepts drag data from external apps, you’ll have to find out what data types those external apps offer. It’s been a while since I’ve done this but check out the low-level drag-motion signal to get the GdkDragContext, then call gdk_drag_context_list_targets.

I’m not too sure about info. Most code I’ve seen start at 0 and go up but it doesn’t seem to really matter.

I tried a lot of different target strings, indeed even ‘STRING’, ‘TEXT’, ‘ASCII’ etc. Didn’t work so far, and their weird thing is that Windows works and Linux doesn’t, I would have expected it to behave the other way around: Linux working and Windows causing trouble as usual.

Thanks, that might give me some indication as to what various OS’es/WM’s expect.

Yeah, I just use an enum there, starting at 0 and going up. Probably meant to avoid having to use strcmp() or similar.

Anyway, thanks for the info on drag-motion and friends, that should give me a proper way to debug my drag-n-drop code.

Here’s PyGObject code that advertises that you want to accept any target type and prints them on dropping.

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk

window = Gtk.Window()
window.connect('destroy', Gtk.main_quit)

def on_drag_motion(widget, context, x, y, timestamp):
    Gdk.drag_status(context, Gdk.DragAction.COPY, timestamp)
    return True

def on_drag_drop(widget, context, x, y, timestamp):
    for target in context.list_targets():
        print(target.name())
    Gtk.drag_finish(context, True, False, timestamp)
    return True

window.connect('drag-motion', on_drag_motion)
window.connect('drag-drop', on_drag_drop)
window.drag_dest_set(0, [], 0)  # Do everything manually

window.show_all()
Gtk.main()

Dragging a file from Caja, I get these target types:

x-special/mate-icon-list
text/uri-list
UTF8_STRING
COMPOUND_TEXT
TEXT
STRING
text/plain;charset=utf-8
text/plain

Note that on Windows, dragging from external (non-GTK?) apps seems somewhat broken. drag-motion doesn’t get emitted at all, and it only accepts files (for example from Windows Explorer).

I’m having some trouble translating that code to C. I’m using gdk_drag_context_list_targets(), which returns a GList, Iterating that list, I found that each data member of the GList node contains a low value (ie a byte), and not a string as print(target.name()) would suggest:

[debug-gtk3] ../../../../../vice/src/arch/gtk3/widgets/vsidmainwidget.c:113::on_drag_drop(): target: 0xa6.
[debug-gtk3] ../../../../../vice/src/arch/gtk3/widgets/vsidmainwidget.c:113::on_drag_drop(): target: 0x8a.
[debug-gtk3] ../../../../../vice/src/arch/gtk3/widgets/vsidmainwidget.c:113::on_drag_drop(): target: 0x46.
[debug-gtk3] ../../../../../vice/src/arch/gtk3/widgets/vsidmainwidget.c:113::on_drag_drop(): target: 0x86.
[debug-gtk3] ../../../../../vice/src/arch/gtk3/widgets/vsidmainwidget.c:113::on_drag_drop(): target: 0x85.
[debug-gtk3] ../../../../../vice/src/arch/gtk3/widgets/vsidmainwidget.c:113::on_drag_drop(): target: 0x1f.
[debug-gtk3] ../../../../../vice/src/arch/gtk3/widgets/vsidmainwidget.c:113::on_drag_drop(): target: 0x88.
[debug-gtk3] ../../../../../vice/src/arch/gtk3/widgets/vsidmainwidget.c:113::on_drag_drop(): target: 0x87.

(I’m using “%p” in those g_print() calls)

So it seems I need to use some function to translate the ‘data’ member of the GList nodes to something human-readable.

But I suppose the more serious issue is that none of the d’n’d events get triggered when dragging and dropping a file from the ‘Files’ application (which I assume is Nautilus) onto the GtkTextView mentioned in a previous post.

The function documentation actually mentions the type being returned, but you have to know where to look: in the Returns section you can see «[element-type GdkAtom]». And then from there to get the string you can call gdk_atom_name.

But I suppose the more serious issue is that none of the d’n’d events get triggered when dragging and dropping a file from the ‘Files’ application (which I assume is Nautilus) onto the GtkTextView mentioned in a previous post.

I can’t reproduce the issue you’re getting on Linux (under Xorg).

PyGObject code
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk

window = Gtk.Window()
window.connect('destroy', Gtk.main_quit)

textview = Gtk.TextView()
textview.connect('drag-data-received', lambda *a: print('ddr', a))
textview.drag_dest_set(Gtk.DestDefaults.ALL, [Gtk.TargetEntry.new('text/uri-list', 0, 0)], Gdk.DragAction.COPY)
window.add(textview)

window.show_all()
Gtk.main()

With this code I’m getting the drag-data-received signal (twice, even).

Well, I just found the issue, or at least partially: when setting the GtkTextView to readonly, Gtk3 on Linux doesn’t emit any signals. Windows seems to be fine with a (parent) widget accept signals even if the GtkTextView is readonly.
I’m still waiting on reports on how MacOS handles this. (Edit: massive CPU usage, not responding well, but that could be my fault)

So now, connecting extra event handlers to the GtkTextView and setting it to editable makes dragging a file onto it work, but currently pastes the URI into the textview, which I somehow have to block.

Ah, I see. There’s an easier way that doesn’t require you to set editable, I think: just connect to drag-motion and return true. Not sure that’s the proper way to do it, but it seems to work on my limited testing.

Just tried that, doesn’t work on my Debian VM. Non of the drag-motion, drag-drop or drag-data-received events trigger when having the GtkTextView set to non-editable.

I could try bare-metal Linux, but I don’t think that’ll do much good.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.