Răsfoiți Sursa

WIP: Linux port
- Drag and drop functionality

Marko Pintera 8 ani în urmă
părinte
comite
8ddc569691

+ 2 - 0
Source/BansheeCore/CMakeSources.cmake

@@ -600,11 +600,13 @@ set(BS_BANSHEECORE_SRC_PLATFORM_WIN32
 set(BS_BANSHEECORE_INC_PLATFORM_UNIX
 	"Linux/BsLinuxPlatform.h"
 	"Linux/BsLinuxWindow.h"
+	"Linux/BsLinuxDragAndDrop.h"
 )
 
 set(BS_BANSHEECORE_SRC_PLATFORM_UNIX
 	"Linux/BsLinuxPlatform.cpp"
 	"Linux/BsLinuxWindow.cpp"
+	"Linux/BsLinuxDragAndDrop.cpp"
 )
 
 if(WIN32)

+ 561 - 0
Source/BansheeCore/Linux/BsLinuxDragAndDrop.cpp

@@ -0,0 +1,561 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include <X11/Xatom.h>
+#include <X11/Xlib.h>
+#include <String/BsUnicode.h>
+#include "Platform/BsPlatform.h"
+#include "RenderAPI/BsRenderWindow.h"
+#include "BsLinuxDragAndDrop.h"
+#include "BsLinuxWindow.h"
+#include "BsLinuxPlatform.h"
+
+namespace bs
+{
+	::Display* LinuxDragAndDrop::sXDisplay = nullptr;
+	bool LinuxDragAndDrop::sDragActive = false;
+	Vector<OSDropTarget*> LinuxDragAndDrop::sTargets;
+	Mutex LinuxDragAndDrop::sMutex;
+	INT32 LinuxDragAndDrop::sDNDVersion = 0;
+	::Window LinuxDragAndDrop::sDNDSource = None;
+	Vector2I LinuxDragAndDrop::sDragPosition;
+	Vector<DragAndDropOp> LinuxDragAndDrop::sQueuedOperations;
+	Vector<OSDropTarget*> LinuxDragAndDrop::sTargetsToRegister;
+	Vector<OSDropTarget*> LinuxDragAndDrop::sTargetsToUnregister;
+
+	Atom LinuxDragAndDrop::sXdndAware;
+	Atom LinuxDragAndDrop::sXdndSelection;
+	Atom LinuxDragAndDrop::sXdndEnter;
+	Atom LinuxDragAndDrop::sXdndLeave;
+	Atom LinuxDragAndDrop::sXdndPosition;
+	Atom LinuxDragAndDrop::sXdndStatus;
+	Atom LinuxDragAndDrop::sXdndDrop;
+	Atom LinuxDragAndDrop::sXdndFinished;
+	Atom LinuxDragAndDrop::sXdndActionCopy;
+	Atom LinuxDragAndDrop::sXdndTypeList;
+
+	struct X11Property
+	{
+		UINT8* data;
+		INT32 format;
+		UINT32 count;
+		Atom type;
+	};
+
+	// Results must be freed using XFree
+	X11Property readX11Property(::Display *display, ::Window window, Atom type)
+	{
+		X11Property output;
+
+		unsigned long bytesLeft;
+		int bytesToFetch = 0;
+
+		do
+		{
+			if(output.data != nullptr)
+				XFree(output.data);
+
+			XGetWindowProperty(display, window, type, 0, bytesToFetch, False, AnyPropertyType,
+					&output.type, &output.format, (unsigned long*)&output.count, &bytesLeft, &output.data);
+			bytesToFetch += bytesLeft;
+
+		} while(bytesLeft != 0);
+
+		return output;
+	}
+
+	/**
+	 * Decodes percent (%) encoded characters in an URI to actual characters. Characters are decoded into the input string.
+	 */
+	void decodeURI(char* uri)
+	{
+		if(uri == nullptr)
+			return;
+
+		UINT32 length = (UINT32)strlen(uri);
+		UINT8 decodedChar = '\0';
+		UINT32 writeIdx = 0;
+		UINT32 decodeState = 0; // 0 - Not decoding, 1 - found a % char, 2- found a % and following char
+
+		for(UINT32 i = 0; i < length; i++)
+		{
+			// Not currently decoding, check for % or write as-is
+			if(decodeState == 0)
+			{
+				// Potentially an encoded char, start decode
+				if(uri[i] == '%')
+				{
+					decodedChar = '\0';
+					decodeState = 1;
+				}
+				else // Normal char, write as-is
+				{
+					uri[writeIdx] = uri[i];
+					writeIdx++;
+				}
+			}
+			else // Currently decoding, check if following chars are valid
+			{
+				char isa = uri[i] >= 'a' && uri[i] <= 'f';
+				char isA = uri[i] >= 'A' && uri[i] <= 'F';
+				char isn = uri[i] >= '0' && uri[i] <= '9';
+
+				bool isHex = isa || isA || isn;
+				if(!isHex)
+				{
+					// If not a hex, this can't be an encoded character. Write the last "decodeState" characters as normal
+					for(UINT32 j = decodeState; j > 0; j--)
+					{
+						uri[writeIdx] = uri[i - j];
+						writeIdx++;
+					}
+
+					decodeState = 0;
+				}
+				else
+				{
+					// Decode the hex character into a number
+					char offset = '\0';
+					if(isn)
+						offset = 0 - '0';
+					else if(isa)
+						offset = 10 - 'a';
+					else if(isA)
+						offset = 10 - 'A';
+
+					decodedChar |= (uri[i] + offset) << ((2 - decodeState) * 4);
+
+					// Check if done decoding and write
+					if(decodeState == 2)
+					{
+						uri[writeIdx] = decodedChar;
+						writeIdx++;
+						decodeState = 0;
+					}
+					else // decodeState == 1
+						decodeState = 2;
+				}
+			}
+		}
+
+		uri[writeIdx] = '\0';
+	}
+
+	char* convertURIToLocalPath(char* uri)
+	{
+		if (memcmp(uri, "file:/", 6) == 0)
+			uri += 6;
+		else if (strstr(uri, ":/") != nullptr)
+			return nullptr;
+
+		bool isLocal = uri[0] != '/' || (uri[0] != '\0' && uri[1] == '/');
+
+		// Ignore hostname
+		if (!isLocal && uri[0] == '/' && uri[2] != '/')
+		{
+			char* hostnameEnd = strchr(uri+1, '/');
+			if (hostnameEnd != nullptr)
+			{
+				char hostname[257];
+				if (gethostname(hostname, 255) == 0)
+				{
+					hostname[256] = '\0';
+					if (memcmp(uri+1, hostname, hostnameEnd - (uri+1)) == 0)
+					{
+						uri = hostnameEnd + 1;
+						isLocal = true;
+					}
+				}
+			}
+		}
+
+		if (isLocal)
+		{
+			decodeURI(uri);
+			if (uri[1] == '/')
+				uri++;
+			else
+				uri--;
+
+			return uri;
+		}
+
+		return nullptr;
+	}
+
+	void LinuxDragAndDrop::startUp(::Display* xDisplay)
+	{
+#define INIT_ATOM(name)		s##name = XInternAtom(xDisplay, #name, False);
+
+		INIT_ATOM(XdndAware)
+		INIT_ATOM(XdndSelection)
+		INIT_ATOM(XdndEnter)
+		INIT_ATOM(XdndLeave)
+		INIT_ATOM(XdndPosition)
+		INIT_ATOM(XdndStatus)
+		INIT_ATOM(XdndDrop)
+		INIT_ATOM(XdndFinished)
+		INIT_ATOM(XdndActionCopy)
+		INIT_ATOM(XdndTypeList)
+		INIT_ATOM(PRIMARY)
+
+#undef INIT_ATOM
+	}
+
+	void LinuxDragAndDrop::shutDown()
+	{
+		// Do nothing
+	}
+
+	void LinuxDragAndDrop::makeDNDAware(::Window xWindow)
+	{
+		UINT32 dndVersion = 5;
+		XChangeProperty(sXDisplay, xWindow, sXdndAware, XA_ATOM, 32, PropModeReplace, (unsigned char*)dndVersion, 1);
+	}
+
+	OSDropTarget& LinuxDragAndDrop::createDropTarget(const RenderWindow* window, INT32 x, INT32 y, UINT32 width,
+		UINT32 height)
+	{
+		Lock lock(sMutex);
+		OSDropTarget* newDropTarget = bs_new<OSDropTarget>(window, x, y, width, height);
+		sTargetsToRegister.push_back(newDropTarget);
+
+		return *newDropTarget;
+	}
+
+	void LinuxDragAndDrop::destroyDropTarget(OSDropTarget& target)
+	{
+		Lock lock(sMutex);
+		sTargetsToUnregister.push_back(&target);
+	}
+
+	bool LinuxDragAndDrop::handleClientMessage(XClientMessageEvent& event)
+	{
+		// First handle any queued registration/unregistration
+		{
+			Lock lock(sMutex);
+
+			for(auto& target : sTargetsToRegister)
+				sTargets.push_back(target);
+
+			for(auto& target : sTargetsToUnregister)
+			{
+				// Remove any operations queued for this target
+				for(auto iter = sQueuedOperations.begin(); iter !=sQueuedOperations.end();)
+				{
+					if(iter->target == target)
+						iter = sQueuedOperations.erase(iter);
+					else
+						++iter;
+				}
+
+				auto iterFind = std::find(sTargets.begin(), sTargets.end(), target);
+				if (iterFind != sTargets.end())
+					sTargets.erase(iterFind);
+
+				bs_delete(target);
+			}
+		}
+
+		// Source window notifies us a drag has just entered our window area
+		if(event.message_type == sXdndEnter)
+		{
+			sDNDSource = (::Window)event.data.l[0];
+			bool isList = (event.data.l[1] & 1) != 0;
+			sDNDVersion = (INT32)(event.data.l[1] >> 24);
+
+			// Get a list of properties are determine if there are any relevant ones
+			Atom* propertyList = nullptr;
+			UINT32 numProperties = 0;
+
+			// If more than 3 properties we need to read the list property to get them all
+			if(isList)
+			{
+				X11Property property = readX11Property(sXDisplay, sDNDSource, sXdndTypeList);
+
+				propertyList = (Atom*)property.data;
+				numProperties = property.count;
+			}
+			else
+			{
+				propertyList = bs_stack_alloc<Atom>(3);
+
+				for(int i = 0; i < 3; i++)
+				{
+					if(event.data.l[2 + i])
+					{
+						propertyList[i] = (Atom)event.data.l[2 + i];
+						numProperties++;
+					}
+				}
+			}
+
+			// Scan the list for URI list (file list), which is the only one we support (currently)
+			bool foundSupportedType = false;
+			for (UINT32 i = 0; i < numProperties; ++i)
+			{
+				char* name = XGetAtomName(sXDisplay, propertyList[i]);
+				if(strcmp(name, "text/uri-list") == 0)
+				{
+					sDNDType = propertyList[i];
+
+					XFree(name);
+					foundSupportedType = true;
+					break;
+				}
+
+				XFree(name);
+			}
+
+			// Free the property list
+			if(isList)
+				XFree(propertyList);
+			else
+				bs_stack_free(propertyList);
+
+			sDragActive = foundSupportedType;
+		}
+		// Cursor moved while drag is active (also includes the initial cursor activity when drag entered)
+		else if(event.message_type == sXdndPosition)
+		{
+			::Window source = (::Window)event.data.l[0];
+
+			sDragPosition.x = (INT32)((event.data.l[2] >> 16) & 0xFFFF);
+			sDragPosition.y = (INT32)((event.data.l[2]) & 0xFFFF);
+
+			// Respond with a status message, we either accept or reject the dnd
+			XClientMessageEvent response;
+			bs_zero_out(response);
+
+			response.type = ClientMessage;
+			response.display = event.display;
+			response.window = source;
+			response.message_type = sXdndStatus;
+			response.format = 32;
+			response.data.l[0] = event.window;
+			response.data.l[1] = 0; // Reject drop by default
+			response.data.l[2] = 0; // Empty rectangle
+			response.data.l[3] = 0; // Empty rectangle
+			response.data.l[4] = sXdndActionCopy;
+
+			if(sDragActive)
+			{
+				for(auto& dropTarget : sTargets)
+				{
+					LinuxWindow* linuxWindow;
+					dropTarget->_getOwnerWindow()->getCustomAttribute("WINDOW", &linuxWindow);
+					::Window xWindow = linuxWindow->_getXWindow();
+
+					if(xWindow == event.window)
+					{
+						Vector2I windowPos = linuxWindow->screenToWindowPos(sDragPosition);
+						if(dropTarget->_isInside(windowPos))
+						{
+							// Accept drop
+							response.data.l[1] = 1;
+
+							if(dropTarget->_isActive())
+							{
+								Lock lock(sMutex);
+								sQueuedOperations.push_back(DragAndDropOp(DragAndDropOpType::DragOver, dropTarget,
+									windowPos));
+							}
+							else
+							{
+								Lock lock(sMutex);
+								sQueuedOperations.push_back(DragAndDropOp(DragAndDropOpType::Enter, dropTarget,
+										windowPos));
+							}
+
+							dropTarget->_setActive(true);
+						}
+						else
+						{
+							// Cursor left previously active target's area
+							if(dropTarget->_isActive())
+							{
+								{
+									Lock lock(sMutex);
+									sQueuedOperations.push_back(DragAndDropOp(DragAndDropOpType::Leave, dropTarget));
+								}
+
+								dropTarget->_setActive(false);
+							}
+						}
+					}
+				}
+			}
+
+			XSendEvent(sXDisplay, source, False, NoEventMask, (XEvent*)&response);
+			XFlush(sXDisplay);
+		}
+		// Cursor left the target window, or the drop was rejected
+		else if(event.message_type == sXdndLeave)
+		{
+			for(auto& dropTarget : sTargets)
+			{
+				if(dropTarget->_isActive())
+				{
+					{
+						Lock lock(sMutex);
+						sQueuedOperations.push_back(DragAndDropOp(DragAndDropOpType::Leave, dropTarget));
+					}
+
+					dropTarget->_setActive(false);
+				}
+			}
+
+			sDragActive = false;
+		}
+		else if(event.message_type == sXdndDrop)
+		{
+			::Window source = (::Window)event.data.l[0];
+			bool dropAccepted = false;
+
+			if(sDragActive)
+			{
+				for (auto& dropTarget : sTargets)
+				{
+					if (dropTarget->_isActive())
+						dropAccepted = true;
+				}
+			}
+
+			if(dropAccepted)
+			{
+				Time timestamp;
+				if(sDNDVersion >= 1)
+					timestamp = (Time)event.data.l[2];
+				else
+					timestamp = CurrentTime;
+
+				XConvertSelection(sXDisplay, sXdndSelection, sDNDType, sPRIMARY, LinuxPlatform::getMainXWindow(), timestamp);
+
+				// Now we wait for SelectionNotify
+			}
+			else
+			{
+				// Respond with a status message that we reject the drop
+				XClientMessageEvent response;
+				bs_zero_out(response);
+
+				response.type = ClientMessage;
+				response.display = event.display;
+				response.window = source;
+				response.message_type = sXdndFinished;
+				response.format = 32;
+				response.data.l[0] = LinuxPlatform::getMainXWindow();
+				response.data.l[1] = 0;
+				response.data.l[2] = 0;
+				response.data.l[3] = None;
+				response.data.l[4] = None;
+
+				XSendEvent(sXDisplay, source, False, NoEventMask, (XEvent*)&response);
+				XFlush(sXDisplay);
+
+				sDragActive = false;
+			}
+		}
+		else
+			return false;
+
+		return true;
+	}
+
+	bool LinuxDragAndDrop::handleSelectionNotify(XSelectionEvent& event)
+	{
+		if(event.target != sDNDType)
+			return false;
+
+		if(!sDragActive)
+			return true;
+
+		// Read data
+		X11Property property = readX11Property(sXDisplay, LinuxPlatform::getMainXWindow(), sXdndTypeList);
+		if(property.format == 8)
+		{
+			// Assuming this is a file list, since we rejected any other drop type
+			Vector<WString> filePaths;
+
+			char* token = strtok((char*)property.data, "\r\n");
+			while(token != nullptr)
+			{
+				char* filePath = convertURIToLocalPath(token);
+				if(filePath != nullptr)
+					filePaths.push_back(UTF8::toWide(filePath));
+
+				token = strtok(nullptr, "\r\n");
+			}
+
+			for(auto& target : sTargets)
+			{
+				if(!target->_isActive())
+					continue;
+
+				LinuxWindow* linuxWindow;
+				target->_getOwnerWindow()->getCustomAttribute("WINDOW", &linuxWindow);
+
+				Vector2I windowPos = linuxWindow->screenToWindowPos(sDragPosition);
+
+				Lock lock(sMutex);
+				sQueuedOperations.push_back(DragAndDropOp(DragAndDropOpType::Drop, target, windowPos, filePaths));
+
+				target->_setActive(false);
+			}
+		}
+
+		XFree(property.data);
+
+		// Respond with a status message that we accepted the drop
+		XClientMessageEvent response;
+		bs_zero_out(response);
+
+		response.type = ClientMessage;
+		response.display = event.display;
+		response.window = sDNDSource;
+		response.message_type = sXdndFinished;
+		response.format = 32;
+		response.data.l[0] = LinuxPlatform::getMainXWindow();
+		response.data.l[1] = 1;
+		response.data.l[2] = sXdndActionCopy;
+		response.data.l[3] = None;
+		response.data.l[4] = None;
+
+		XSendEvent(sXDisplay, sDNDSource, False, NoEventMask, (XEvent*)&response);
+		XFlush(sXDisplay);
+
+		sDragActive = false;
+
+		return true;
+	}
+
+	void LinuxDragAndDrop::update()
+	{
+		Vector<DragAndDropOp> operations;
+
+		{
+			Lock lock(sMutex);
+			std::swap(operations, sQueuedOperations);
+		}
+
+		for(auto& op : operations)
+		{
+			switch(op.type)
+			{
+			case DragAndDropOpType::Enter:
+				op.target->onEnter(op.position.x, op.position.y);
+				break;
+			case DragAndDropOpType::DragOver:
+				op.target->onDragOver(op.position.x, op.position.y);
+				break;
+			case DragAndDropOpType::Drop:
+				op.target->_setFileList(op.fileList);
+				op.target->onDrop(op.position.x, op.position.y);
+				break;
+			case DragAndDropOpType::Leave:
+				op.target->_clear();
+				op.target->onLeave();
+				break;
+			}
+		}
+	}
+}

+ 150 - 0
Source/BansheeCore/Linux/BsLinuxDragAndDrop.h

@@ -0,0 +1,150 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsCorePrerequisites.h"
+#include <X11/X.h>
+#include <X11/Xlib.h>
+
+namespace bs
+{
+	/** Handles X11 drag and drop functionality. */
+	class LinuxDragAndDrop
+	{
+		/** Possible states of the DND manager. */
+		enum class State
+		{
+			Inactive,
+			Entered,
+			Active
+		};
+
+		/** Type of drag and drop operation. */
+		enum class DragAndDropOpType
+		{
+			Enter,
+			DragOver,
+			Drop,
+			Leave
+		};
+
+		/**	Structure describing a drag and drop operation. */
+		struct DragAndDropOp
+		{
+			DragAndDropOp(DragAndDropOpType type, OSDropTarget* target)
+					:type(type), target(target)
+			{ }
+
+			DragAndDropOp(DragAndDropOpType type, OSDropTarget* target, const Vector2I& pos)
+				:type(type), position(pos), target(target)
+			{ }
+
+			DragAndDropOp(DragAndDropOpType type, OSDropTarget* target, const Vector2I& pos,
+				const Vector<WString>& fileList)
+				:type(type), position(pos), target(target), fileList(fileList)
+			{ }
+
+			DragAndDropOpType type;
+			OSDropTarget* target;
+			Vector2I position;
+			Vector<WString> fileList;
+		};
+
+	public:
+		/**
+		 * Initializes the drag and drop system. Must be called before any other drag and drop methods are called.
+		 *
+		 * @note	Core thread only.
+		 */
+		static void startUp(::Display* xDisplay);
+
+		/**
+		 * Shuts down the drag and drop system. Should be called after no more calls to the system are expected.
+		 *
+		 * @note	Core thread only.
+		 */
+		static void shutDown();
+
+		/**
+		 * Triggers any drag and drop events.
+		 *
+		 * @note 	Sim thread only.
+		 */
+		static void update();
+
+		/**
+		 * Marks an X11 window as drag and drop aware (being able to accept and send drag and drop events).
+		 *
+		 * @note	Core thread only.
+		 */
+		static void makeDNDAware(::Window xWindow);
+
+		/**
+		 * Creates a new drop target. Any further events processed will take this target into account, trigger its event
+		 * and populate its data if a drop occurs.
+		 *
+		 * @note 	Thread safe.
+		 */
+		static OSDropTarget& createDropTarget(const RenderWindow* window, INT32 x, INT32 y, UINT32 width, UINT32 height);
+
+		/**
+		 * Destroys a drop target.
+		 *
+		 * @note	Thread safe.
+		 */
+		static void destroyDropTarget(OSDropTarget& target);
+
+		/**
+		 * Processes X11 ClientMessage event and handles any messages relating to drag and drop. Returns true if a message
+		 * was handled, or false if it needs to be handled by the caller.
+		 *
+		 * @note 	Core thread only.
+		 */
+		static bool handleClientMessage(XClientMessageEvent& event);
+
+		/**
+		 * Processes X11 SelectionNotify event and handles it if it relates to drag and drop. Returns true if the event was
+		 * handled, or false otherwise.
+		 *
+		 * @note 	Core thread only.
+		 */
+		static bool handleSelectionNotify(XSelectionEvent& event);
+
+	private:
+		static ::Display* sXDisplay;
+		static bool sDragActive;
+		static Vector<OSDropTarget*> sTargets;
+		static Mutex sMutex;
+		static INT32 sDNDVersion;
+		static Atom sDNDType;
+		static ::Window sDNDSource;
+		static Vector2I sDragPosition;
+		static Vector<DragAndDropOp> sQueuedOperations;
+		static Vector<OSDropTarget*> sTargetsToRegister;
+		static Vector<OSDropTarget*> sTargetsToUnregister;
+
+		// Awareness
+		static Atom sXdndAware;
+
+		// Selection handling
+		static Atom sXdndSelection;
+
+		// Client messages
+		static Atom sXdndEnter;
+		static Atom sXdndLeave;
+		static Atom sXdndPosition;
+		static Atom sXdndStatus;
+		static Atom sXdndDrop;
+		static Atom sXdndFinished;
+
+		// Actions
+		static Atom sXdndActionCopy;
+
+		// Type list
+		static Atom sXdndTypeList;
+
+		// Other
+		static Atom sPRIMARY;
+	};
+}
+

+ 17 - 6
Source/BansheeCore/Linux/BsLinuxPlatform.cpp

@@ -6,6 +6,7 @@
 #include "Linux/BsLinuxPlatform.h"
 #include "Linux/BsLinuxWindow.h"
 #include "RenderAPI/BsRenderWindow.h"
+#include "BsLinuxDragAndDrop.h"
 #include <X11/Xatom.h>
 #include <X11/Xcursor/Xcursor.h>
 
@@ -374,16 +375,14 @@ namespace bs
 		usleep(duration * 1000);
 	}
 
-	OSDropTarget& Platform::createDropTarget(const RenderWindow* window, int x, int y, unsigned int width, unsigned int height)
+	OSDropTarget& Platform::createDropTarget(const RenderWindow* window, INT32 x, INT32 y, UINT32 width, UINT32 height)
 	{
-		Lock lock(mData->lock);
-		// TODOPORT
+		return LinuxDragAndDrop::createDropTarget(window, x, y, width, height);
 	}
 
 	void Platform::destroyDropTarget(OSDropTarget& target)
 	{
-		Lock lock(mData->lock);
-		// TODOPORT
+		LinuxDragAndDrop::destroyDropTarget(target);
 	}
 
 	void Platform::copyToClipboard(const WString& string)
@@ -468,6 +467,9 @@ namespace bs
 			{
 			case ClientMessage:
 			{
+				if(LinuxDragAndDrop::handleClientMessage(event.xclient))
+					break;
+
 				if(event.xclient.data.l[0] == mData->atomDeleteWindow)
 					XDestroyWindow(mData->xDisplay, event.xclient.window);
 			}
@@ -722,6 +724,9 @@ namespace bs
 				// Update input context focus
 				XUnsetICFocus(mData->IC);
 				break;
+			case SelectionNotify:
+				LinuxDragAndDrop::handleSelectionNotify(event.xselection);
+				break;
 			case SelectionRequest:
 			{
 				// Send the data saved by the last clipboard copy operation
@@ -792,6 +797,9 @@ namespace bs
 
 		mData->atomDeleteWindow = XInternAtom(mData->xDisplay, "WM_DELETE_WINDOW", False);
 
+		// Drag and drop
+		LinuxDragAndDrop::startUp(mData->xDisplay);
+
 		// Create empty cursor
 		char data[1];
 		memset(data, 0, sizeof(data));
@@ -807,7 +815,7 @@ namespace bs
 
 	void Platform::_update()
 	{
-		// Do nothing
+		LinuxDragAndDrop::update();
 	}
 
 	void Platform::_coreUpdate()
@@ -823,6 +831,9 @@ namespace bs
 		XFreeCursor(mData->xDisplay, mData->emptyCursor);
 		mData->emptyCursor = None;
 
+		// Shutdown drag and drop
+		LinuxDragAndDrop::shutDown();
+
 		if(mData->IC)
 		{
 			XDestroyIC(mData->IC);

+ 5 - 2
Source/BansheeCore/Linux/BsLinuxWindow.cpp

@@ -2,7 +2,7 @@
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 #include "BsLinuxWindow.h"
 #include "BsLinuxPlatform.h"
-#include "Math/BsRect2I.h"
+#include "BsLinuxDragAndDrop.h"
 
 #include <X11/Xlib.h>
 #include <X11/Xutil.h>
@@ -116,10 +116,13 @@ namespace bs
 		Atom atomDeleteWindow = XInternAtom(display, "WM_DELETE_WINDOW", False);
 		XSetWMProtocols(display, m->xWindow, &atomDeleteWindow, 1);
 
+		// Enable drag and drop
+		LinuxDragAndDrop::makeDNDAware(m->xWindow);
+
 		// Set background image if assigned
 		if(desc.background)
 		{
-			Pixmap pixmap = LinuxPlatform::createPixmap(desc.background);
+			Pixmap pixmap = LinuxPlatform::createPixmap(*desc.background);
 			XSetWindowBackgroundPixmap(display, m->xWindow, pixmap);
 			XFreePixmap(display, pixmap);
 		}

+ 7 - 7
Source/BansheeCore/Platform/BsPlatform.h

@@ -86,6 +86,9 @@ namespace bs
 	class BS_CORE_EXPORT OSDropTarget
 	{
 	public:
+		OSDropTarget(const RenderWindow* ownerWindow, INT32 x, INT32 y, UINT32 width, UINT32 height);
+		~OSDropTarget();
+
 		/**
 		 * Triggered when a pointer is being dragged over the drop area. Provides window coordinates of the pointer position.
 		 */
@@ -130,15 +133,12 @@ namespace bs
 
 		/** Returns true if the drop target is active. */
 		bool _isActive() const { return mActive; }
-	private:
-		friend class Platform;
-
-		OSDropTarget(const RenderWindow* ownerWindow, INT32 x, INT32 y, UINT32 width, UINT32 height);
-		~OSDropTarget();
 
 		/**	Returns a render window this drop target is attached to. */
-		const RenderWindow* getOwnerWindow() const { return mOwnerWindow; }
+		const RenderWindow* _getOwnerWindow() const { return mOwnerWindow; }
 	private:
+		friend class Platform;
+
 		INT32 mX, mY;
 		UINT32 mWidth, mHeight;
 		bool mActive;
@@ -325,7 +325,7 @@ namespace bs
 		 * @return				OSDropTarget that you will use to receive all drop data. When no longer needed make sure 
 		 *						to destroy it with destroyDropTarget().
 		 */
-		static OSDropTarget& createDropTarget(const RenderWindow* window, int x, int y, unsigned int width, unsigned int height);
+		static OSDropTarget& createDropTarget(const RenderWindow* window, INT32 x, INT32 y, UINT32 width, UINT32 height);
 
 		/** Destroys a drop target previously created with createDropTarget. */
 		static void destroyDropTarget(OSDropTarget& target);

+ 2 - 2
Source/BansheeCore/Win32/BsWin32Platform.cpp

@@ -251,7 +251,7 @@ namespace bs
 		Sleep((DWORD)duration);
 	}
 
-	OSDropTarget& Platform::createDropTarget(const RenderWindow* window, int x, int y, unsigned int width, unsigned int height)
+	OSDropTarget& Platform::createDropTarget(const RenderWindow* window, INT32 x, INT32 y, UINT32 width, UINT32 height)
 	{
 		Win32DropTarget* win32DropTarget = nullptr;
 		auto iterFind = mData->mDropTargets.dropTargetsPerWindow.find(window);
@@ -279,7 +279,7 @@ namespace bs
 
 	void Platform::destroyDropTarget(OSDropTarget& target)
 	{
-		auto iterFind = mData->mDropTargets.dropTargetsPerWindow.find(target.getOwnerWindow());
+		auto iterFind = mData->mDropTargets.dropTargetsPerWindow.find(target._getOwnerWindow());
 		if (iterFind == mData->mDropTargets.dropTargetsPerWindow.end())
 		{
 			LOGWRN("Attempting to destroy a drop target but cannot find its parent window.");

+ 3 - 2
Source/BansheeEngine/GUI/BsGUIInputBox.cpp

@@ -15,6 +15,7 @@
 #include "GUI/BsGUIContextMenu.h"
 #include "GUI/BsGUIHelper.h"
 #include "Utility/BsTime.h"
+#include "Platform/BsPlatform.h"
 
 namespace bs
 {
@@ -1232,7 +1233,7 @@ namespace bs
 
 	void GUIInputBox::copyText()
 	{
-		PlatformUtility::copyToClipboard(getSelectedText());
+		Platform::copyToClipboard(getSelectedText());
 	}
 
 	void GUIInputBox::pasteText()
@@ -1240,7 +1241,7 @@ namespace bs
 		if (mSelectionShown)
 			deleteSelectedText(true);
 
-		WString textInClipboard = PlatformUtility::copyFromClipboard();
+		WString textInClipboard = Platform::copyFromClipboard();
 		UINT32 charIdx = gGUIManager().getInputCaretTool()->getCharIdxAtCaretPos();
 
 		bool filterOkay = true;

+ 1 - 1
Source/BansheeEngine/Platform/BsSplashScreen.cpp

@@ -1,6 +1,5 @@
 //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#include <X11/Xutil.h>
 #include "Platform/BsSplashScreen.h"
 #include "Resources/BsBuiltinResources.h"
 #include "Utility/BsTimer.h"
@@ -78,6 +77,7 @@ namespace bs
 #elif BS_PLATFORM == BS_PLATFORM_LINUX
 #include "Linux/BsLinuxPlatform.h"
 #include "Linux/BsLinuxWindow.h"
+#include "X11/Xutil.h"
 
 namespace bs
 {

+ 2 - 2
Source/BansheeUtility/String/BsUnicode.cpp

@@ -217,7 +217,7 @@ namespace bs
 
 	}
 
-	char32_t ANSIToUTF32(char input, const std::locale& locale = std::locale())
+	char32_t ANSIToUTF32(char input, const std::locale& locale = std::locale(""))
 	{
 		const std::ctype<wchar_t>& facet = std::use_facet<std::ctype<wchar_t>>(locale);
 
@@ -245,7 +245,7 @@ namespace bs
 			return UTF32To16(input, output, maxElems, invalidChar);
 	}
 
-	char UTF32ToANSI(char32_t input, char invalidChar = 0, const std::locale& locale = std::locale())
+	char UTF32ToANSI(char32_t input, char invalidChar = 0, const std::locale& locale = std::locale(""))
 	{
 		const std::ctype<wchar_t>& facet = std::use_facet<std::ctype<wchar_t>>(locale);
 

+ 2 - 2
Source/BansheeUtility/String/BsUnicode.h

@@ -22,7 +22,7 @@ namespace bs
 		 * @param[in]	locale	Locale that determines how are the ANSI characters interpreted.
 		 * @return				UTF-8 encoded string.
 		 */
-		static String fromANSI(const String& input, const std::locale& locale = std::locale());
+		static String fromANSI(const String& input, const std::locale& locale = std::locale(""));
 
 		/**
 		 * Converts from an UTF-8 encoding into ANSI encoding in the specified locale.
@@ -33,7 +33,7 @@ namespace bs
 		 *								the selected ANSI code page.
 		 * @return						ANSI encoded string in the specified locale.
 		 */
-		static String toANSI(const String& input, const std::locale& locale = std::locale(), char invalidChar = 0);
+		static String toANSI(const String& input, const std::locale& locale = std::locale(""), char invalidChar = 0);
 
 		/**
 		 * Converts from a system-specific wide character encoding into UTF-8.