Merge pull request #542 from VPeruS/drag-and-drop
Drag and drop support
This commit is contained in:
commit
09bd2d5d10
13 changed files with 440 additions and 10 deletions
|
@ -279,9 +279,9 @@ namespace OpenTK
|
|||
//event EventHandler<MouseEventArgs> MouseClick;
|
||||
//event EventHandler<MouseEventArgs> MouseDoubleClick;
|
||||
|
||||
//event EventHandler<DragEventArgs> DragDrop;
|
||||
//event EventHandler<DragEventArgs> DragEnter;
|
||||
//event EventHandler<DragEventArgs> DragOver;
|
||||
//event EventHandler<EventArgs> DragLeave;
|
||||
/// <summary>
|
||||
/// Occurs whenever file dropped on window.
|
||||
/// </summary>
|
||||
event EventHandler<OpenTK.Input.FileDropEventArgs> FileDrop;
|
||||
}
|
||||
}
|
||||
|
|
22
src/OpenTK/Input/FileDropEventArgs.cs
Normal file
22
src/OpenTK/Input/FileDropEventArgs.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -692,6 +692,11 @@ namespace OpenTK
|
|||
/// </summary>
|
||||
public event EventHandler<MouseWheelEventArgs> MouseWheel = delegate { };
|
||||
|
||||
/// <summary>
|
||||
/// Occurs whenever a file dropped on window;
|
||||
/// </summary>
|
||||
public event EventHandler<FileDropEventArgs> FileDrop = delegate { };
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
@ -964,6 +969,18 @@ namespace OpenTK
|
|||
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
|
||||
|
||||
/// <summary>
|
||||
|
@ -1125,6 +1142,8 @@ namespace OpenTK
|
|||
private void OnMouseMoveInternal(object sender, MouseMoveEventArgs e) { OnMouseMove(e); }
|
||||
private void OnMouseWheelInternal(object sender, MouseWheelEventArgs e) { OnMouseWheel(e); }
|
||||
|
||||
private void OnFileDropInternal(object sender, FileDropEventArgs e) { OnFileDrop(e); }
|
||||
|
||||
#region OnMoveInternal
|
||||
|
||||
private void OnMoveInternal(object sender, EventArgs e) { OnMove(e); }
|
||||
|
@ -1197,6 +1216,7 @@ namespace OpenTK
|
|||
implementation.VisibleChanged += OnVisibleChangedInternal;
|
||||
implementation.WindowBorderChanged += OnWindowBorderChangedInternal;
|
||||
implementation.WindowStateChanged += OnWindowStateChangedInternal;
|
||||
implementation.FileDrop += OnFileDropInternal;
|
||||
events = true;
|
||||
}
|
||||
else if (events)
|
||||
|
@ -1221,6 +1241,7 @@ namespace OpenTK
|
|||
implementation.VisibleChanged -= OnVisibleChangedInternal;
|
||||
implementation.WindowBorderChanged -= OnWindowBorderChangedInternal;
|
||||
implementation.WindowStateChanged -= OnWindowStateChangedInternal;
|
||||
implementation.FileDrop -= OnFileDropInternal;
|
||||
events = false;
|
||||
}
|
||||
else
|
||||
|
|
|
@ -772,8 +772,10 @@
|
|||
<Compile Include="Platform\Linux\Bindings\Evdev.cs" />
|
||||
<Compile Include="Platform\Linux\DefaultCursor.cs" />
|
||||
<Compile Include="Platform\Linux\Bindings\Kms.cs" />
|
||||
<Compile Include="Input\FileDropEventArgs.cs" />
|
||||
<None Include="OpenTK.csproj.paket.template" />
|
||||
<None Include="paket.references" />
|
||||
<Compile Include="Platform\MacOS\Cocoa\NSDragOperation.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
|
|
23
src/OpenTK/Platform/MacOS/Cocoa/NSDragOperation.cs
Normal file
23
src/OpenTK/Platform/MacOS/Cocoa/NSDragOperation.cs
Normal 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,
|
||||
}
|
||||
}
|
|
@ -116,6 +116,7 @@ namespace OpenTK.Platform.MacOS
|
|||
static readonly IntPtr NSImage;
|
||||
static readonly IntPtr NSBitmapImageRep;
|
||||
static readonly IntPtr NSDeviceRGBColorSpace = Cocoa.ToNSString("NSDeviceRGBColorSpace");
|
||||
static readonly IntPtr NSFilenamesPboardType;
|
||||
|
||||
static CocoaNativeWindow()
|
||||
{
|
||||
|
@ -125,6 +126,7 @@ namespace OpenTK.Platform.MacOS
|
|||
NSCursor = Class.Get("NSCursor");
|
||||
NSImage = Class.Get("NSImage");
|
||||
NSBitmapImageRep = Class.Get("NSBitmapImageRep");
|
||||
NSFilenamesPboardType = Cocoa.GetStringConstant(Cocoa.AppKitLibrary, "NSFilenamesPboardType");
|
||||
}
|
||||
|
||||
private CocoaWindowInfo windowInfo;
|
||||
|
@ -165,6 +167,8 @@ namespace OpenTK.Platform.MacOS
|
|||
CanBecomeKeyWindowHandler = CanBecomeKeyWindow;
|
||||
CanBecomeMainWindowHandler = CanBecomeMainWindow;
|
||||
ResetCursorRectsHandler = ResetCursorRects;
|
||||
PerformDragOperationHandler = PerformDragOperation;
|
||||
DraggingEnteredHandler = DraggingEntered;
|
||||
|
||||
// Create the window class
|
||||
int unique_id = Interlocked.Increment(ref UniqueId);
|
||||
|
@ -182,6 +186,9 @@ namespace OpenTK.Platform.MacOS
|
|||
Class.RegisterMethod(windowClass, AcceptsFirstResponderHandler, "acceptsFirstResponder", "b@:");
|
||||
Class.RegisterMethod(windowClass, CanBecomeKeyWindowHandler, "canBecomeKeyWindow", "b@:");
|
||||
Class.RegisterMethod(windowClass, CanBecomeMainWindowHandler, "canBecomeMainWindow", "b@:");
|
||||
Class.RegisterMethod(windowClass, DraggingEnteredHandler, "draggingEntered:", "@@:@");
|
||||
Class.RegisterMethod(windowClass, PerformDragOperationHandler, "performDragOperation:", "b@:@");
|
||||
|
||||
Class.RegisterClass(windowClass);
|
||||
|
||||
IntPtr viewClass = Class.AllocateClass("OpenTK_NSView" + unique_id, "NSView");
|
||||
|
@ -257,6 +264,16 @@ namespace OpenTK.Platform.MacOS
|
|||
|
||||
exists = true;
|
||||
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)]
|
||||
|
@ -287,6 +304,10 @@ namespace OpenTK.Platform.MacOS
|
|||
delegate bool CanBecomeMainWindowDelegate(IntPtr self, IntPtr cmd);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
|
||||
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;
|
||||
WindowDidResizeDelegate WindowDidResizeHandler;
|
||||
|
@ -302,6 +323,37 @@ namespace OpenTK.Platform.MacOS
|
|||
CanBecomeKeyWindowDelegate CanBecomeKeyWindowHandler;
|
||||
CanBecomeMainWindowDelegate CanBecomeMainWindowHandler;
|
||||
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)
|
||||
{
|
||||
|
|
|
@ -49,6 +49,8 @@ namespace OpenTK.Platform
|
|||
readonly KeyboardKeyEventArgs KeyUpArgs = new KeyboardKeyEventArgs();
|
||||
readonly KeyPressEventArgs KeyPressArgs = new KeyPressEventArgs((char)0);
|
||||
|
||||
readonly FileDropEventArgs FileDropArgs = new FileDropEventArgs();
|
||||
|
||||
// In order to simplify mouse event implementation,
|
||||
// we can store the current mouse state here.
|
||||
protected MouseState MouseState = new MouseState();
|
||||
|
@ -149,6 +151,13 @@ namespace OpenTK.Platform
|
|||
KeyUp(this, e);
|
||||
}
|
||||
|
||||
protected void OnFileDrop(string s)
|
||||
{
|
||||
var e = FileDropArgs;
|
||||
FileDropArgs.FileName = s;
|
||||
FileDrop(this, e);
|
||||
}
|
||||
|
||||
/// \internal
|
||||
/// <summary>
|
||||
/// Call this method to simulate KeyDown/KeyUp events
|
||||
|
@ -311,6 +320,7 @@ namespace OpenTK.Platform
|
|||
public event EventHandler<MouseButtonEventArgs> MouseUp = delegate { };
|
||||
public event EventHandler<MouseMoveEventArgs> MouseMove = delegate { };
|
||||
public event EventHandler<MouseWheelEventArgs> MouseWheel = delegate { };
|
||||
public event EventHandler<FileDropEventArgs> FileDrop = delegate { };
|
||||
|
||||
public abstract void Close();
|
||||
|
||||
|
|
|
@ -139,6 +139,10 @@ namespace OpenTK.Platform.SDL2
|
|||
[DllImport(lib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_FreeSurface", ExactSpelling = true)]
|
||||
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
|
||||
|
||||
[SuppressUnmanagedCodeSecurity]
|
||||
|
@ -1442,6 +1446,8 @@ namespace OpenTK.Platform.SDL2
|
|||
public ControllerButtonEvent ControllerButton;
|
||||
[FieldOffset(0)]
|
||||
public ControllerDeviceEvent ControllerDevice;
|
||||
[FieldOffset(0)]
|
||||
public DropEvent Drop;
|
||||
#if false
|
||||
[FieldOffset(0)]
|
||||
public QuitEvent quit;
|
||||
|
@ -1455,8 +1461,6 @@ namespace OpenTK.Platform.SDL2
|
|||
public MultiGestureEvent mgesture;
|
||||
[FieldOffset(0)]
|
||||
public DollarGestureEvent dgesture;
|
||||
[FieldOffset(0)]
|
||||
public DropEvent drop;
|
||||
#endif
|
||||
|
||||
// Ensure the structure is big enough
|
||||
|
@ -1755,6 +1759,17 @@ namespace OpenTK.Platform.SDL2
|
|||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -197,6 +197,15 @@ namespace OpenTK.Platform.SDL2
|
|||
}
|
||||
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:
|
||||
Debug.WriteLine("Sdl2 application quit");
|
||||
break;
|
||||
|
@ -293,6 +302,12 @@ namespace OpenTK.Platform.SDL2
|
|||
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)
|
||||
{
|
||||
switch (e.Event)
|
||||
|
|
|
@ -57,6 +57,8 @@ namespace OpenTK.Platform.Windows
|
|||
using HKEY = System.IntPtr;
|
||||
using PHKEY = System.IntPtr;
|
||||
|
||||
using HDROP = System.IntPtr;
|
||||
|
||||
using LRESULT = System.IntPtr;
|
||||
using LPVOID = System.IntPtr;
|
||||
using LPCTSTR = System.String;
|
||||
|
@ -134,6 +136,25 @@ namespace OpenTK.Platform.Windows
|
|||
{
|
||||
#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
|
||||
|
||||
// WINUSERAPI BOOL WINAPI SetWindowPos(__in HWND hWnd, __in_opt HWND hWndInsertAfter,
|
||||
|
|
|
@ -150,6 +150,7 @@ namespace OpenTK.Platform.Windows
|
|||
0, 0, ClientSize.Width, ClientSize.Height,
|
||||
title, options, device, window.Handle),
|
||||
window);
|
||||
Functions.DragAcceptFiles(window.Handle, true);
|
||||
|
||||
exists = true;
|
||||
}
|
||||
|
@ -680,6 +681,27 @@ namespace OpenTK.Platform.Windows
|
|||
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
|
||||
|
||||
#region WindowProcedure
|
||||
|
@ -800,6 +822,10 @@ namespace OpenTK.Platform.Windows
|
|||
HandleKillFocus(handle, message, wParam, lParam);
|
||||
break;
|
||||
|
||||
case WindowMessage.DROPFILES:
|
||||
HandleDropFiles(handle, message, wParam, lParam);
|
||||
break;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Creation / Destruction events
|
||||
|
|
|
@ -52,6 +52,22 @@ namespace OpenTK.Platform.X11
|
|||
|
||||
#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
|
||||
|
||||
internal static class API
|
||||
|
|
|
@ -68,7 +68,7 @@ namespace OpenTK.Platform.X11
|
|||
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).
|
||||
IntPtr _atom_wm_destroy;
|
||||
IntPtr _atom_wm_destroy;
|
||||
|
||||
IntPtr _atom_net_wm_state;
|
||||
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_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
|
||||
IntPtr _atom_net_wm_allowed_actions;
|
||||
IntPtr _atom_net_wm_action_resize;
|
||||
|
@ -264,6 +282,13 @@ namespace OpenTK.Platform.X11
|
|||
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;
|
||||
}
|
||||
|
||||
|
@ -306,6 +331,33 @@ namespace OpenTK.Platform.X11
|
|||
|
||||
#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()
|
||||
|
||||
/// <summary>
|
||||
|
@ -342,6 +394,19 @@ namespace OpenTK.Platform.X11
|
|||
_atom_net_frame_extents =
|
||||
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[]
|
||||
// {
|
||||
// //"WM_TITLE",
|
||||
|
@ -795,10 +860,11 @@ namespace OpenTK.Platform.X11
|
|||
using (new XLock(window.Display))
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
// Respond to the event e
|
||||
switch (e.type)
|
||||
{
|
||||
|
@ -839,7 +905,117 @@ namespace OpenTK.Platform.X11
|
|||
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;
|
||||
|
||||
case XEventName.DestroyNotify:
|
||||
|
@ -1006,6 +1182,37 @@ namespace OpenTK.Platform.X11
|
|||
//}
|
||||
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:
|
||||
//Debug.WriteLine(String.Format("{0} event was not handled", e.type));
|
||||
break;
|
||||
|
|
Loading…
Reference in a new issue