mirror of
https://github.com/glfw/glfw.git
synced 2024-11-29 21:37:27 +00:00
X11: Implement extended drag & drop API
This commit is contained in:
parent
2933c00e54
commit
0a757737d3
@ -910,11 +910,6 @@ static GLFWbool initExtensions(void)
|
||||
// the keyboard mapping.
|
||||
createKeyTables();
|
||||
|
||||
// String format atoms
|
||||
_glfw.x11.NULL_ = XInternAtom(_glfw.x11.display, "NULL", False);
|
||||
_glfw.x11.UTF8_STRING = XInternAtom(_glfw.x11.display, "UTF8_STRING", False);
|
||||
_glfw.x11.ATOM_PAIR = XInternAtom(_glfw.x11.display, "ATOM_PAIR", False);
|
||||
|
||||
// Custom selection property atom
|
||||
_glfw.x11.GLFW_SELECTION =
|
||||
XInternAtom(_glfw.x11.display, "GLFW_SELECTION", False);
|
||||
@ -925,6 +920,7 @@ static GLFWbool initExtensions(void)
|
||||
_glfw.x11.PRIMARY = XInternAtom(_glfw.x11.display, "PRIMARY", False);
|
||||
_glfw.x11.INCR = XInternAtom(_glfw.x11.display, "INCR", False);
|
||||
_glfw.x11.CLIPBOARD = XInternAtom(_glfw.x11.display, "CLIPBOARD", False);
|
||||
_glfw.x11.ATOM_PAIR = XInternAtom(_glfw.x11.display, "ATOM_PAIR", False);
|
||||
|
||||
// Clipboard manager atoms
|
||||
_glfw.x11.CLIPBOARD_MANAGER =
|
||||
@ -937,11 +933,24 @@ static GLFWbool initExtensions(void)
|
||||
_glfw.x11.XdndEnter = XInternAtom(_glfw.x11.display, "XdndEnter", False);
|
||||
_glfw.x11.XdndPosition = XInternAtom(_glfw.x11.display, "XdndPosition", False);
|
||||
_glfw.x11.XdndStatus = XInternAtom(_glfw.x11.display, "XdndStatus", False);
|
||||
_glfw.x11.XdndActionCopy = XInternAtom(_glfw.x11.display, "XdndActionCopy", False);
|
||||
_glfw.x11.XdndLeave = XInternAtom(_glfw.x11.display, "XdndLeave", False);
|
||||
_glfw.x11.XdndActions[_GLFW_DND_COPY_INDEX] =
|
||||
XInternAtom(_glfw.x11.display, "XdndActionCopy", False);
|
||||
_glfw.x11.XdndActions[_GLFW_DND_LINK_INDEX] =
|
||||
XInternAtom(_glfw.x11.display, "XdndActionLink", False);
|
||||
_glfw.x11.XdndActions[_GLFW_DND_MOVE_INDEX] =
|
||||
XInternAtom(_glfw.x11.display, "XdndActionMove", False);
|
||||
_glfw.x11.XdndDrop = XInternAtom(_glfw.x11.display, "XdndDrop", False);
|
||||
_glfw.x11.XdndFinished = XInternAtom(_glfw.x11.display, "XdndFinished", False);
|
||||
_glfw.x11.XdndSelection = XInternAtom(_glfw.x11.display, "XdndSelection", False);
|
||||
_glfw.x11.XdndTypeList = XInternAtom(_glfw.x11.display, "XdndTypeList", False);
|
||||
|
||||
// Format atoms (shared by Xdnd and selection)
|
||||
_glfw.x11.NULL_ = XInternAtom(_glfw.x11.display, "NULL", False);
|
||||
_glfw.x11.UTF8_STRING = XInternAtom(_glfw.x11.display, "UTF8_STRING", False);
|
||||
_glfw.x11.STRING = XInternAtom(_glfw.x11.display, "STRING", False);
|
||||
_glfw.x11.TEXT = XInternAtom(_glfw.x11.display, "TEXT", False);
|
||||
_glfw.x11.text_plain = XInternAtom(_glfw.x11.display, "text/plain", False);
|
||||
_glfw.x11.text_uri_list = XInternAtom(_glfw.x11.display, "text/uri-list", False);
|
||||
|
||||
// ICCCM, EWMH and Motif window property atoms
|
||||
|
@ -97,6 +97,9 @@ typedef struct __GLXFBConfig* GLXFBConfig;
|
||||
typedef struct __GLXcontext* GLXContext;
|
||||
typedef void (*__GLXextproc)(void);
|
||||
|
||||
// TODO for dev; remove
|
||||
typedef char* (* PFN_XGetAtomName)(Display*,Atom);
|
||||
|
||||
typedef XClassHint* (* PFN_XAllocClassHint)(void);
|
||||
typedef XSizeHints* (* PFN_XAllocSizeHints)(void);
|
||||
typedef XWMHints* (* PFN_XAllocWMHints)(void);
|
||||
@ -622,12 +625,12 @@ typedef struct _GLFWlibraryX11
|
||||
Atom XdndEnter;
|
||||
Atom XdndPosition;
|
||||
Atom XdndStatus;
|
||||
Atom XdndActionCopy;
|
||||
Atom XdndLeave;
|
||||
Atom XdndActions[_GLFW_DND_ACTION_COUNT];
|
||||
Atom XdndDrop;
|
||||
Atom XdndFinished;
|
||||
Atom XdndSelection;
|
||||
Atom XdndTypeList;
|
||||
Atom text_uri_list;
|
||||
|
||||
// Selection (clipboard) atoms
|
||||
Atom TARGETS;
|
||||
@ -637,12 +640,17 @@ typedef struct _GLFWlibraryX11
|
||||
Atom PRIMARY;
|
||||
Atom CLIPBOARD_MANAGER;
|
||||
Atom SAVE_TARGETS;
|
||||
Atom NULL_;
|
||||
Atom UTF8_STRING;
|
||||
Atom COMPOUND_STRING;
|
||||
Atom ATOM_PAIR;
|
||||
Atom GLFW_SELECTION;
|
||||
|
||||
// Format atoms (shared by Xdnd and Selection)
|
||||
Atom NULL_;
|
||||
Atom UTF8_STRING;
|
||||
Atom STRING;
|
||||
Atom TEXT;
|
||||
Atom text_plain;
|
||||
Atom text_uri_list;
|
||||
|
||||
struct {
|
||||
void* handle;
|
||||
GLFWbool utf8;
|
||||
@ -800,7 +808,12 @@ typedef struct _GLFWlibraryX11
|
||||
struct {
|
||||
int version;
|
||||
Window source;
|
||||
Atom format;
|
||||
Atom formatAtoms[_GLFW_DND_FORMAT_COUNT];
|
||||
int availableFormats;
|
||||
int chosenFormat;
|
||||
int availableActions;
|
||||
int proposedAction;
|
||||
int chosenAction;
|
||||
} xdnd;
|
||||
|
||||
struct {
|
||||
|
146
src/x11_window.c
146
src/x11_window.c
@ -1519,15 +1519,22 @@ static void processEvent(XEvent *event)
|
||||
// A drag operation has entered the window
|
||||
unsigned long count;
|
||||
Atom* formats = NULL;
|
||||
double xpos, ypos;
|
||||
const GLFWbool list = event->xclient.data.l[1] & 1;
|
||||
|
||||
memset(&_glfw.x11.xdnd, 0, sizeof(_glfw.x11.xdnd));
|
||||
_glfw.x11.xdnd.source = event->xclient.data.l[0];
|
||||
_glfw.x11.xdnd.version = event->xclient.data.l[1] >> 24;
|
||||
_glfw.x11.xdnd.format = None;
|
||||
|
||||
if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION)
|
||||
return;
|
||||
|
||||
// Xdnd doesn't support the concepts "available actions" and
|
||||
// "proposed action"; default to all and copy
|
||||
_glfw.x11.xdnd.availableActions =
|
||||
GLFW_DND_COPY | GLFW_DND_LINK | GLFW_DND_MOVE;
|
||||
_glfw.x11.xdnd.proposedAction = GLFW_DND_COPY;
|
||||
|
||||
if (list)
|
||||
{
|
||||
count = _glfwGetWindowPropertyX11(_glfw.x11.xdnd.source,
|
||||
@ -1541,27 +1548,75 @@ static void processEvent(XEvent *event)
|
||||
formats = (Atom*) event->xclient.data.l + 2;
|
||||
}
|
||||
|
||||
// TODO remove
|
||||
PFN_XGetAtomName GetAtomName = (PFN_XGetAtomName)
|
||||
_glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XGetAtomName");
|
||||
for (unsigned int i = 0; i < count; i++)
|
||||
{
|
||||
if (formats[i] == _glfw.x11.text_uri_list)
|
||||
printf("format (%d): %s\n", i, GetAtomName(_glfw.x11.display, formats[i]));
|
||||
if (!_glfw.x11.xdnd.formatAtoms[_GLFW_DND_PATHS_INDEX]
|
||||
&& formats[i] == _glfw.x11.text_uri_list)
|
||||
{
|
||||
_glfw.x11.xdnd.format = _glfw.x11.text_uri_list;
|
||||
break;
|
||||
_glfw.x11.xdnd.formatAtoms[_GLFW_DND_PATHS_INDEX] = formats[i];
|
||||
_glfw.x11.xdnd.availableFormats |= GLFW_DND_PATHS;
|
||||
}
|
||||
else if (!_glfw.x11.xdnd.formatAtoms[_GLFW_DND_TEXT_INDEX]
|
||||
&& (formats[i] == _glfw.x11.text_plain
|
||||
|| formats[i] == _glfw.x11.UTF8_STRING
|
||||
|| formats[i] == _glfw.x11.STRING
|
||||
|| formats[i] == _glfw.x11.TEXT))
|
||||
{
|
||||
_glfw.x11.xdnd.formatAtoms[_GLFW_DND_TEXT_INDEX] = formats[i];
|
||||
_glfw.x11.xdnd.availableFormats |= GLFW_DND_TEXT;
|
||||
}
|
||||
}
|
||||
|
||||
if (list && formats)
|
||||
XFree(formats);
|
||||
|
||||
window->dndDragging = GLFW_TRUE;
|
||||
|
||||
_glfwGetCursorPosX11(window, &xpos, &ypos);
|
||||
|
||||
_glfw.x11.xdnd.chosenFormat = _glfw.x11.xdnd.availableFormats;
|
||||
_glfw.x11.xdnd.chosenAction = _glfw.x11.xdnd.proposedAction;
|
||||
_glfwInputDrag(window, GLFW_DND_ENTER, xpos, ypos,
|
||||
_glfw.x11.xdnd.availableFormats,
|
||||
&_glfw.x11.xdnd.chosenFormat,
|
||||
_glfw.x11.xdnd.availableActions,
|
||||
&_glfw.x11.xdnd.chosenAction);
|
||||
}
|
||||
else if (event->xclient.message_type == _glfw.x11.XdndLeave)
|
||||
{
|
||||
// A drag operation has left the window or was canceled
|
||||
window->dndDragging = GLFW_FALSE;
|
||||
|
||||
_glfw.x11.xdnd.chosenFormat =
|
||||
_glfw.x11.xdnd.availableFormats = GLFW_DND_NONE;
|
||||
_glfw.x11.xdnd.chosenAction =
|
||||
_glfw.x11.xdnd.proposedAction =
|
||||
_glfw.x11.xdnd.availableActions = GLFW_DND_NONE;
|
||||
_glfwInputDrag(window, GLFW_DND_LEAVE, 0.0, 0.0,
|
||||
_glfw.x11.xdnd.availableFormats,
|
||||
&_glfw.x11.xdnd.chosenFormat,
|
||||
_glfw.x11.xdnd.availableActions,
|
||||
&_glfw.x11.xdnd.chosenAction);
|
||||
}
|
||||
else if (event->xclient.message_type == _glfw.x11.XdndDrop)
|
||||
{
|
||||
// The drag operation has finished by dropping on the window
|
||||
Time time = CurrentTime;
|
||||
int formatIndex = _glfw_ffs(_glfw.x11.xdnd.chosenFormat
|
||||
& _GLFW_DND_MASK) - 1;
|
||||
|
||||
window->dndDragging = GLFW_FALSE;
|
||||
|
||||
if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION)
|
||||
return;
|
||||
|
||||
if (_glfw.x11.xdnd.format)
|
||||
if (_glfw.x11.xdnd.chosenAction
|
||||
&& formatIndex >= 0 && formatIndex < _GLFW_DND_FORMAT_COUNT
|
||||
&& _glfw.x11.xdnd.formatAtoms[formatIndex])
|
||||
{
|
||||
if (_glfw.x11.xdnd.version >= 1)
|
||||
time = event->xclient.data.l[2];
|
||||
@ -1569,7 +1624,7 @@ static void processEvent(XEvent *event)
|
||||
// Request the chosen format from the source window
|
||||
XConvertSelection(_glfw.x11.display,
|
||||
_glfw.x11.XdndSelection,
|
||||
_glfw.x11.xdnd.format,
|
||||
_glfw.x11.xdnd.formatAtoms[formatIndex],
|
||||
_glfw.x11.XdndSelection,
|
||||
window->x11.handle,
|
||||
time);
|
||||
@ -1594,12 +1649,29 @@ static void processEvent(XEvent *event)
|
||||
// The drag operation has moved over the window
|
||||
const int xabs = (event->xclient.data.l[2] >> 16) & 0xffff;
|
||||
const int yabs = (event->xclient.data.l[2]) & 0xffff;
|
||||
const Atom action = event->xclient.data.l[4];
|
||||
Window dummy;
|
||||
int xpos, ypos;
|
||||
|
||||
if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION)
|
||||
return;
|
||||
|
||||
if (_glfw.x11.xdnd.version >= 2)
|
||||
{
|
||||
_glfw.x11.xdnd.proposedAction = GLFW_DND_NONE;
|
||||
for(int i = 0; i < _GLFW_DND_ACTION_COUNT; ++i)
|
||||
{
|
||||
if(action == _glfw.x11.XdndActions[i])
|
||||
{
|
||||
_glfw.x11.xdnd.proposedAction =
|
||||
(GLFW_DND_COPY & ~_GLFW_DND_MASK) | (1 << i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
_glfw.x11.xdnd.proposedAction = GLFW_DND_COPY;
|
||||
|
||||
XTranslateCoordinates(_glfw.x11.display,
|
||||
_glfw.x11.root,
|
||||
window->x11.handle,
|
||||
@ -1609,6 +1681,13 @@ static void processEvent(XEvent *event)
|
||||
|
||||
_glfwInputCursorPos(window, xpos, ypos);
|
||||
|
||||
_glfw.x11.xdnd.chosenAction = _glfw.x11.xdnd.proposedAction;
|
||||
_glfwInputDrag(window, GLFW_DND_DRAG, xpos, ypos,
|
||||
_glfw.x11.xdnd.availableFormats,
|
||||
&_glfw.x11.xdnd.chosenFormat,
|
||||
_glfw.x11.xdnd.availableActions,
|
||||
&_glfw.x11.xdnd.chosenAction);
|
||||
|
||||
XEvent reply = { ClientMessage };
|
||||
reply.xclient.window = _glfw.x11.xdnd.source;
|
||||
reply.xclient.message_type = _glfw.x11.XdndStatus;
|
||||
@ -1617,12 +1696,19 @@ static void processEvent(XEvent *event)
|
||||
reply.xclient.data.l[2] = 0; // Specify an empty rectangle
|
||||
reply.xclient.data.l[3] = 0;
|
||||
|
||||
if (_glfw.x11.xdnd.format)
|
||||
if (_glfw.x11.xdnd.chosenAction)
|
||||
{
|
||||
// Reply that we are ready to copy the dragged data
|
||||
reply.xclient.data.l[1] = 1; // Accept with no rectangle
|
||||
if (_glfw.x11.xdnd.version >= 2)
|
||||
reply.xclient.data.l[4] = _glfw.x11.XdndActionCopy;
|
||||
{
|
||||
int actionIndex = _glfw_ffs(_glfw.x11.xdnd.chosenAction
|
||||
& _GLFW_DND_MASK) - 1;
|
||||
if(actionIndex >= 0 && actionIndex < _GLFW_DND_ACTION_COUNT)
|
||||
{
|
||||
reply.xclient.data.l[4] = _glfw.x11.XdndActions[actionIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source,
|
||||
@ -1647,14 +1733,38 @@ static void processEvent(XEvent *event)
|
||||
|
||||
if (result)
|
||||
{
|
||||
int count;
|
||||
char** paths = _glfwParseUriList(data, &count);
|
||||
int formatIndex = _glfw_ffs(_glfw.x11.xdnd.chosenFormat
|
||||
& _GLFW_DND_MASK) - 1;
|
||||
if(formatIndex >= 0 && formatIndex < _GLFW_DND_FORMAT_COUNT)
|
||||
{
|
||||
int format = (GLFW_DND_TEXT & ~_GLFW_DND_MASK) | (1 << formatIndex);
|
||||
switch (format)
|
||||
{
|
||||
case GLFW_DND_PATHS:
|
||||
{
|
||||
int count;
|
||||
char** paths = _glfwParseUriList(data, &count);
|
||||
|
||||
_glfwInputDrop(window, count, (const char**) paths);
|
||||
_glfwInputDrop(window, count, (const char **) paths);
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
_glfw_free(paths[i]);
|
||||
_glfw_free(paths);
|
||||
_glfwInputDropEx(window, format, count, paths,
|
||||
&_glfw.x11.xdnd.chosenAction);
|
||||
|
||||
for (int i = 0; i < count; ++i)
|
||||
_glfw_free(paths[i]);
|
||||
_glfw_free(paths);
|
||||
break;
|
||||
}
|
||||
case GLFW_DND_TEXT:
|
||||
{
|
||||
_glfwInputDropEx(window, format, -1, data,
|
||||
&_glfw.x11.xdnd.chosenAction);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (data)
|
||||
@ -1668,7 +1778,13 @@ static void processEvent(XEvent *event)
|
||||
reply.xclient.format = 32;
|
||||
reply.xclient.data.l[0] = window->x11.handle;
|
||||
reply.xclient.data.l[1] = result;
|
||||
reply.xclient.data.l[2] = _glfw.x11.XdndActionCopy;
|
||||
|
||||
int actionIndex = _glfw_ffs(_glfw.x11.xdnd.chosenAction
|
||||
& _GLFW_DND_MASK) - 1;
|
||||
if(actionIndex >= 0 && actionIndex < _GLFW_DND_ACTION_COUNT)
|
||||
{
|
||||
reply.xclient.data.l[2] = _glfw.x11.XdndActions[actionIndex];
|
||||
}
|
||||
|
||||
XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source,
|
||||
False, NoEventMask, &reply);
|
||||
|
Loading…
Reference in New Issue
Block a user