Add drag&drop support for Linux
This commit is contained in:
@@ -20,6 +20,7 @@
|
||||
#include "Engine/Platform/MessageBox.h"
|
||||
#include "Engine/Platform/WindowsManager.h"
|
||||
#include "Engine/Platform/Clipboard.h"
|
||||
#include "Engine/Platform/IGuiData.h"
|
||||
#include "Engine/Utilities/StringConverter.h"
|
||||
#include "Engine/Threading/Threading.h"
|
||||
#include "Engine/Engine/Engine.h"
|
||||
@@ -58,6 +59,15 @@ X11::Display* xDisplay = nullptr;
|
||||
X11::XIM IM = nullptr;
|
||||
X11::XIC IC = nullptr;
|
||||
X11::Atom xAtomDeleteWindow;
|
||||
X11::Atom xAtomXdndEnter;
|
||||
X11::Atom xAtomXdndPosition;
|
||||
X11::Atom xAtomXdndLeave;
|
||||
X11::Atom xAtomXdndDrop;
|
||||
X11::Atom xAtomXdndActionCopy;
|
||||
X11::Atom xAtomXdndStatus;
|
||||
X11::Atom xAtomXdndSelection;
|
||||
X11::Atom xAtomXdndFinished;
|
||||
X11::Atom xAtomXdndAware;
|
||||
X11::Atom xAtomWmState;
|
||||
X11::Atom xAtomWmStateHidden;
|
||||
X11::Atom xAtomWmStateMaxVert;
|
||||
@@ -65,6 +75,11 @@ X11::Atom xAtomWmStateMaxHorz;
|
||||
X11::Atom xAtomWmWindowOpacity;
|
||||
X11::Atom xAtomWmName;
|
||||
X11::Atom xAtomClipboard;
|
||||
X11::Atom xDnDRequested = 0;
|
||||
X11::Window xDndSourceWindow = 0;
|
||||
DragDropEffect xDndResult;
|
||||
Vector2 xDndPos;
|
||||
int32 xDnDVersion = 0;
|
||||
int32 SystemDpi = 96;
|
||||
X11::Cursor Cursors[(int32)CursorType::MAX];
|
||||
X11::XcursorImage* CursorsImg[(int32)CursorType::MAX];
|
||||
@@ -1171,6 +1186,13 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
struct Property
|
||||
{
|
||||
unsigned char* data;
|
||||
int format, nitems;
|
||||
X11::Atom type;
|
||||
};
|
||||
|
||||
namespace Impl
|
||||
{
|
||||
LinuxKeyboard Keyboard;
|
||||
@@ -1222,6 +1244,371 @@ namespace Impl
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Property ReadProperty(X11::Display* display, X11::Window window, X11::Atom property)
|
||||
{
|
||||
X11::Atom readType = 0;
|
||||
int readFormat = 0;
|
||||
unsigned long nitems = 0;
|
||||
unsigned long readBytes = 0;
|
||||
unsigned char* result = nullptr;
|
||||
int bytesCount = 1024;
|
||||
if (property != 0)
|
||||
{
|
||||
do
|
||||
{
|
||||
if (result != nullptr)
|
||||
X11::XFree(result);
|
||||
XGetWindowProperty(display, window, property, 0, bytesCount, 0, AnyPropertyType, &readType, &readFormat, &nitems, &readBytes, &result);
|
||||
bytesCount *= 2;
|
||||
} while (readBytes != 0);
|
||||
}
|
||||
Property p = { result, readFormat, (int)nitems, readType };
|
||||
return p;
|
||||
}
|
||||
|
||||
static X11::Atom SelectTargetFromList(X11::Display* display, const char* targetType, X11::Atom* list, int count)
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
X11::Atom atom = list[i];
|
||||
if (atom != 0 && StringAnsi(XGetAtomName(display, atom)) == targetType)
|
||||
return atom;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static X11::Atom SelectTargetFromAtoms(X11::Display* display, const char* targetType, X11::Atom t1, X11::Atom t2, X11::Atom t3)
|
||||
{
|
||||
if (t1 != 0 && StringAnsi(XGetAtomName(display, t1)) == targetType)
|
||||
return t1;
|
||||
if (t2 != 0 && StringAnsi(XGetAtomName(display, t2)) == targetType)
|
||||
return t2;
|
||||
if (t3 != 0 && StringAnsi(XGetAtomName(display, t3)) == targetType)
|
||||
return t3;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static X11::Window FindAppWindow(X11::Display* display, X11::Window w)
|
||||
{
|
||||
int nprops, i = 0;
|
||||
X11::Atom* a;
|
||||
if (w == 0)
|
||||
return 0;
|
||||
a = X11::XListProperties(display, w, &nprops);
|
||||
for (i = 0; i < nprops; i++)
|
||||
{
|
||||
if (a[i] == xAtomXdndAware)
|
||||
break;
|
||||
}
|
||||
if (nprops)
|
||||
X11::XFree(a);
|
||||
if (i != nprops)
|
||||
return w;
|
||||
X11::Window child, wtmp;
|
||||
int tmp;
|
||||
unsigned int utmp;
|
||||
X11::XQueryPointer(display, w, &wtmp, &child, &tmp, &tmp, &tmp, &tmp, &utmp);
|
||||
return FindAppWindow(display, child);
|
||||
}
|
||||
}
|
||||
|
||||
class LinuxDropFilesData : public IGuiData
|
||||
{
|
||||
public:
|
||||
Array<String> Files;
|
||||
|
||||
Type GetType() const override
|
||||
{
|
||||
return Type::Files;
|
||||
}
|
||||
String GetAsText() const override
|
||||
{
|
||||
return String::Empty;
|
||||
}
|
||||
void GetAsFiles(Array<String>* files) const override
|
||||
{
|
||||
files->Add(Files);
|
||||
}
|
||||
};
|
||||
|
||||
class LinuxDropTextData : public IGuiData
|
||||
{
|
||||
public:
|
||||
StringView Text;
|
||||
|
||||
Type GetType() const override
|
||||
{
|
||||
return Type::Text;
|
||||
}
|
||||
String GetAsText() const override
|
||||
{
|
||||
return String(Text);
|
||||
}
|
||||
void GetAsFiles(Array<String>* files) const override
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
DragDropEffect LinuxWindow::DoDragDrop(const StringView& data)
|
||||
{
|
||||
auto cursorWrong = X11::XCreateFontCursor(xDisplay, 54);
|
||||
auto cursorTransient = X11::XCreateFontCursor(xDisplay, 24);
|
||||
auto cursorGood = X11::XCreateFontCursor(xDisplay, 4);
|
||||
Array<X11::Atom, FixedAllocation<3>> formats;
|
||||
formats.Add(X11::XInternAtom(xDisplay, "text/plain", 0));
|
||||
formats.Add(X11::XInternAtom(xDisplay, "TEXT", 0));
|
||||
formats.Add((X11::Atom)31);
|
||||
StringAnsi dataAnsi(data);
|
||||
LinuxDropTextData dropData;
|
||||
dropData.Text = data;
|
||||
|
||||
// Begin dragging
|
||||
auto screen = X11::XDefaultScreen(xDisplay);
|
||||
auto rootWindow = X11::XRootWindow(xDisplay, screen);
|
||||
if (X11::XGrabPointer(xDisplay, _window, 1, Button1MotionMask | ButtonReleaseMask, GrabModeAsync, GrabModeAsync, rootWindow, cursorWrong, CurrentTime) != GrabSuccess)
|
||||
return DragDropEffect::None;
|
||||
X11::XSetSelectionOwner(xDisplay, xAtomXdndSelection, _window, CurrentTime);
|
||||
|
||||
// Process events
|
||||
X11::XEvent event;
|
||||
enum Status
|
||||
{
|
||||
Unaware,
|
||||
Unreceptive,
|
||||
CanDrop,
|
||||
};
|
||||
int status = Unaware, previousVersion = -1;
|
||||
X11::Window previousWindow = 0;
|
||||
DragDropEffect result = DragDropEffect::None;
|
||||
float lastDraw = Platform::GetTimeSeconds();
|
||||
while (true)
|
||||
{
|
||||
X11::XNextEvent(xDisplay, &event);
|
||||
|
||||
if (event.type == SelectionClear)
|
||||
break;
|
||||
if (event.type == SelectionRequest)
|
||||
{
|
||||
// Extract the relavent data
|
||||
X11::Window owner = event.xselectionrequest.owner;
|
||||
X11::Atom selection = event.xselectionrequest.selection;
|
||||
X11::Atom target = event.xselectionrequest.target;
|
||||
X11::Atom property = event.xselectionrequest.property;
|
||||
X11::Window requestor = event.xselectionrequest.requestor;
|
||||
X11::Time timestamp = event.xselectionrequest.time;
|
||||
X11::Display* disp = event.xselection.display;
|
||||
X11::XEvent s;
|
||||
s.xselection.type = SelectionNotify;
|
||||
s.xselection.requestor = requestor;
|
||||
s.xselection.selection = selection;
|
||||
s.xselection.target = target;
|
||||
s.xselection.property = 0;
|
||||
s.xselection.time = timestamp;
|
||||
if (target == X11::XInternAtom(disp, "TARGETS", 0))
|
||||
{
|
||||
Array<X11::Atom> targets;
|
||||
targets.Add(target);
|
||||
targets.Add(X11::XInternAtom(disp, "MULTIPLE", 0));
|
||||
targets.Add(formats.Get(), formats.Count());
|
||||
X11::XChangeProperty(disp, requestor, property, (X11::Atom)4, 32, PropModeReplace, (unsigned char*)targets.Get(), targets.Count());
|
||||
s.xselection.property = property;
|
||||
}
|
||||
else if (formats.Contains(target))
|
||||
{
|
||||
s.xselection.property = property;
|
||||
X11::XChangeProperty(disp, requestor, property, target, 8, PropModeReplace, reinterpret_cast<const unsigned char*>(dataAnsi.Get()), dataAnsi.Length());
|
||||
}
|
||||
X11::XSendEvent(event.xselection.display, event.xselectionrequest.requestor, 1, 0, &s);
|
||||
}
|
||||
else if (event.type == MotionNotify)
|
||||
{
|
||||
// Find window under mouse
|
||||
auto window = Impl::FindAppWindow(xDisplay, rootWindow);
|
||||
int fmt, version = -1;
|
||||
X11::Atom atmp;
|
||||
unsigned long nitems, bytesLeft;
|
||||
unsigned char* data = nullptr;
|
||||
if (window == previousWindow)
|
||||
version = previousVersion;
|
||||
else if(window == 0)
|
||||
;
|
||||
else if (X11::XGetWindowProperty(xDisplay, window, xAtomXdndAware, 0, 2, 0, AnyPropertyType, &atmp, &fmt, &nitems, &bytesLeft, &data) != Success)
|
||||
continue;
|
||||
else if (data == 0)
|
||||
continue;
|
||||
else if (fmt != 32)
|
||||
continue;
|
||||
else if (nitems != 1)
|
||||
continue;
|
||||
else
|
||||
version = data[0];
|
||||
if (status == Unaware && version != -1)
|
||||
status = Unreceptive;
|
||||
else if(version == -1)
|
||||
status = Unaware;
|
||||
xDndPos = Vector2((float)event.xmotion.x_root, (float)event.xmotion.y_root);
|
||||
|
||||
// Update mouse grab
|
||||
if (status == Unaware)
|
||||
X11::XChangeActivePointerGrab(xDisplay, Button1MotionMask | ButtonReleaseMask, cursorWrong, CurrentTime);
|
||||
else if(status == Unreceptive)
|
||||
X11::XChangeActivePointerGrab(xDisplay, Button1MotionMask | ButtonReleaseMask, cursorTransient, CurrentTime);
|
||||
else
|
||||
X11::XChangeActivePointerGrab(xDisplay, Button1MotionMask | ButtonReleaseMask, cursorGood, CurrentTime);
|
||||
|
||||
if (window != previousWindow && previousVersion != -1)
|
||||
{
|
||||
// Send drag left event
|
||||
auto ww = WindowsManager::GetByNativePtr((void*)previousWindow);
|
||||
if (ww)
|
||||
{
|
||||
ww->_dragOver = false;
|
||||
ww->OnDragLeave();
|
||||
}
|
||||
else
|
||||
{
|
||||
X11::XClientMessageEvent m;
|
||||
memset(&m, 0, sizeof(m));
|
||||
m.type = ClientMessage;
|
||||
m.display = event.xclient.display;
|
||||
m.window = previousWindow;
|
||||
m.message_type = xAtomXdndLeave;
|
||||
m.format = 32;
|
||||
m.data.l[0] = _window;
|
||||
m.data.l[1] = 0;
|
||||
m.data.l[2] = 0;
|
||||
m.data.l[3] = 0;
|
||||
m.data.l[4] = 0;
|
||||
X11::XSendEvent(xDisplay, previousWindow, 0, NoEventMask, (X11::XEvent*)&m);
|
||||
X11::XFlush(xDisplay);
|
||||
}
|
||||
}
|
||||
|
||||
if (window != previousWindow && version != -1)
|
||||
{
|
||||
// Send drag enter event
|
||||
auto ww = WindowsManager::GetByNativePtr((void*)window);
|
||||
if (ww)
|
||||
{
|
||||
xDndPos = ww->ScreenToClient(Platform::GetMousePosition());
|
||||
xDndResult = DragDropEffect::None;
|
||||
ww->OnDragEnter(&dropData, xDndPos, xDndResult);
|
||||
}
|
||||
else
|
||||
{
|
||||
X11::XClientMessageEvent m;
|
||||
memset(&m, 0, sizeof(m));
|
||||
m.type = ClientMessage;
|
||||
m.display = event.xclient.display;
|
||||
m.window = window;
|
||||
m.message_type = xAtomXdndEnter;
|
||||
m.format = 32;
|
||||
m.data.l[0] = _window;
|
||||
m.data.l[1] = Math::Min(5, version) << 24 | (formats.Count() > 3);
|
||||
m.data.l[2] = formats.Count() > 0 ? formats[0] : 0;
|
||||
m.data.l[3] = formats.Count() > 1 ? formats[1] : 0;
|
||||
m.data.l[4] = formats.Count() > 2 ? formats[2] : 0;
|
||||
X11::XSendEvent(xDisplay, window, 0, NoEventMask, (X11::XEvent*)&m);
|
||||
X11::XFlush(xDisplay);
|
||||
}
|
||||
}
|
||||
|
||||
if (version != -1)
|
||||
{
|
||||
// Send position event
|
||||
auto ww = WindowsManager::GetByNativePtr((void*)window);
|
||||
if (ww)
|
||||
{
|
||||
xDndPos = ww->ScreenToClient(Platform::GetMousePosition());
|
||||
ww->_dragOver = true;
|
||||
xDndResult = DragDropEffect::None;
|
||||
ww->OnDragOver(&dropData, xDndPos, xDndResult);
|
||||
status = CanDrop;
|
||||
}
|
||||
else
|
||||
{
|
||||
int x, y, tmp;
|
||||
unsigned int utmp;
|
||||
X11::Window wtmp;
|
||||
X11::XQueryPointer(xDisplay, window, &wtmp, &wtmp, &tmp, &tmp, &x, &y, &utmp);
|
||||
X11::XClientMessageEvent m;
|
||||
memset(&m, 0, sizeof(m));
|
||||
m.type = ClientMessage;
|
||||
m.display = event.xclient.display;
|
||||
m.window = window;
|
||||
m.message_type = xAtomXdndPosition;
|
||||
m.format = 32;
|
||||
m.data.l[0] = _window;
|
||||
m.data.l[1] = 0;
|
||||
m.data.l[2] = (x << 16) | y;
|
||||
m.data.l[3] = CurrentTime;
|
||||
m.data.l[4] = xAtomXdndActionCopy;
|
||||
X11::XSendEvent(xDisplay, window, 0, NoEventMask, (X11::XEvent*)&m);
|
||||
X11::XFlush(xDisplay);
|
||||
}
|
||||
}
|
||||
|
||||
previousWindow = window;
|
||||
previousVersion = version;
|
||||
}
|
||||
else if (event.type == ClientMessage && event.xclient.message_type == xAtomXdndStatus)
|
||||
{
|
||||
if ((event.xclient.data.l[1]&1) && status != Unaware)
|
||||
status = CanDrop;
|
||||
if (!(event.xclient.data.l[1]&1) && status != Unaware)
|
||||
status = Unreceptive;
|
||||
}
|
||||
else if (event.type == ButtonRelease && event.xbutton.button == 1)
|
||||
{
|
||||
if (status == CanDrop)
|
||||
{
|
||||
// Send drop event
|
||||
auto ww = WindowsManager::GetByNativePtr((void*)previousWindow);
|
||||
if (ww)
|
||||
{
|
||||
xDndPos = ww->ScreenToClient(Platform::GetMousePosition());
|
||||
xDndResult = DragDropEffect::None;
|
||||
ww->OnDragDrop(&dropData, xDndPos, xDndResult);
|
||||
ww->Focus();
|
||||
result = xDndResult;
|
||||
}
|
||||
else
|
||||
{
|
||||
X11::XClientMessageEvent m;
|
||||
memset(&m, 0, sizeof(m));
|
||||
m.type = ClientMessage;
|
||||
m.display = event.xclient.display;
|
||||
m.window = previousWindow;
|
||||
m.message_type = xAtomXdndDrop;
|
||||
m.format = 32;
|
||||
m.data.l[0] = _window;
|
||||
m.data.l[1] = 0;
|
||||
m.data.l[2] = CurrentTime;
|
||||
m.data.l[3] = 0;
|
||||
m.data.l[4] = 0;
|
||||
X11::XSendEvent(xDisplay, previousWindow, 0, NoEventMask, (X11::XEvent*)&m);
|
||||
X11::XFlush(xDisplay);
|
||||
result = DragDropEffect::Copy;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Redraw
|
||||
const float time = Platform::GetTimeSeconds();
|
||||
if (time - lastDraw >= 1.0f / 60.0f)
|
||||
{
|
||||
lastDraw = time;
|
||||
Engine::OnDraw();
|
||||
}
|
||||
}
|
||||
|
||||
// End grabbing
|
||||
X11::XChangeActivePointerGrab(xDisplay, Button1MotionMask | ButtonReleaseMask, 0, CurrentTime);
|
||||
XUngrabPointer(xDisplay, CurrentTime);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void LinuxClipboard::Clear()
|
||||
@@ -1651,7 +2038,15 @@ bool LinuxPlatform::Init()
|
||||
}
|
||||
|
||||
xAtomDeleteWindow = X11::XInternAtom(xDisplay, "WM_DELETE_WINDOW", 0);
|
||||
xAtomWmState = X11::XInternAtom(xDisplay, "_NET_WM_STATE", 0);
|
||||
xAtomXdndEnter = X11::XInternAtom(xDisplay, "XdndEnter", 0);
|
||||
xAtomXdndPosition = X11::XInternAtom(xDisplay, "XdndPosition", 0);
|
||||
xAtomXdndLeave = X11::XInternAtom(xDisplay, "XdndLeave", 0);
|
||||
xAtomXdndDrop = X11::XInternAtom(xDisplay, "XdndDrop", 0);
|
||||
xAtomXdndActionCopy = X11::XInternAtom(xDisplay, "XdndActionCopy", 0);
|
||||
xAtomXdndStatus = X11::XInternAtom(xDisplay, "XdndStatus", 0);
|
||||
xAtomXdndSelection = X11::XInternAtom(xDisplay, "XdndSelection", 0);
|
||||
xAtomXdndFinished = X11::XInternAtom(xDisplay, "XdndFinished", 0);
|
||||
xAtomXdndAware = X11::XInternAtom(xDisplay, "XdndAware", 0);
|
||||
xAtomWmStateHidden = X11::XInternAtom(xDisplay, "_NET_WM_STATE_HIDDEN", 0);
|
||||
xAtomWmStateMaxHorz = X11::XInternAtom(xDisplay, "_NET_WM_STATE_MAXIMIZED_HORZ", 0);
|
||||
xAtomWmStateMaxVert = X11::XInternAtom(xDisplay, "_NET_WM_STATE_MAXIMIZED_VERT", 0);
|
||||
@@ -1799,6 +2194,93 @@ void LinuxPlatform::Tick()
|
||||
window->Close(ClosingReason::User);
|
||||
}
|
||||
}
|
||||
else if ((uint32)event.xclient.message_type == (uint32)xAtomXdndEnter)
|
||||
{
|
||||
// Drag&drop enter
|
||||
X11::Window source = event.xclient.data.l[0];
|
||||
xDnDVersion = (int32)(event.xclient.data.l[1] >> 24);
|
||||
const char* targetTypeFiles = "text/uri-list";
|
||||
if (event.xclient.data.l[1] & 1)
|
||||
{
|
||||
Property p = Impl::ReadProperty(xDisplay, source, XInternAtom(xDisplay, "XdndTypeList", 0));
|
||||
xDnDRequested = Impl::SelectTargetFromList(xDisplay, targetTypeFiles, (X11::Atom*)p.data, p.nitems);
|
||||
X11::XFree(p.data);
|
||||
}
|
||||
else
|
||||
{
|
||||
xDnDRequested = Impl::SelectTargetFromAtoms(xDisplay, targetTypeFiles, event.xclient.data.l[2], event.xclient.data.l[3], event.xclient.data.l[4]);
|
||||
}
|
||||
}
|
||||
else if ((uint32)event.xclient.message_type == (uint32)xAtomXdndPosition)
|
||||
{
|
||||
// Drag&drop move
|
||||
X11::XClientMessageEvent m;
|
||||
memset(&m, 0, sizeof(m));
|
||||
m.type = ClientMessage;
|
||||
m.display = event.xclient.display;
|
||||
m.window = event.xclient.data.l[0];
|
||||
m.message_type = xAtomXdndStatus;
|
||||
m.format = 32;
|
||||
m.data.l[0] = event.xany.window;
|
||||
m.data.l[1] = (xDnDRequested != 0);
|
||||
m.data.l[2] = 0;
|
||||
m.data.l[3] = 0;
|
||||
m.data.l[4] = xAtomXdndActionCopy;
|
||||
X11::XSendEvent(xDisplay, event.xclient.data.l[0], 0, NoEventMask, (X11::XEvent*)&m);
|
||||
X11::XFlush(xDisplay);
|
||||
xDndPos = Vector2((float)(event.xclient.data.l[2] >> 16), (float)(event.xclient.data.l[2] & 0xffff));
|
||||
window = WindowsManager::GetByNativePtr((void*)event.xany.window);
|
||||
if (window)
|
||||
{
|
||||
LinuxDropFilesData dropData;
|
||||
xDndResult = DragDropEffect::None;
|
||||
if (window->_dragOver)
|
||||
{
|
||||
window->OnDragEnter(&dropData, xDndPos, xDndResult);
|
||||
}
|
||||
else
|
||||
{
|
||||
window->_dragOver = true;
|
||||
window->OnDragOver(&dropData, xDndPos, xDndResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ((uint32)event.xclient.message_type == (uint32)xAtomXdndLeave)
|
||||
{
|
||||
window = WindowsManager::GetByNativePtr((void*)event.xany.window);
|
||||
if (window && window->_dragOver)
|
||||
{
|
||||
window->_dragOver = false;
|
||||
window->OnDragLeave();
|
||||
}
|
||||
}
|
||||
else if ((uint32)event.xclient.message_type == (uint32)xAtomXdndDrop)
|
||||
{
|
||||
auto w = event.xany.window;
|
||||
if (xDnDRequested != 0)
|
||||
{
|
||||
xDndSourceWindow = event.xclient.data.l[0];
|
||||
auto primary = XInternAtom(xDisplay, "PRIMARY", 0);
|
||||
if (xDnDVersion >= 1)
|
||||
XConvertSelection(xDisplay, xAtomXdndSelection, xDnDRequested, primary, w, event.xclient.data.l[2]);
|
||||
else
|
||||
XConvertSelection(xDisplay, xAtomXdndSelection, xDnDRequested, primary, w, CurrentTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
X11::XClientMessageEvent m;
|
||||
memset(&m, 0, sizeof(m));
|
||||
m.type = ClientMessage;
|
||||
m.display = event.xclient.display;
|
||||
m.window = event.xclient.data.l[0];
|
||||
m.message_type = xAtomXdndFinished;
|
||||
m.format = 32;
|
||||
m.data.l[0] = w;
|
||||
m.data.l[1] = 0;
|
||||
m.data.l[2] = 0;
|
||||
X11::XSendEvent(xDisplay, event.xclient.data.l[0], 0, NoEventMask, (X11::XEvent*)&m);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MapNotify:
|
||||
// Auto-focus shown windows
|
||||
@@ -1980,6 +2462,41 @@ void LinuxPlatform::Tick()
|
||||
X11::XSendEvent(xDisplay, ev.requestor, 0, 0, (X11::XEvent*)&ev);
|
||||
break;
|
||||
}
|
||||
case SelectionNotify:
|
||||
if (event.xselection.target == xDnDRequested)
|
||||
{
|
||||
// Drag&drop
|
||||
window = WindowsManager::GetByNativePtr((void*)event.xany.window);
|
||||
if (window)
|
||||
{
|
||||
Property p = Impl::ReadProperty(xDisplay, event.xany.window, X11::XInternAtom(xDisplay, "PRIMARY", 0));
|
||||
if (xDndResult != DragDropEffect::None)
|
||||
{
|
||||
LinuxDropFilesData dropData;
|
||||
const String filesList((const char*)p.data);
|
||||
filesList.Split('\n', dropData.Files);
|
||||
for (auto& e : dropData.Files)
|
||||
{
|
||||
e.Replace(TEXT("file://"), TEXT(""));
|
||||
e = e.TrimTrailing();
|
||||
}
|
||||
xDndResult = DragDropEffect::None;
|
||||
window->OnDragDrop(&dropData, xDndPos, xDndResult);
|
||||
}
|
||||
}
|
||||
X11::XClientMessageEvent m;
|
||||
memset(&m, 0, sizeof(m));
|
||||
m.type = ClientMessage;
|
||||
m.display = xDisplay;
|
||||
m.window = xDndSourceWindow;
|
||||
m.message_type = xAtomXdndFinished;
|
||||
m.format = 32;
|
||||
m.data.l[0] = event.xany.window;
|
||||
m.data.l[1] = 1;
|
||||
m.data.l[2] = xAtomXdndActionCopy;
|
||||
XSendEvent(xDisplay, xDndSourceWindow, 0, NoEventMask, (X11::XEvent*)&m);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -103,7 +103,6 @@ LinuxWindow::LinuxWindow(const CreateWindowSettings& settings)
|
||||
bool Fullscreen;
|
||||
bool AllowMinimize;
|
||||
bool AllowMaximize;
|
||||
bool AllowDragAndDrop;
|
||||
*/
|
||||
|
||||
const X11::Window window = X11::XCreateWindow(
|
||||
@@ -223,6 +222,15 @@ LinuxWindow::LinuxWindow(const CreateWindowSettings& settings)
|
||||
}
|
||||
X11::XChangeProperty(display, window, wmState, (X11::Atom)4, 32, PropModeReplace, (unsigned char*)states, statesCount);
|
||||
|
||||
// Drag&drop support
|
||||
if (settings.AllowDragAndDrop)
|
||||
{
|
||||
auto xdndVersion = 5;
|
||||
auto xdndAware = XInternAtom(display, "XdndAware", 0);
|
||||
if (xdndAware != 0)
|
||||
X11::XChangeProperty(display, window, xdndAware, (X11::Atom)4, 32, PropModeReplace, (unsigned char*)&xdndVersion, 1);
|
||||
}
|
||||
|
||||
// Sync
|
||||
X11::XFlush(display);
|
||||
X11::XSync(display, 0);
|
||||
@@ -725,12 +733,6 @@ void LinuxWindow::SetTitle(const StringView& title)
|
||||
_title = title;
|
||||
}
|
||||
|
||||
DragDropEffect LinuxWindow::DoDragDrop(const StringView& data)
|
||||
{
|
||||
// TODO: impl drag and drop on Linux
|
||||
return DragDropEffect::None;
|
||||
}
|
||||
|
||||
void LinuxWindow::StartTrackingMouse(bool useMouseScreenOffset)
|
||||
{
|
||||
// TODO: impl this
|
||||
|
||||
@@ -13,14 +13,13 @@
|
||||
class LinuxWindow : public WindowBase
|
||||
{
|
||||
friend LinuxPlatform;
|
||||
|
||||
public:
|
||||
|
||||
typedef unsigned long HandleType;
|
||||
|
||||
private:
|
||||
|
||||
bool _resizeDisabled, _focusOnMapped = false;
|
||||
bool _resizeDisabled, _focusOnMapped = false, _dragOver = false;
|
||||
float _opacity = 1.0f;
|
||||
HandleType _window;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user