Closed snake-scaly closed 1 year ago
I've tested my application on a vanilla Kubuntu with Eto.Platforms.Gtk 2.7.5. The crash is 100% reproducible there when a Drop handler attempts to get Uris from the drop event.
My fix worked well for me so far. All drag and drop scenarios which I needed worked correctly. However I didn't do any extensive testing which would require trying this in multiple operating systems and desktop environments.
Expected Behavior
The
Control.OnDragEnter
,Control.OnDragDrop
and friends should allow drag data introspection and retrieval. E.g. ifDragEventArgs.Data.ContainsUris
istrue
thenDragEventArgs.Data.Uris
should be a non-empty array of URIs.Actual Behavior
An attempt to access the
DragEventArgs.Data.Uris
property crashes with aNullReferenceException
.Steps to Reproduce the Problem
You can use my project https://github.com/snake-scaly/PinBoard to reproduce the issue. It should support dragging and dropping images from a file manager, an image viewer, or a web browser onto the canvas. The most straightforward test is to drag an image from a file manager and drop it onto the application.
See the
BoardView
class for the relevant code. For me it is 100% reproducible, AKA never works.Analysis
The problem is that Gtk drag-and-drop model is arguably incompatible with Eto architecture. This is not a design flaw on the Eto part, this is just the way things are. The main problems are:
drag-motion
or adrag-drop
event. An application is expected to callGtk.DragDrop.GetData()
, exit the current handler, wait for thedrag-data-received
event, and complete the handling there. There is no such concept in Eto however. The data must be available on demand right in theOnDragEnter
andOnDragDrop
handlers. The current solution is to block the handler execution whenever data is requested and spin a nested message loop untildrag-data-received
arrives. The problem with that is that any other events will be processed, too, and if another drag-related event arrives it can mess up the handler's internal state. That other event is probably anotherdrag-motion
event, I'm not entirely sure.drag-leave
event before getting adrag-drop
event. It may also never receive thedrag-drop
event ifdrag-leave
was generated by the mouse leaving the control boundaries. In thedrag-leave
handler there is no way to tell if adrag-drop
will follow. Currently the Etodrag-leave
handler performs some cleanup which may be the reason why the application crashes. Again, not entirely sure.Solution
https://github.com/picoe/Eto/compare/develop...snake-scaly:Eto:feature/drag-drop-fix
I've made a patch that solves this issue for me. My approach is to request and cache data for all targets in the context, in the normal message loop, before calling
OnDragEnter
. Then I destroy that data ondrag-leave
, and request it again ifdrag-drop
happens. This is very resource-intensive, because if there are 7 targets in the context, all 7 will be requested and stored even though many are duplicates, and most will never be requested by the application. This however allows me to stay within the normal message loop and therefore have a predictable state machine in the handler.Specifications