Merge pull request #542 from VPeruS/drag-and-drop

Drag and drop support
This commit is contained in:
varon 2017-07-04 21:40:26 +02:00 committed by GitHub
commit 09bd2d5d10
13 changed files with 440 additions and 10 deletions

View file

@ -279,9 +279,9 @@ namespace OpenTK
//event EventHandler<MouseEventArgs> MouseClick; //event EventHandler<MouseEventArgs> MouseClick;
//event EventHandler<MouseEventArgs> MouseDoubleClick; //event EventHandler<MouseEventArgs> MouseDoubleClick;
//event EventHandler<DragEventArgs> DragDrop; /// <summary>
//event EventHandler<DragEventArgs> DragEnter; /// Occurs whenever file dropped on window.
//event EventHandler<DragEventArgs> DragOver; /// </summary>
//event EventHandler<EventArgs> DragLeave; event EventHandler<OpenTK.Input.FileDropEventArgs> FileDrop;
} }
} }

View file

@ -0,0 +1,22 @@
using System;
namespace OpenTK.Input
{
/// <summary>
/// Defines the event data for <see cref="NativeWindow"/> events.
/// </summary>
public class FileDropEventArgs : EventArgs
{
string fileName;
/// <summary>
/// Gets the name of the file.
/// </summary>
/// <value>The name of the file.</value>
public string FileName
{
get;
internal set;
}
}
}

View file

@ -692,6 +692,11 @@ namespace OpenTK
/// </summary> /// </summary>
public event EventHandler<MouseWheelEventArgs> MouseWheel = delegate { }; public event EventHandler<MouseWheelEventArgs> MouseWheel = delegate { };
/// <summary>
/// Occurs whenever a file dropped on window;
/// </summary>
public event EventHandler<FileDropEventArgs> FileDrop = delegate { };
#endregion #endregion
#endregion #endregion
@ -964,6 +969,18 @@ namespace OpenTK
MouseWheel(this, e); MouseWheel(this, e);
} }
/// <summary>
/// Raises the <see cref="FileDrop"/> event.
/// </summary>
/// <param name="e">
/// A <see cref="FileDropEventArgs"/> instance carrying file name.
/// The information carried by this instance is only valid within this method body.
/// </param>
protected virtual void OnFileDrop(FileDropEventArgs e)
{
FileDrop(this, e);
}
#region OnResize #region OnResize
/// <summary> /// <summary>
@ -1125,6 +1142,8 @@ namespace OpenTK
private void OnMouseMoveInternal(object sender, MouseMoveEventArgs e) { OnMouseMove(e); } private void OnMouseMoveInternal(object sender, MouseMoveEventArgs e) { OnMouseMove(e); }
private void OnMouseWheelInternal(object sender, MouseWheelEventArgs e) { OnMouseWheel(e); } private void OnMouseWheelInternal(object sender, MouseWheelEventArgs e) { OnMouseWheel(e); }
private void OnFileDropInternal(object sender, FileDropEventArgs e) { OnFileDrop(e); }
#region OnMoveInternal #region OnMoveInternal
private void OnMoveInternal(object sender, EventArgs e) { OnMove(e); } private void OnMoveInternal(object sender, EventArgs e) { OnMove(e); }
@ -1197,6 +1216,7 @@ namespace OpenTK
implementation.VisibleChanged += OnVisibleChangedInternal; implementation.VisibleChanged += OnVisibleChangedInternal;
implementation.WindowBorderChanged += OnWindowBorderChangedInternal; implementation.WindowBorderChanged += OnWindowBorderChangedInternal;
implementation.WindowStateChanged += OnWindowStateChangedInternal; implementation.WindowStateChanged += OnWindowStateChangedInternal;
implementation.FileDrop += OnFileDropInternal;
events = true; events = true;
} }
else if (events) else if (events)
@ -1221,6 +1241,7 @@ namespace OpenTK
implementation.VisibleChanged -= OnVisibleChangedInternal; implementation.VisibleChanged -= OnVisibleChangedInternal;
implementation.WindowBorderChanged -= OnWindowBorderChangedInternal; implementation.WindowBorderChanged -= OnWindowBorderChangedInternal;
implementation.WindowStateChanged -= OnWindowStateChangedInternal; implementation.WindowStateChanged -= OnWindowStateChangedInternal;
implementation.FileDrop -= OnFileDropInternal;
events = false; events = false;
} }
else else

View file

@ -772,8 +772,10 @@
<Compile Include="Platform\Linux\Bindings\Evdev.cs" /> <Compile Include="Platform\Linux\Bindings\Evdev.cs" />
<Compile Include="Platform\Linux\DefaultCursor.cs" /> <Compile Include="Platform\Linux\DefaultCursor.cs" />
<Compile Include="Platform\Linux\Bindings\Kms.cs" /> <Compile Include="Platform\Linux\Bindings\Kms.cs" />
<Compile Include="Input\FileDropEventArgs.cs" />
<None Include="OpenTK.csproj.paket.template" /> <None Include="OpenTK.csproj.paket.template" />
<None Include="paket.references" /> <None Include="paket.references" />
<Compile Include="Platform\MacOS\Cocoa\NSDragOperation.cs" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<PropertyGroup> <PropertyGroup>

View file

@ -0,0 +1,23 @@
using System;
namespace OpenTK.Platform.MacOS
{
/// <summary>
/// Used by draggingSourceOperationMask() <see cref="CocoaNativeWindow.DraggingEntered"/> to get permission for dropped object
/// also used for respones to drag source
/// Values for enum can be found here https://developer.apple.com/documentation/appkit/nsdragoperation?language=objc
/// or for Mac users /System/Library/Frameworks/AppKit.framework/Headers
/// </summary>
internal enum NSDragOperation : int
{
None = 0,
Copy = 1,
Link = 2,
Generic = 4,
Private = 8,
AllObsolete = 15,
Move = 16,
Delete = 32,
Every = Int32.MaxValue,
}
}

View file

@ -116,6 +116,7 @@ namespace OpenTK.Platform.MacOS
static readonly IntPtr NSImage; static readonly IntPtr NSImage;
static readonly IntPtr NSBitmapImageRep; static readonly IntPtr NSBitmapImageRep;
static readonly IntPtr NSDeviceRGBColorSpace = Cocoa.ToNSString("NSDeviceRGBColorSpace"); static readonly IntPtr NSDeviceRGBColorSpace = Cocoa.ToNSString("NSDeviceRGBColorSpace");
static readonly IntPtr NSFilenamesPboardType;
static CocoaNativeWindow() static CocoaNativeWindow()
{ {
@ -125,6 +126,7 @@ namespace OpenTK.Platform.MacOS
NSCursor = Class.Get("NSCursor"); NSCursor = Class.Get("NSCursor");
NSImage = Class.Get("NSImage"); NSImage = Class.Get("NSImage");
NSBitmapImageRep = Class.Get("NSBitmapImageRep"); NSBitmapImageRep = Class.Get("NSBitmapImageRep");
NSFilenamesPboardType = Cocoa.GetStringConstant(Cocoa.AppKitLibrary, "NSFilenamesPboardType");
} }
private CocoaWindowInfo windowInfo; private CocoaWindowInfo windowInfo;
@ -165,6 +167,8 @@ namespace OpenTK.Platform.MacOS
CanBecomeKeyWindowHandler = CanBecomeKeyWindow; CanBecomeKeyWindowHandler = CanBecomeKeyWindow;
CanBecomeMainWindowHandler = CanBecomeMainWindow; CanBecomeMainWindowHandler = CanBecomeMainWindow;
ResetCursorRectsHandler = ResetCursorRects; ResetCursorRectsHandler = ResetCursorRects;
PerformDragOperationHandler = PerformDragOperation;
DraggingEnteredHandler = DraggingEntered;
// Create the window class // Create the window class
int unique_id = Interlocked.Increment(ref UniqueId); int unique_id = Interlocked.Increment(ref UniqueId);
@ -182,6 +186,9 @@ namespace OpenTK.Platform.MacOS
Class.RegisterMethod(windowClass, AcceptsFirstResponderHandler, "acceptsFirstResponder", "b@:"); Class.RegisterMethod(windowClass, AcceptsFirstResponderHandler, "acceptsFirstResponder", "b@:");
Class.RegisterMethod(windowClass, CanBecomeKeyWindowHandler, "canBecomeKeyWindow", "b@:"); Class.RegisterMethod(windowClass, CanBecomeKeyWindowHandler, "canBecomeKeyWindow", "b@:");
Class.RegisterMethod(windowClass, CanBecomeMainWindowHandler, "canBecomeMainWindow", "b@:"); Class.RegisterMethod(windowClass, CanBecomeMainWindowHandler, "canBecomeMainWindow", "b@:");
Class.RegisterMethod(windowClass, DraggingEnteredHandler, "draggingEntered:", "@@:@");
Class.RegisterMethod(windowClass, PerformDragOperationHandler, "performDragOperation:", "b@:@");
Class.RegisterClass(windowClass); Class.RegisterClass(windowClass);
IntPtr viewClass = Class.AllocateClass("OpenTK_NSView" + unique_id, "NSView"); IntPtr viewClass = Class.AllocateClass("OpenTK_NSView" + unique_id, "NSView");
@ -257,6 +264,16 @@ namespace OpenTK.Platform.MacOS
exists = true; exists = true;
NSApplication.Quit += ApplicationQuit; NSApplication.Quit += ApplicationQuit;
// Enable drag and drop
Cocoa.SendIntPtr(
windowPtr,
Selector.Get("registerForDraggedTypes:"),
Cocoa.SendIntPtr(
Class.Get("NSArray"),
Selector.Get("arrayWithObjects:"),
NSFilenamesPboardType,
IntPtr.Zero));
} }
[UnmanagedFunctionPointer(CallingConvention.Winapi)] [UnmanagedFunctionPointer(CallingConvention.Winapi)]
@ -287,6 +304,10 @@ namespace OpenTK.Platform.MacOS
delegate bool CanBecomeMainWindowDelegate(IntPtr self, IntPtr cmd); delegate bool CanBecomeMainWindowDelegate(IntPtr self, IntPtr cmd);
[UnmanagedFunctionPointer(CallingConvention.Winapi)] [UnmanagedFunctionPointer(CallingConvention.Winapi)]
delegate void ResetCursorRectsDelegate(IntPtr self, IntPtr cmd); delegate void ResetCursorRectsDelegate(IntPtr self, IntPtr cmd);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
delegate IntPtr DraggingEnteredDelegate(IntPtr self, IntPtr cmd, IntPtr sender);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
delegate bool PerformDragOperationDelegate(IntPtr self, IntPtr cmd, IntPtr sender);
WindowKeyDownDelegate WindowKeyDownHandler; WindowKeyDownDelegate WindowKeyDownHandler;
WindowDidResizeDelegate WindowDidResizeHandler; WindowDidResizeDelegate WindowDidResizeHandler;
@ -302,6 +323,37 @@ namespace OpenTK.Platform.MacOS
CanBecomeKeyWindowDelegate CanBecomeKeyWindowHandler; CanBecomeKeyWindowDelegate CanBecomeKeyWindowHandler;
CanBecomeMainWindowDelegate CanBecomeMainWindowHandler; CanBecomeMainWindowDelegate CanBecomeMainWindowHandler;
ResetCursorRectsDelegate ResetCursorRectsHandler; ResetCursorRectsDelegate ResetCursorRectsHandler;
DraggingEnteredDelegate DraggingEnteredHandler;
PerformDragOperationDelegate PerformDragOperationHandler;
private IntPtr DraggingEntered(IntPtr self, IntPtr cmd, IntPtr sender)
{
int mask = Cocoa.SendInt(sender, Selector.Get("draggingSourceOperationMask"));
if ((mask & (int)NSDragOperation.Generic) == (int)NSDragOperation.Generic)
{
return new IntPtr((int)NSDragOperation.Generic);
}
return new IntPtr((int)NSDragOperation.None);
}
private bool PerformDragOperation(IntPtr self, IntPtr cmd, IntPtr sender)
{
IntPtr pboard = Cocoa.SendIntPtr(sender, Selector.Get("draggingPasteboard"));
IntPtr files = Cocoa.SendIntPtr(pboard, Selector.Get("propertyListForType:"), NSFilenamesPboardType);
int count = Cocoa.SendInt(files, Selector.Get("count"));
for (int i = 0; i < count; ++i)
{
IntPtr obj = Cocoa.SendIntPtr(files, Selector.Get("objectAtIndex:"), new IntPtr(i));
IntPtr str = Cocoa.SendIntPtr(obj, Selector.Get("cStringUsingEncoding:"), new IntPtr(1));
OnFileDrop(Marshal.PtrToStringAuto(str));
}
return true;
}
private void WindowKeyDown(IntPtr self, IntPtr cmd, IntPtr notification) private void WindowKeyDown(IntPtr self, IntPtr cmd, IntPtr notification)
{ {

View file

@ -49,6 +49,8 @@ namespace OpenTK.Platform
readonly KeyboardKeyEventArgs KeyUpArgs = new KeyboardKeyEventArgs(); readonly KeyboardKeyEventArgs KeyUpArgs = new KeyboardKeyEventArgs();
readonly KeyPressEventArgs KeyPressArgs = new KeyPressEventArgs((char)0); readonly KeyPressEventArgs KeyPressArgs = new KeyPressEventArgs((char)0);
readonly FileDropEventArgs FileDropArgs = new FileDropEventArgs();
// In order to simplify mouse event implementation, // In order to simplify mouse event implementation,
// we can store the current mouse state here. // we can store the current mouse state here.
protected MouseState MouseState = new MouseState(); protected MouseState MouseState = new MouseState();
@ -149,6 +151,13 @@ namespace OpenTK.Platform
KeyUp(this, e); KeyUp(this, e);
} }
protected void OnFileDrop(string s)
{
var e = FileDropArgs;
FileDropArgs.FileName = s;
FileDrop(this, e);
}
/// \internal /// \internal
/// <summary> /// <summary>
/// Call this method to simulate KeyDown/KeyUp events /// Call this method to simulate KeyDown/KeyUp events
@ -311,6 +320,7 @@ namespace OpenTK.Platform
public event EventHandler<MouseButtonEventArgs> MouseUp = delegate { }; public event EventHandler<MouseButtonEventArgs> MouseUp = delegate { };
public event EventHandler<MouseMoveEventArgs> MouseMove = delegate { }; public event EventHandler<MouseMoveEventArgs> MouseMove = delegate { };
public event EventHandler<MouseWheelEventArgs> MouseWheel = delegate { }; public event EventHandler<MouseWheelEventArgs> MouseWheel = delegate { };
public event EventHandler<FileDropEventArgs> FileDrop = delegate { };
public abstract void Close(); public abstract void Close();

View file

@ -139,6 +139,10 @@ namespace OpenTK.Platform.SDL2
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_FreeSurface", ExactSpelling = true)] [DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_FreeSurface", ExactSpelling = true)]
public static extern void FreeSurface(IntPtr surface); public static extern void FreeSurface(IntPtr surface);
[SuppressUnmanagedCodeSecurity]
[DllImport (lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_free", ExactSpelling = true)]
public static extern void Free(IntPtr memblock);
#region GameContoller #region GameContoller
[SuppressUnmanagedCodeSecurity] [SuppressUnmanagedCodeSecurity]
@ -1442,6 +1446,8 @@ namespace OpenTK.Platform.SDL2
public ControllerButtonEvent ControllerButton; public ControllerButtonEvent ControllerButton;
[FieldOffset(0)] [FieldOffset(0)]
public ControllerDeviceEvent ControllerDevice; public ControllerDeviceEvent ControllerDevice;
[FieldOffset(0)]
public DropEvent Drop;
#if false #if false
[FieldOffset(0)] [FieldOffset(0)]
public QuitEvent quit; public QuitEvent quit;
@ -1455,8 +1461,6 @@ namespace OpenTK.Platform.SDL2
public MultiGestureEvent mgesture; public MultiGestureEvent mgesture;
[FieldOffset(0)] [FieldOffset(0)]
public DollarGestureEvent dgesture; public DollarGestureEvent dgesture;
[FieldOffset(0)]
public DropEvent drop;
#endif #endif
// Ensure the structure is big enough // Ensure the structure is big enough
@ -1755,6 +1759,17 @@ namespace OpenTK.Platform.SDL2
public Int32 Data2; public Int32 Data2;
} }
/// <summary>
/// Drop event for SDL2 interop. For detailed info look: https://wiki.libsdl.org/SDL_DropEvent
/// </summary>
struct DropEvent
{
public UInt32 Type;
public UInt32 Timestamp;
public IntPtr File;
public UInt32 WindowID;
}
#endregion #endregion
} }

View file

@ -197,6 +197,15 @@ namespace OpenTK.Platform.SDL2
} }
break; break;
case EventType.DROPFILE:
if (windows.TryGetValue(ev.Drop.WindowID, out window))
{
ProcessDropEvent(window, ev.Drop);
SDL.Free(ev.Drop.File);
processed = true;
}
break;
case EventType.QUIT: case EventType.QUIT:
Debug.WriteLine("Sdl2 application quit"); Debug.WriteLine("Sdl2 application quit");
break; break;
@ -293,6 +302,12 @@ namespace OpenTK.Platform.SDL2
window.OnMouseWheel(ev.X, ev.Y); window.OnMouseWheel(ev.X, ev.Y);
} }
static unsafe void ProcessDropEvent(Sdl2NativeWindow window, DropEvent ev)
{
string dropString = Marshal.PtrToStringAuto(ev.File);
window.OnFileDrop(dropString);
}
static void ProcessWindowEvent(Sdl2NativeWindow window, WindowEvent e) static void ProcessWindowEvent(Sdl2NativeWindow window, WindowEvent e)
{ {
switch (e.Event) switch (e.Event)

View file

@ -57,6 +57,8 @@ namespace OpenTK.Platform.Windows
using HKEY = System.IntPtr; using HKEY = System.IntPtr;
using PHKEY = System.IntPtr; using PHKEY = System.IntPtr;
using HDROP = System.IntPtr;
using LRESULT = System.IntPtr; using LRESULT = System.IntPtr;
using LPVOID = System.IntPtr; using LPVOID = System.IntPtr;
using LPCTSTR = System.String; using LPCTSTR = System.String;
@ -134,6 +136,25 @@ namespace OpenTK.Platform.Windows
{ {
#region Window functions #region Window functions
[DllImport("shell32.dll")]
internal static extern bool DragAcceptFiles(
IntPtr handle,
[MarshalAs(UnmanagedType.Bool)] bool fAccept
);
[DllImport("shell32.dll")]
internal static extern uint DragQueryFile(
HDROP hDrop,
uint iFile,
IntPtr lpszFile,
uint cch
);
[DllImport("shell32.dll")]
internal static extern void DragFinish(
HDROP hDrop
);
#region SetWindowPos #region SetWindowPos
// WINUSERAPI BOOL WINAPI SetWindowPos(__in HWND hWnd, __in_opt HWND hWndInsertAfter, // WINUSERAPI BOOL WINAPI SetWindowPos(__in HWND hWnd, __in_opt HWND hWndInsertAfter,

View file

@ -150,6 +150,7 @@ namespace OpenTK.Platform.Windows
0, 0, ClientSize.Width, ClientSize.Height, 0, 0, ClientSize.Width, ClientSize.Height,
title, options, device, window.Handle), title, options, device, window.Handle),
window); window);
Functions.DragAcceptFiles(window.Handle, true);
exists = true; exists = true;
} }
@ -680,6 +681,27 @@ namespace OpenTK.Platform.Windows
OnClosed(EventArgs.Empty); OnClosed(EventArgs.Empty);
} }
void HandleDropFiles(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
IntPtr hDrop = wParam;
uint filesCounter = Functions.DragQueryFile(hDrop, 0xFFFFFFFF, IntPtr.Zero, 0);
for (uint i = 0; i < filesCounter; ++i)
{
// Don't forget about \0 at the end
uint fileNameSize = Functions.DragQueryFile(hDrop, i, IntPtr.Zero, 0) + 1;
IntPtr str = Marshal.AllocHGlobal((int)fileNameSize);
Functions.DragQueryFile(hDrop, i, str, fileNameSize);
string dropString = Marshal.PtrToStringAuto(str);
OnFileDrop(dropString);
Marshal.FreeHGlobal(str);
}
Functions.DragFinish(hDrop);
}
#endregion #endregion
#region WindowProcedure #region WindowProcedure
@ -800,6 +822,10 @@ namespace OpenTK.Platform.Windows
HandleKillFocus(handle, message, wParam, lParam); HandleKillFocus(handle, message, wParam, lParam);
break; break;
case WindowMessage.DROPFILES:
HandleDropFiles(handle, message, wParam, lParam);
break;
#endregion #endregion
#region Creation / Destruction events #region Creation / Destruction events

View file

@ -52,6 +52,22 @@ namespace OpenTK.Platform.X11
#endregion #endregion
/// <summary>
/// X11 has some defined values they are defined with c's #define in X.h
/// </summary>
internal static class Consts
{
/// <summary>
/// Universal null resource or null atom. From header: #define None 0L
/// </summary>
public static readonly IntPtr None = IntPtr.Zero;
//
/// <summary>
/// Special time value. From header: #define CurrentTime 0L
/// </summary>
public static readonly IntPtr CurrentTime = IntPtr.Zero; //
}
#region internal static class API #region internal static class API
internal static class API internal static class API

View file

@ -68,7 +68,7 @@ namespace OpenTK.Platform.X11
const string ICON_NET_ATOM = "_NET_WM_ICON"; const string ICON_NET_ATOM = "_NET_WM_ICON";
// The Atom class from Mono might be useful to avoid calling XInternAtom by hand (somewhat error prone). // The Atom class from Mono might be useful to avoid calling XInternAtom by hand (somewhat error prone).
IntPtr _atom_wm_destroy; IntPtr _atom_wm_destroy;
IntPtr _atom_net_wm_state; IntPtr _atom_net_wm_state;
IntPtr _atom_net_wm_state_minimized; IntPtr _atom_net_wm_state_minimized;
@ -76,6 +76,24 @@ namespace OpenTK.Platform.X11
IntPtr _atom_net_wm_state_maximized_horizontal; IntPtr _atom_net_wm_state_maximized_horizontal;
IntPtr _atom_net_wm_state_maximized_vertical; IntPtr _atom_net_wm_state_maximized_vertical;
IntPtr xdndFormat;
long sourceXdndVersion;
IntPtr sourceHandler;
// Xdnd atoms
IntPtr _atom_xdnd_enter;
IntPtr _atom_xdnd_position;
IntPtr _atom_xdnd_status;
IntPtr _atom_xdnd_type_list;
IntPtr _atom_xdnd_action_copy;
IntPtr _atom_xdnd_drop;
IntPtr _atom_xdnd_finished;
IntPtr _atom_xdnd_selection;
IntPtr _atom_xdnd_leave;
IntPtr _atom_xdnd_primary;
#pragma warning disable 414 // assigned but never used #pragma warning disable 414 // assigned but never used
IntPtr _atom_net_wm_allowed_actions; IntPtr _atom_net_wm_allowed_actions;
IntPtr _atom_net_wm_action_resize; IntPtr _atom_net_wm_action_resize;
@ -264,6 +282,13 @@ namespace OpenTK.Platform.X11
xi2_version = XI2MouseKeyboard.XIVersion; xi2_version = XI2MouseKeyboard.XIVersion;
} }
// Alow window recive Xdnd Events
IntPtr xdndAware = Functions.XInternAtom(window.Display, "XdndAware", false);
IntPtr xdndProtocol = new IntPtr(5);
using (new XLock (window.Display)) {
Functions.XChangeProperty(this.window.Display, this.Handle, xdndAware, (IntPtr)AtomName.XA_ATOM, 32, PropertyMode.Replace, ref xdndProtocol, 1);
}
exists = true; exists = true;
} }
@ -306,6 +331,33 @@ namespace OpenTK.Platform.X11
#region Private Members #region Private Members
private void ReadProperty(IntPtr window, IntPtr property, IntPtr type, ref IntPtr data, ref IntPtr itemsCount)
{
int format;
IntPtr length = new IntPtr(int.MaxValue);
IntPtr actualType;
IntPtr bytesLeft;
Functions.XGetWindowProperty(this.window.Display, window, property, IntPtr.Zero,
length, false, type,
out actualType, out format, out itemsCount, out bytesLeft, ref data);
}
private string[] parseUriList(string rawString)
{
string[] separator = new string[] {"\r", "\n"};
string[] splitted = rawString.Split(separator, StringSplitOptions.RemoveEmptyEntries);
string[] fileNames = new string[splitted.Length];
for (int i = 0; i < splitted.Length; i++)
{
// Delete start of name - file://
fileNames[i] = splitted[i].Substring(7);
}
return fileNames;
}
#region private void RegisterAtoms() #region private void RegisterAtoms()
/// <summary> /// <summary>
@ -342,6 +394,19 @@ namespace OpenTK.Platform.X11
_atom_net_frame_extents = _atom_net_frame_extents =
Functions.XInternAtom(window.Display, "_NET_FRAME_EXTENTS", false); Functions.XInternAtom(window.Display, "_NET_FRAME_EXTENTS", false);
// Some Xdnd atoms
_atom_xdnd_enter = Functions.XInternAtom(window.Display, "XdndEnter", false);
_atom_xdnd_position = Functions.XInternAtom(window.Display, "XdndPosition", false);
_atom_xdnd_status = Functions.XInternAtom(window.Display, "XdndStatus", false);
_atom_xdnd_type_list = Functions.XInternAtom(window.Display, "XdndTypeList", false);
_atom_xdnd_action_copy = Functions.XInternAtom(window.Display, "XdndActionCopy", false);
_atom_xdnd_drop = Functions.XInternAtom(window.Display, "XdndDrop", false);
_atom_xdnd_finished = Functions.XInternAtom(window.Display, "XdndFinished", false);
_atom_xdnd_selection = Functions.XInternAtom(window.Display, "XdndSelection", false);
_atom_xdnd_leave = Functions.XInternAtom(window.Display, "XdndLeave", false);
// Selection atoms
_atom_xdnd_primary = Functions.XInternAtom(window.Display, "PRIMARY", false);
// string[] atom_names = new string[] // string[] atom_names = new string[]
// { // {
// //"WM_TITLE", // //"WM_TITLE",
@ -795,10 +860,11 @@ namespace OpenTK.Platform.X11
using (new XLock(window.Display)) using (new XLock(window.Display))
{ {
if (!Functions.XCheckWindowEvent(window.Display, window.Handle, window.EventMask, ref e) && if (!Functions.XCheckWindowEvent(window.Display, window.Handle, window.EventMask, ref e) &&
!Functions.XCheckTypedWindowEvent(window.Display, window.Handle, XEventName.ClientMessage, ref e)) !Functions.XCheckTypedWindowEvent(window.Display, window.Handle, XEventName.ClientMessage, ref e) &&
!Functions.XCheckTypedWindowEvent(window.Display, window.Handle, XEventName.SelectionNotify, ref e))
break; break;
} }
// Respond to the event e // Respond to the event e
switch (e.type) switch (e.type)
{ {
@ -839,7 +905,117 @@ namespace OpenTK.Platform.X11
break; break;
} }
} }
// For X11 drag and drop handling use https://freedesktop.org/wiki/Specifications/XDND/#index9h2
else if (e.ClientMessageEvent.message_type == _atom_xdnd_enter)
{
// Xdnd started
// ptr1 -> source window handler
// ptr2 bit 0 -> set to 1 if source support more than three data formats
// ptr2 third byte contains Xdnd version that source supports
bool useList = ((e.ClientMessageEvent.ptr2.ToInt64() & 1) == 1);
sourceHandler = e.ClientMessageEvent.ptr1;
sourceXdndVersion = e.ClientMessageEvent.ptr2.ToInt64() >> 24;
IntPtr formats = IntPtr.Zero;
int formatCount;
if (useList)
{
IntPtr count = IntPtr.Zero;
ReadProperty(sourceHandler, _atom_xdnd_type_list, (IntPtr)AtomName.XA_ATOM, ref formats, ref count);
formatCount = count.ToInt32();
}
else
{
formats = Marshal.AllocHGlobal(3 * IntPtr.Size);
Marshal.WriteIntPtr(formats, e.ClientMessageEvent.ptr3);
Marshal.WriteIntPtr(formats, IntPtr.Size * 2, e.ClientMessageEvent.ptr4);
Marshal.WriteIntPtr(formats, IntPtr.Size * 3, e.ClientMessageEvent.ptr5);
formatCount = 3;
}
xdndFormat = Consts.None;
for (int i = 0; i < formatCount && xdndFormat == Consts.None; ++i)
{
IntPtr tempAtom = Marshal.ReadIntPtr(formats, IntPtr.Size * i);
IntPtr atomName = Functions.XGetAtomName(this.window.Display, tempAtom);
string str = Marshal.PtrToStringAnsi(atomName);
if (str == "text/uri-list")
{
xdndFormat = tempAtom;
}
Functions.XFree(atomName);
}
if (useList && formats != IntPtr.Zero)
{
Functions.XFree(formats);
}
else
{
Marshal.FreeHGlobal(formats);
}
}
else if (e.ClientMessageEvent.message_type == _atom_xdnd_position)
{
XEvent reply = new XEvent ();
reply.ClientMessageEvent.type = XEventName.ClientMessage;
reply.ClientMessageEvent.display = this.window.Display;
reply.ClientMessageEvent.window = sourceHandler;
reply.ClientMessageEvent.message_type = _atom_xdnd_status;
reply.ClientMessageEvent.format = 32;
reply.ClientMessageEvent.ptr1 = this.window.Handle;
if (xdndFormat != Consts.None)
{
reply.ClientMessageEvent.ptr2 = (IntPtr)1;
}
else
{
reply.ClientMessageEvent.ptr2 = (IntPtr)0;
}
reply.ClientMessageEvent.ptr3 = (IntPtr)0;
reply.ClientMessageEvent.ptr4 = (IntPtr)0;
reply.ClientMessageEvent.ptr5 = _atom_xdnd_action_copy;
Functions.XSendEvent(this.window.Display, sourceHandler, false, (IntPtr)EventMask.NoEventMask, ref reply);
Functions.XFlush(this.window.Display);
}
else if (e.ClientMessageEvent.message_type == _atom_xdnd_drop)
{
if (xdndFormat == Consts.None)
{
XEvent reply = new XEvent ();
reply.ClientMessageEvent.type = XEventName.ClientMessage;
reply.ClientMessageEvent.display = this.window.Display;
reply.ClientMessageEvent.window = sourceHandler;
reply.ClientMessageEvent.message_type = _atom_xdnd_finished;
reply.ClientMessageEvent.format = 32;
reply.ClientMessageEvent.ptr1 = this.window.Handle;
reply.ClientMessageEvent.ptr2 = (IntPtr)0;
reply.ClientMessageEvent.ptr3 = Consts.None;
Functions.XSendEvent(this.window.Display, sourceHandler, false, (IntPtr)EventMask.NoEventMask, ref reply);
Functions.XFlush(this.window.Display);
}
else
{
if (sourceXdndVersion >= 1)
{
Functions.XConvertSelection(this.window.Display, _atom_xdnd_selection, xdndFormat, _atom_xdnd_primary, this.window.Handle, e.ClientMessageEvent.ptr3);
}
else
{
Functions.XConvertSelection(this.window.Display, _atom_xdnd_selection, xdndFormat, _atom_xdnd_primary, this.window.Handle, Consts.CurrentTime);
}
}
}
else if (e.ClientMessageEvent.message_type == _atom_xdnd_leave)
{
break;
}
break; break;
case XEventName.DestroyNotify: case XEventName.DestroyNotify:
@ -1006,6 +1182,37 @@ namespace OpenTK.Platform.X11
//} //}
break; break;
case XEventName.SelectionNotify:
if (e.SelectionEvent.property == _atom_xdnd_primary)
{
IntPtr data = IntPtr.Zero;
IntPtr count = IntPtr.Zero;
ReadProperty(e.SelectionEvent.requestor, e.SelectionEvent.property, e.SelectionEvent.target, ref data, ref count);
string rawString = Marshal.PtrToStringAnsi(data);
Functions.XFree(data);
string[] fileNames = parseUriList(rawString);
for (int i = 0; i < fileNames.Length; i++)
{
OnFileDrop(fileNames[i]);
}
XEvent reply = new XEvent ();
reply.ClientMessageEvent.type = XEventName.ClientMessage;
reply.ClientMessageEvent.display = this.window.Display;
reply.ClientMessageEvent.window = sourceHandler;
reply.ClientMessageEvent.message_type = _atom_xdnd_finished;
reply.ClientMessageEvent.format = 32;
reply.ClientMessageEvent.ptr1 = this.window.Handle;
reply.ClientMessageEvent.ptr2 = (IntPtr)1;
reply.ClientMessageEvent.ptr3 = _atom_xdnd_action_copy;
Functions.XSendEvent(this.window.Display, e.ClientMessageEvent.ptr1, false, (IntPtr)EventMask.NoEventMask, ref reply);
}
break;
default: default:
//Debug.WriteLine(String.Format("{0} event was not handled", e.type)); //Debug.WriteLine(String.Format("{0} event was not handled", e.type));
break; break;