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> 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
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>
|
/// </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
|
||||||
|
|
|
@ -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>
|
||||||
|
|
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 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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue