mirror of
https://github.com/glfw/glfw.git
synced 2025-06-16 04:32:14 +00:00
initial drag and drop support for windows and linux
This commit is contained in:
parent
0e12fb869c
commit
f383cc8289
@ -772,6 +772,20 @@ typedef void (* GLFWkeyfun)(GLFWwindow*,int,int,int,int);
|
|||||||
*/
|
*/
|
||||||
typedef void (* GLFWcharfun)(GLFWwindow*,unsigned int);
|
typedef void (* GLFWcharfun)(GLFWwindow*,unsigned int);
|
||||||
|
|
||||||
|
|
||||||
|
/*! @brief The function signature for drop callbacks.
|
||||||
|
*
|
||||||
|
* This is the function signature for drop callbacks.
|
||||||
|
*
|
||||||
|
* @param[in] window The window that received the event.
|
||||||
|
* @param[in] string The string descriptor for the dropped object.
|
||||||
|
*
|
||||||
|
* @sa glfwSetDropCallback
|
||||||
|
*
|
||||||
|
* @ingroup input
|
||||||
|
*/
|
||||||
|
typedef void (* GLFWdropfun)(GLFWwindow*,const char*);
|
||||||
|
|
||||||
/*! @brief The function signature for monitor configuration callbacks.
|
/*! @brief The function signature for monitor configuration callbacks.
|
||||||
*
|
*
|
||||||
* This is the function signature for monitor configuration callback functions.
|
* This is the function signature for monitor configuration callback functions.
|
||||||
@ -1983,6 +1997,22 @@ GLFWAPI GLFWcursorenterfun glfwSetCursorEnterCallback(GLFWwindow* window, GLFWcu
|
|||||||
*/
|
*/
|
||||||
GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* window, GLFWscrollfun cbfun);
|
GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* window, GLFWscrollfun cbfun);
|
||||||
|
|
||||||
|
/*! @brief Sets the drop callback.
|
||||||
|
*
|
||||||
|
* This function sets the drop callback of the specified window, which is
|
||||||
|
* called when an object is dropped over the window.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param[in] window The window whose callback to set.
|
||||||
|
* @param[in] cbfun The new drop callback, or `NULL` to remove the currently
|
||||||
|
* set callback.
|
||||||
|
* @return The previously set callback, or `NULL` if no callback was set or an
|
||||||
|
* error occurred.
|
||||||
|
*
|
||||||
|
* @ingroup input
|
||||||
|
*/
|
||||||
|
GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* window, GLFWdropfun cbfun);
|
||||||
|
|
||||||
/*! @brief Returns whether the specified joystick is present.
|
/*! @brief Returns whether the specified joystick is present.
|
||||||
*
|
*
|
||||||
* This function returns whether the specified joystick is present.
|
* This function returns whether the specified joystick is present.
|
||||||
|
17
src/input.c
17
src/input.c
@ -218,6 +218,12 @@ void _glfwInputCursorEnter(_GLFWwindow* window, int entered)
|
|||||||
window->callbacks.cursorEnter((GLFWwindow*) window, entered);
|
window->callbacks.cursorEnter((GLFWwindow*) window, entered);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _glfwInputDrop(_GLFWwindow* window, const char* dropString){
|
||||||
|
|
||||||
|
if (window->callbacks.drop)
|
||||||
|
window->callbacks.drop((GLFWwindow*) window, dropString);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
////// GLFW public API //////
|
////// GLFW public API //////
|
||||||
@ -425,3 +431,14 @@ GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* handle,
|
|||||||
return previous;
|
return previous;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* handle, GLFWdropfun cbfun)
|
||||||
|
{
|
||||||
|
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||||
|
GLFWdropfun previous;
|
||||||
|
|
||||||
|
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||||
|
|
||||||
|
previous = window->callbacks.drop;
|
||||||
|
window->callbacks.drop = cbfun;
|
||||||
|
return previous;
|
||||||
|
}
|
||||||
|
@ -229,6 +229,7 @@ struct _GLFWwindow
|
|||||||
GLFWscrollfun scroll;
|
GLFWscrollfun scroll;
|
||||||
GLFWkeyfun key;
|
GLFWkeyfun key;
|
||||||
GLFWcharfun character;
|
GLFWcharfun character;
|
||||||
|
GLFWdropfun drop;
|
||||||
} callbacks;
|
} callbacks;
|
||||||
|
|
||||||
// This is defined in the window API's platform.h
|
// This is defined in the window API's platform.h
|
||||||
@ -666,6 +667,14 @@ void _glfwInputMonitorChange(void);
|
|||||||
*/
|
*/
|
||||||
void _glfwInputError(int error, const char* format, ...);
|
void _glfwInputError(int error, const char* format, ...);
|
||||||
|
|
||||||
|
/*! @brief Notifies dropped object over window.
|
||||||
|
* @param[in] window The window that received the event.
|
||||||
|
* @param[in] dropString The string descriptor of the dropped object
|
||||||
|
* description.
|
||||||
|
* @ingroup event
|
||||||
|
*/
|
||||||
|
void _glfwInputDrop(_GLFWwindow* window, const char* dropString);
|
||||||
|
|
||||||
|
|
||||||
//========================================================================
|
//========================================================================
|
||||||
// Utility functions
|
// Utility functions
|
||||||
|
@ -167,6 +167,7 @@ typedef struct _GLFWlibraryWin32
|
|||||||
ATOM classAtom;
|
ATOM classAtom;
|
||||||
DWORD foregroundLockTimeout;
|
DWORD foregroundLockTimeout;
|
||||||
char* clipboardString;
|
char* clipboardString;
|
||||||
|
char* dropString;
|
||||||
|
|
||||||
// Timer data
|
// Timer data
|
||||||
struct {
|
struct {
|
||||||
|
@ -715,6 +715,45 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
|
|||||||
// TODO: Restore vsync if compositing was disabled
|
// TODO: Restore vsync if compositing was disabled
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case WM_DROPFILES:
|
||||||
|
|
||||||
|
// DragQueryFile() takes a LPWSTR for the name so we need a TCHAR string
|
||||||
|
TCHAR szName[MAX_PATH];
|
||||||
|
|
||||||
|
// Here we cast the wParam as a HDROP handle to pass into the next functions
|
||||||
|
HDROP hDrop = (HDROP)wParam;
|
||||||
|
|
||||||
|
POINT pt;
|
||||||
|
DragQueryPoint(hDrop, &pt);
|
||||||
|
//printf("%i %i \n", pt.x, pt.y);
|
||||||
|
|
||||||
|
_glfwInputMouseMotion(window,pt.x,pt.y);
|
||||||
|
|
||||||
|
|
||||||
|
// This functions has a couple functionalities. If you pass in 0xFFFFFFFF in
|
||||||
|
// the second parameter then it returns the count of how many filers were drag
|
||||||
|
// and dropped. Otherwise, the function fills in the szName string array with
|
||||||
|
// the current file being queried.
|
||||||
|
int count = DragQueryFile(hDrop, 0xFFFFFFFF, szName, MAX_PATH);
|
||||||
|
|
||||||
|
wchar_t * s;
|
||||||
|
// Here we go through all the files that were drag and dropped then display them
|
||||||
|
for(int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
// Grab the name of the file associated with index "i" in the list of files dropped.
|
||||||
|
// Be sure you know that the name is attached to the FULL path of the file.
|
||||||
|
DragQueryFile(hDrop, i, szName, MAX_PATH);
|
||||||
|
|
||||||
|
wcscat(s, (const wchar_t*)szName);
|
||||||
|
wcscat(s, (const wchar_t*)"\n");
|
||||||
|
}
|
||||||
|
free(_glfw.win32.dropString);
|
||||||
|
char * str = _glfwCreateUTF8FromWideString(s);
|
||||||
|
|
||||||
|
_glfwInputDrop(window,_glfw.win32.dropString);
|
||||||
|
|
||||||
|
DragFinish(hDrop);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return DefWindowProc(hWnd, uMsg, wParam, lParam);
|
return DefWindowProc(hWnd, uMsg, wParam, lParam);
|
||||||
|
@ -540,6 +540,27 @@ static GLboolean initExtensions(void)
|
|||||||
_glfw.x11.SAVE_TARGETS =
|
_glfw.x11.SAVE_TARGETS =
|
||||||
XInternAtom(_glfw.x11.display, "SAVE_TARGETS", False);
|
XInternAtom(_glfw.x11.display, "SAVE_TARGETS", False);
|
||||||
|
|
||||||
|
// Find or create drag and drop atoms
|
||||||
|
|
||||||
|
//Atoms for Xdnd
|
||||||
|
_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.XdndTypeList = XInternAtom(_glfw.x11.display, "XdndTypeList", False);
|
||||||
|
_glfw.x11.XdndActionCopy = XInternAtom(_glfw.x11.display, "XdndActionCopy", False);
|
||||||
|
_glfw.x11.XdndDrop = XInternAtom(_glfw.x11.display, "XdndDrop", False);
|
||||||
|
_glfw.x11.XdndLeave = XInternAtom(_glfw.x11.display, "XdndLeave", False);
|
||||||
|
_glfw.x11.XdndFinished = XInternAtom(_glfw.x11.display, "XdndFinished", False);
|
||||||
|
_glfw.x11.XdndSelection = XInternAtom(_glfw.x11.display, "XdndSelection", False);
|
||||||
|
_glfw.x11.XdndProxy = XInternAtom(_glfw.x11.display, "XdndProxy", False);
|
||||||
|
_glfw.x11.XA_TARGETS = XInternAtom(_glfw.x11.display, "TARGETS", False);
|
||||||
|
_glfw.x11.xdnd.string = NULL;
|
||||||
|
_glfw.x11.xdnd.type1 = NULL;
|
||||||
|
_glfw.x11.xdnd.type2 = NULL;
|
||||||
|
_glfw.x11.xdnd.type3 = NULL;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return GL_TRUE;
|
return GL_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,6 +123,26 @@ typedef struct _GLFWlibraryX11
|
|||||||
Atom NET_ACTIVE_WINDOW;
|
Atom NET_ACTIVE_WINDOW;
|
||||||
Atom MOTIF_WM_HINTS;
|
Atom MOTIF_WM_HINTS;
|
||||||
|
|
||||||
|
// Atoms for Xdnd
|
||||||
|
Atom XdndEnter;
|
||||||
|
Atom XdndPosition;
|
||||||
|
Atom XdndStatus;
|
||||||
|
Atom XdndTypeList;
|
||||||
|
Atom XdndActionCopy;
|
||||||
|
Atom XdndDrop;
|
||||||
|
Atom XdndLeave;
|
||||||
|
Atom XdndFinished;
|
||||||
|
Atom XdndSelection;
|
||||||
|
Atom XdndProxy;
|
||||||
|
Atom XA_TARGETS;
|
||||||
|
struct{
|
||||||
|
Window sourceWindow;
|
||||||
|
char* string;
|
||||||
|
char* type1;
|
||||||
|
char* type2;
|
||||||
|
char* type3;
|
||||||
|
} xdnd;
|
||||||
|
|
||||||
// Selection atoms
|
// Selection atoms
|
||||||
Atom TARGETS;
|
Atom TARGETS;
|
||||||
Atom MULTIPLE;
|
Atom MULTIPLE;
|
||||||
|
115
src/x11_window.c
115
src/x11_window.c
@ -286,6 +286,15 @@ static GLboolean createWindow(_GLFWwindow* window,
|
|||||||
XISelectEvents(_glfw.x11.display, window->x11.handle, &eventmask, 1);
|
XISelectEvents(_glfw.x11.display, window->x11.handle, &eventmask, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Enable Xdnd
|
||||||
|
{
|
||||||
|
//Announce XDND support
|
||||||
|
XMapWindow(_glfw.x11.display, window->x11.handle);
|
||||||
|
Atom XdndAware = XInternAtom(_glfw.x11.display, "XdndAware", False);
|
||||||
|
Atom version=5;
|
||||||
|
XChangeProperty(_glfw.x11.display, window->x11.handle, XdndAware, XA_ATOM, 32, PropModeReplace, (unsigned char*)&version, 1);
|
||||||
|
}
|
||||||
|
|
||||||
_glfwPlatformSetWindowTitle(window, wndconfig->title);
|
_glfwPlatformSetWindowTitle(window, wndconfig->title);
|
||||||
|
|
||||||
XRRSelectInput(_glfw.x11.display, window->x11.handle,
|
XRRSelectInput(_glfw.x11.display, window->x11.handle,
|
||||||
@ -494,6 +503,7 @@ static void leaveFullscreenMode(_GLFWwindow* window)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Process the specified X event
|
// Process the specified X event
|
||||||
//
|
//
|
||||||
static void processEvent(XEvent *event)
|
static void processEvent(XEvent *event)
|
||||||
@ -689,10 +699,113 @@ static void processEvent(XEvent *event)
|
|||||||
False,
|
False,
|
||||||
SubstructureNotifyMask | SubstructureRedirectMask,
|
SubstructureNotifyMask | SubstructureRedirectMask,
|
||||||
event);
|
event);
|
||||||
|
|
||||||
|
}
|
||||||
|
else if(event->xclient.message_type == _glfw.x11.XdndEnter)
|
||||||
|
{
|
||||||
|
// Xdnd Enter: the drag&drop event has started in the window,
|
||||||
|
// we could be getting the type and possible conversions here
|
||||||
|
// but since we use always string conversion we don't need
|
||||||
|
// it
|
||||||
|
}
|
||||||
|
else if(event->xclient.message_type == _glfw.x11.XdndDrop)
|
||||||
|
{
|
||||||
|
// Xdnd Drop: The drag&drop event has finished dropping on
|
||||||
|
// the window, ask to convert the selection
|
||||||
|
|
||||||
|
_glfw.x11.xdnd.sourceWindow = event->xclient.data.l[0];
|
||||||
|
|
||||||
|
const Atom formats[] = { _glfw.x11.UTF8_STRING,
|
||||||
|
_glfw.x11.COMPOUND_STRING,
|
||||||
|
XA_STRING };
|
||||||
|
const int formatCount = sizeof(formats) / sizeof(formats[0]);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i=0;i<formatCount;i++){
|
||||||
|
XConvertSelection(_glfw.x11.display,
|
||||||
|
_glfw.x11.XdndSelection,
|
||||||
|
formats[i],
|
||||||
|
_glfw.x11.XdndSelection,
|
||||||
|
window->x11.handle, CurrentTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else if(event->xclient.message_type == _glfw.x11.XdndLeave)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
else if(event->xclient.message_type == _glfw.x11.XdndPosition)
|
||||||
|
{
|
||||||
|
// Xdnd Position: get coordinates of the mouse inside the window
|
||||||
|
// and update the mouse position
|
||||||
|
int absX = (event->xclient.data.l[2]>>16) & 0xFFFF;
|
||||||
|
int absY = (event->xclient.data.l[2]) & 0xFFFF;
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
|
||||||
|
_glfwPlatformGetWindowPos(window,&x,&y);
|
||||||
|
|
||||||
|
_glfwInputCursorMotion(window,absX-x,absY-y);
|
||||||
|
|
||||||
|
// Xdnd: reply with an XDND status message
|
||||||
|
XClientMessageEvent m;
|
||||||
|
memset(&m, sizeof(m), 0);
|
||||||
|
m.type = ClientMessage;
|
||||||
|
m.display = event->xclient.display;
|
||||||
|
m.window = event->xclient.data.l[0];
|
||||||
|
m.message_type = _glfw.x11.XdndStatus;
|
||||||
|
m.format=32;
|
||||||
|
m.data.l[0] = window->x11.handle;
|
||||||
|
m.data.l[1] = 1; // Always accept the dnd with no rectangle
|
||||||
|
m.data.l[2] = 0; // Specify an empty rectangle
|
||||||
|
m.data.l[3] = 0;
|
||||||
|
m.data.l[4] = _glfw.x11.XdndActionCopy; // We only accept copying
|
||||||
|
|
||||||
|
XSendEvent(_glfw.x11.display, event->xclient.data.l[0], False, NoEventMask, (XEvent*)&m);
|
||||||
|
XFlush(_glfw.x11.display);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case SelectionNotify:
|
||||||
|
{
|
||||||
|
|
||||||
|
if(event->xselection.property != None)
|
||||||
|
{
|
||||||
|
// Xdnd: got a selection notification from the conversion
|
||||||
|
// we asked for, get the data and finish the d&d event
|
||||||
|
char* data;
|
||||||
|
_glfwGetWindowProperty(event->xselection.requestor,
|
||||||
|
event->xselection.property,
|
||||||
|
event->xselection.target,
|
||||||
|
(unsigned char**) &data);
|
||||||
|
|
||||||
|
free(_glfw.x11.xdnd.string);
|
||||||
|
_glfw.x11.xdnd.string = strdup(data);
|
||||||
|
|
||||||
|
XClientMessageEvent m;
|
||||||
|
memset(&m, sizeof(m), 0);
|
||||||
|
m.type = ClientMessage;
|
||||||
|
m.display = _glfw.x11.display;
|
||||||
|
m.window = _glfw.x11.xdnd.sourceWindow;
|
||||||
|
m.message_type = _glfw.x11.XdndFinished;
|
||||||
|
m.format=32;
|
||||||
|
m.data.l[0] = window->x11.handle;
|
||||||
|
m.data.l[1] = 1;
|
||||||
|
m.data.l[2] = _glfw.x11.XdndActionCopy; //We only ever copy.
|
||||||
|
|
||||||
|
// Reply that all is well.
|
||||||
|
XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.sourceWindow, False, NoEventMask, (XEvent*)&m);
|
||||||
|
|
||||||
|
XSync(_glfw.x11.display, False);
|
||||||
|
|
||||||
|
XFree(data);
|
||||||
|
|
||||||
|
_glfwInputDrop(window,_glfw.x11.xdnd.string);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case MapNotify:
|
case MapNotify:
|
||||||
{
|
{
|
||||||
@ -883,7 +996,7 @@ unsigned long _glfwGetWindowProperty(Window window,
|
|||||||
&bytesAfter,
|
&bytesAfter,
|
||||||
value);
|
value);
|
||||||
|
|
||||||
if (actualType != type)
|
if (type != AnyPropertyType && actualType != type)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return itemCount;
|
return itemCount;
|
||||||
|
Loading…
Reference in New Issue
Block a user