From 8031534c38372ff8313560afafa0abb115797b58 Mon Sep 17 00:00:00 2001 From: Florian Albrechtskirchinger Date: Fri, 10 Jun 2022 22:51:54 +0200 Subject: [PATCH 1/8] Add GLFW_DND_* constants --- include/GLFW/glfw3.h | 22 ++++++++++++++++++++++ src/internal.h | 11 +++++++++++ 2 files changed, 33 insertions(+) diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index 52225c74..29af61cf 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -927,6 +927,9 @@ extern "C" { */ #define GLFW_MOUSE_PASSTHROUGH 0x0002000D +// TODO doc +#define GLFW_DND_DRAGGING 0x0002000E + /*! @brief Framebuffer bit depth hint. * * Framebuffer bit depth [hint](@ref GLFW_RED_BITS). @@ -1302,6 +1305,25 @@ extern "C" { #define GLFW_PLATFORM_NULL 0x00060005 /*! @} */ +// TODO doc +#define GLFW_DND_NONE 0 + +// Drag and drop events +#define GLFW_DND_ENTER 0xdd100001 +#define GLFW_DND_DRAG 0xdd100002 +#define GLFW_DND_LEAVE 0xdd100003 + +// Drag and drop actions +// GLFW_DND_NONE +#define GLFW_DND_COPY 0xdd200001 +#define GLFW_DND_LINK 0xdd200002 +#define GLFW_DND_MOVE 0xdd200004 + +// Drag and drop formats +// GLFW_DND_NONE +#define GLFW_DND_TEXT 0xdd300002 +#define GLFW_DND_PATHS 0xdd300001 + #define GLFW_DONT_CARE -1 diff --git a/src/internal.h b/src/internal.h index 89a18628..5abf6de2 100644 --- a/src/internal.h +++ b/src/internal.h @@ -58,6 +58,17 @@ #define _GLFW_MESSAGE_SIZE 1024 +#define _GLFW_DND_MASK 0x000fffff + +#define _GLFW_DND_TEXT_INDEX 1 +#define _GLFW_DND_PATHS_INDEX 0 +#define _GLFW_DND_FORMAT_COUNT 2 + +#define _GLFW_DND_COPY_INDEX 0 +#define _GLFW_DND_LINK_INDEX 1 +#define _GLFW_DND_MOVE_INDEX 2 +#define _GLFW_DND_ACTION_COUNT 3 + typedef int GLFWbool; typedef void (*GLFWproc)(void); From 519ee688760c1bc7d76e04acc6239c3b667f272a Mon Sep 17 00:00:00 2001 From: Florian Albrechtskirchinger Date: Thu, 16 Jun 2022 15:06:31 +0200 Subject: [PATCH 2/8] Add _glfw_ffs returning 1-based index of lsb set to one --- src/init.c | 35 +++++++++++++++++++++++++++++++++++ src/internal.h | 1 + 2 files changed, 36 insertions(+) diff --git a/src/init.c b/src/init.c index d07a492e..d84c13f6 100644 --- a/src/init.c +++ b/src/init.c @@ -266,6 +266,41 @@ float _glfw_fmaxf(float a, float b) return b; } +int _glfw_ffs(int i) +{ +#if defined(__GNUC__) || defined(__clang__) + return __builtin_ffs(i); +#elif defined(_MSC_VER) + unsigned long n, u = i; + return _BitScanForward(&n, u) ? n + 1 : 0; +#else + // https://en.wikipedia.org/wiki/Find_first_set#CTZ + unsigned int u = i; + int n = 1; + if (!u) return 0; + if (!(u & 0x0000FFFF)) { + n += 16; + u >>= 16; + } + if (!(u & 0x000000FF)) { + n += 8; + u >>= 8; + } + if (!(u & 0x0000000F)) { + n += 4; + u >>= 4; + } + if (!(u & 0x00000003)) { + n += 2; + u >>= 2; + } + if (!(u & 0x00000001)) { + n += 1; + } + return n; +#endif +} + void* _glfw_calloc(size_t count, size_t size) { if (count && size) diff --git a/src/internal.h b/src/internal.h index 5abf6de2..24be9c53 100644 --- a/src/internal.h +++ b/src/internal.h @@ -1015,6 +1015,7 @@ int _glfw_min(int a, int b); int _glfw_max(int a, int b); float _glfw_fminf(float a, float b); float _glfw_fmaxf(float a, float b); +int _glfw_ffs(int i); void* _glfw_calloc(size_t count, size_t size); void* _glfw_realloc(void* pointer, size_t size); From a76293fa407cfad320946bf931b3e981c9181024 Mon Sep 17 00:00:00 2001 From: Florian Albrechtskirchinger Date: Fri, 10 Jun 2022 13:07:54 +0200 Subject: [PATCH 3/8] Add GLFW_DND_DRAGGING window attribute --- src/internal.h | 1 + src/window.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/internal.h b/src/internal.h index 24be9c53..a23c97c0 100644 --- a/src/internal.h +++ b/src/internal.h @@ -541,6 +541,7 @@ struct _GLFWwindow GLFWbool floating; GLFWbool focusOnShow; GLFWbool mousePassthrough; + GLFWbool dndDragging; GLFWbool shouldClose; void* userPointer; GLFWbool doublebuffer; diff --git a/src/window.c b/src/window.c index ebbc6dca..8425c3c1 100644 --- a/src/window.c +++ b/src/window.c @@ -856,6 +856,8 @@ GLFWAPI int glfwGetWindowAttrib(GLFWwindow* handle, int attrib) return window->focusOnShow; case GLFW_MOUSE_PASSTHROUGH: return window->mousePassthrough; + case GLFW_DND_DRAGGING: + return window->dndDragging; case GLFW_TRANSPARENT_FRAMEBUFFER: return _glfw.platform.framebufferTransparent(window); case GLFW_RESIZABLE: From 735f31454e68e5e192fcbf58a4c3a45a13ea0fd3 Mon Sep 17 00:00:00 2001 From: Florian Albrechtskirchinger Date: Fri, 10 Jun 2022 23:01:12 +0200 Subject: [PATCH 4/8] Add extended drag & drop callback functions --- include/GLFW/glfw3.h | 12 ++++++++++++ src/input.c | 36 ++++++++++++++++++++++++++++++++++++ src/internal.h | 4 ++++ 3 files changed, 52 insertions(+) diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index 29af61cf..88a59835 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -1899,6 +1899,9 @@ typedef void (* GLFWcharfun)(GLFWwindow* window, unsigned int codepoint); */ typedef void (* GLFWcharmodsfun)(GLFWwindow* window, unsigned int codepoint, int mods); +// TODO doc +typedef void (* GLFWdragfun)(GLFWwindow* window, int event, double xpos, double ypos, int availableFormats, int *format, int availableActions, int *action); + /*! @brief The function pointer type for path drop callbacks. * * This is the function pointer type for path drop callbacks. A path drop @@ -1923,6 +1926,9 @@ typedef void (* GLFWcharmodsfun)(GLFWwindow* window, unsigned int codepoint, int */ typedef void (* GLFWdropfun)(GLFWwindow* window, int path_count, const char* paths[]); +// TODO doc +typedef void (* GLFWdropfunex)(GLFWwindow* window, int format, int data_count, void *data, int *action); + /*! @brief The function pointer type for monitor configuration callbacks. * * This is the function pointer type for monitor configuration callbacks. @@ -5303,6 +5309,9 @@ GLFWAPI GLFWcursorenterfun glfwSetCursorEnterCallback(GLFWwindow* window, GLFWcu */ GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* window, GLFWscrollfun callback); +// TODO doc +GLFWAPI GLFWdragfun glfwSetDragCallback(GLFWwindow* window, GLFWdragfun callback); + /*! @brief Sets the path drop callback. * * This function sets the path drop callback of the specified window, which is @@ -5340,6 +5349,9 @@ GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* window, GLFWscrollfun ca */ GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* window, GLFWdropfun callback); +// TODO doc +GLFWAPI GLFWdropfunex glfwSetDropCallbackEx(GLFWwindow* window, GLFWdropfunex callback); + /*! @brief Returns whether the specified joystick is present. * * This function returns whether the specified joystick is present. diff --git a/src/input.c b/src/input.c index c2a7a86d..247632af 100644 --- a/src/input.c +++ b/src/input.c @@ -401,6 +401,14 @@ void _glfwInputCursorEnter(_GLFWwindow* window, GLFWbool entered) window->callbacks.cursorEnter((GLFWwindow*) window, entered); } +// Notifies shared code of data being dragged in/over/out of a window +// +void _glfwInputDrag(_GLFWwindow* window, int event, double xpos, double ypos, int availableFormats, int *format, int availableActions, int *action) +{ + if (window->callbacks.drag) + window->callbacks.drag((GLFWwindow*) window, event, xpos, ypos, availableFormats, format, availableActions, action); +} + // Notifies shared code of files or directories dropped on a window // void _glfwInputDrop(_GLFWwindow* window, int count, const char** paths) @@ -413,6 +421,14 @@ void _glfwInputDrop(_GLFWwindow* window, int count, const char** paths) window->callbacks.drop((GLFWwindow*) window, count, paths); } +// Notifies shared code of data dropped on a window +// +void _glfwInputDropEx(_GLFWwindow* window, int format, int data_count, void *data, int *action) +{ + if (window->callbacks.dropex) + window->callbacks.dropex((GLFWwindow*) window, format, data_count, data, action); +} + // Notifies shared code of a joystick connection or disconnection // void _glfwInputJoystick(_GLFWjoystick* js, int event) @@ -1011,6 +1027,16 @@ GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* handle, return cbfun; } +GLFWAPI GLFWdragfun glfwSetDragCallback(GLFWwindow* handle, GLFWdragfun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP(GLFWdragfun, window->callbacks.drag, cbfun); + return cbfun; +} + GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* handle, GLFWdropfun cbfun) { _GLFWwindow* window = (_GLFWwindow*) handle; @@ -1021,6 +1047,16 @@ GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* handle, GLFWdropfun cbfun) return cbfun; } +GLFWAPI GLFWdropfunex glfwSetDropCallbackEx(GLFWwindow* handle, GLFWdropfunex cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP(GLFWdropfunex, window->callbacks.dropex, cbfun); + return cbfun; +} + GLFWAPI int glfwJoystickPresent(int jid) { _GLFWjoystick* js; diff --git a/src/internal.h b/src/internal.h index a23c97c0..1bc9fb72 100644 --- a/src/internal.h +++ b/src/internal.h @@ -582,7 +582,9 @@ struct _GLFWwindow GLFWkeyfun key; GLFWcharfun character; GLFWcharmodsfun charmods; + GLFWdragfun drag; GLFWdropfun drop; + GLFWdropfunex dropex; } callbacks; // This is defined in platform.h @@ -937,7 +939,9 @@ void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset); void _glfwInputMouseClick(_GLFWwindow* window, int button, int action, int mods); void _glfwInputCursorPos(_GLFWwindow* window, double xpos, double ypos); void _glfwInputCursorEnter(_GLFWwindow* window, GLFWbool entered); +void _glfwInputDrag(_GLFWwindow* window, int event, double xpos, double ypos, int availableFormats, int *format, int availableActions, int *action); void _glfwInputDrop(_GLFWwindow* window, int count, const char** names); +void _glfwInputDropEx(_GLFWwindow* window, int format, int data_count, void *data, int *action); void _glfwInputJoystick(_GLFWjoystick* js, int event); void _glfwInputJoystickAxis(_GLFWjoystick* js, int axis, float value); void _glfwInputJoystickButton(_GLFWjoystick* js, int button, char value); From d2763ec2a18407beb0d8650b6f54a6666041115a Mon Sep 17 00:00:00 2001 From: Florian Albrechtskirchinger Date: Fri, 10 Jun 2022 13:24:45 +0200 Subject: [PATCH 5/8] Win32: Implement drag & drop via IDropTarget --- src/win32_init.c | 42 ++++++++++++++ src/win32_platform.h | 47 ++++++++++++++++ src/win32_window.c | 129 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 217 insertions(+), 1 deletion(-) diff --git a/src/win32_init.c b/src/win32_init.c index 8704150c..c2806166 100644 --- a/src/win32_init.c +++ b/src/win32_init.c @@ -167,6 +167,19 @@ static GLFWbool loadLibraries(void) _glfwPlatformGetModuleSymbol(_glfw.win32.ntdll.instance, "RtlVerifyVersionInfo"); } + _glfw.win32.ole32.instance = _glfwPlatformLoadModule("ole32.dll"); + if (_glfw.win32.ole32.instance) + { + _glfw.win32.ole32.OleInitialize_ = (PFN_OleInitialize) + _glfwPlatformGetModuleSymbol(_glfw.win32.ole32.instance, "OleInitialize"); + _glfw.win32.ole32.OleUninitialize_ = (PFN_OleUninitialize) + _glfwPlatformGetModuleSymbol(_glfw.win32.ole32.instance, "OleUninitialize"); + _glfw.win32.ole32.RegisterDragDrop_ = (PFN_RegisterDragDrop) + _glfwPlatformGetModuleSymbol(_glfw.win32.ole32.instance, "RegisterDragDrop"); + _glfw.win32.ole32.RevokeDragDrop_ = (PFN_RevokeDragDrop) + _glfwPlatformGetModuleSymbol(_glfw.win32.ole32.instance, "RevokeDragDrop"); + } + return GLFW_TRUE; } @@ -191,6 +204,9 @@ static void freeLibraries(void) if (_glfw.win32.ntdll.instance) _glfwPlatformFreeModule(_glfw.win32.ntdll.instance); + + if (_glfw.win32.ole32.instance) + _glfwPlatformFreeModule(_glfw.win32.ole32.instance); } // Create key code translation tables @@ -430,6 +446,27 @@ static GLFWbool createHelperWindow(void) return GLFW_TRUE; } +// Initialize OLE as well as the IDropTarget vtable +// +static GLFWbool initDragDrop() +{ + if (FAILED(OleInitialize(NULL))) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to initialize OLE"); + return GLFW_FALSE; + } + + //_glfw.win32.dropTargetVtbl.QueryInterface = _glfwDropTarget_QueryInterface; + _glfw.win32.dropTargetVtbl.AddRef = (PFN_IDropTarget_AddRef)_glfwDropTarget_AddRef; + _glfw.win32.dropTargetVtbl.Release = (PFN_IDropTarget_Release)_glfwDropTarget_Release; + _glfw.win32.dropTargetVtbl.DragEnter = (PFN_IDropTarget_DragEnter)_glfwDropTarget_DragEnter; + _glfw.win32.dropTargetVtbl.DragOver = (PFN_IDropTarget_DragOver)_glfwDropTarget_DragOver; + _glfw.win32.dropTargetVtbl.DragLeave = (PFN_IDropTarget_DragLeave)_glfwDropTarget_DragLeave; + _glfw.win32.dropTargetVtbl.Drop = (PFN_IDropTarget_Drop)_glfwDropTarget_Drop; + + return GLFW_TRUE; +} ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// @@ -699,6 +736,9 @@ int _glfwInitWin32(void) if (!createHelperWindow()) return GLFW_FALSE; + if (!initDragDrop()) + return GLFW_FALSE; + _glfwPollMonitorsWin32(); return GLFW_TRUE; } @@ -708,6 +748,8 @@ void _glfwTerminateWin32(void) if (_glfw.win32.deviceNotificationHandle) UnregisterDeviceNotification(_glfw.win32.deviceNotificationHandle); + OleUninitialize(); + if (_glfw.win32.helperWindowHandle) DestroyWindow(_glfw.win32.helperWindowHandle); if (_glfw.win32.helperWindowClass) diff --git a/src/win32_platform.h b/src/win32_platform.h index c2158943..f91b0424 100644 --- a/src/win32_platform.h +++ b/src/win32_platform.h @@ -64,11 +64,15 @@ // GLFW uses OEM cursor resources #define OEMRESOURCE +// define COM C object macros +#define COBJMACROS + #include #include #include #include #include +#include // HACK: Define macros that some windows.h variants don't #ifndef WM_MOUSEHWHEEL @@ -316,6 +320,22 @@ typedef HRESULT (WINAPI * PFN_GetDpiForMonitor)(HMONITOR,MONITOR_DPI_TYPE,UINT*, typedef LONG (WINAPI * PFN_RtlVerifyVersionInfo)(OSVERSIONINFOEXW*,ULONG,ULONGLONG); #define RtlVerifyVersionInfo _glfw.win32.ntdll.RtlVerifyVersionInfo_ +// ole32.dll function pointer typedefs +typedef HRESULT (WINAPI * PFN_OleInitialize)(LPVOID); +typedef VOID (WINAPI * PFN_OleUninitialize)(VOID); +typedef HRESULT (WINAPI * PFN_RegisterDragDrop)(HWND,LPDROPTARGET); +typedef HRESULT (WINAPI * PFN_RevokeDragDrop)(HWND); +typedef ULONG (STDMETHODCALLTYPE * PFN_IDropTarget_AddRef)(LPDROPTARGET); +typedef ULONG (STDMETHODCALLTYPE * PFN_IDropTarget_Release)(LPDROPTARGET); +typedef HRESULT (STDMETHODCALLTYPE * PFN_IDropTarget_DragEnter)(LPDROPTARGET,IDataObject*,DWORD,POINTL,DWORD*); +typedef HRESULT (STDMETHODCALLTYPE * PFN_IDropTarget_DragOver)(LPDROPTARGET,DWORD,POINTL,DWORD*); +typedef HRESULT (STDMETHODCALLTYPE * PFN_IDropTarget_DragLeave)(LPDROPTARGET); +typedef HRESULT (STDMETHODCALLTYPE * PFN_IDropTarget_Drop)(LPDROPTARGET,IDataObject*,DWORD,POINTL,DWORD*); +#define OleInitialize _glfw.win32.ole32.OleInitialize_ +#define OleUninitialize _glfw.win32.ole32.OleUninitialize_ +#define RegisterDragDrop _glfw.win32.ole32.RegisterDragDrop_ +#define RevokeDragDrop _glfw.win32.ole32.RevokeDragDrop_ + // WGL extension pointer typedefs typedef BOOL (WINAPI * PFNWGLSWAPINTERVALEXTPROC)(int); typedef BOOL (WINAPI * PFNWGLGETPIXELFORMATATTRIBIVARBPROC)(HDC,int,int,UINT,const int*,int*); @@ -408,6 +428,13 @@ typedef struct _GLFWlibraryWGL GLFWbool ARB_context_flush_control; } _GLFWlibraryWGL; +typedef struct _GLFWdropTarget +{ + IDropTargetVtbl* lpVtbl; + ULONG cRefCount; + _GLFWwindow* window; +} _GLFWdropTarget; + // Win32-specific per-window data // typedef struct _GLFWwindowWin32 @@ -432,6 +459,8 @@ typedef struct _GLFWwindowWin32 int lastCursorPosX, lastCursorPosY; // The last received high surrogate when decoding pairs of UTF-16 messages WCHAR highSurrogate; + + _GLFWdropTarget dropTarget; } _GLFWwindowWin32; // Win32-specific global data @@ -455,6 +484,7 @@ typedef struct _GLFWlibraryWin32 RAWINPUT* rawInput; int rawInputSize; UINT mouseTrailSize; + IDropTargetVtbl dropTargetVtbl; struct { HINSTANCE instance; @@ -497,6 +527,15 @@ typedef struct _GLFWlibraryWin32 HINSTANCE instance; PFN_RtlVerifyVersionInfo RtlVerifyVersionInfo_; } ntdll; + + struct { + HINSTANCE instance; + + PFN_OleInitialize OleInitialize_; + PFN_OleUninitialize OleUninitialize_; + PFN_RegisterDragDrop RegisterDragDrop_; + PFN_RevokeDragDrop RevokeDragDrop_; + } ole32; } _GLFWlibraryWin32; // Win32-specific per-monitor data @@ -620,3 +659,11 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig); +// IDropTarget vtable functions +//HRESULT STDMETHODCALLTYPE _glfwDropTarget_QueryInterface(_GLFWdropTarget *This, REFIID riid, void **ppvObject); +ULONG STDMETHODCALLTYPE _glfwDropTarget_AddRef(_GLFWdropTarget *This); +ULONG STDMETHODCALLTYPE _glfwDropTarget_Release(_GLFWdropTarget *This); +HRESULT STDMETHODCALLTYPE _glfwDropTarget_DragEnter(_GLFWdropTarget *This, IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect); +HRESULT STDMETHODCALLTYPE _glfwDropTarget_DragOver(_GLFWdropTarget *This, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect); +HRESULT STDMETHODCALLTYPE _glfwDropTarget_DragLeave(_GLFWdropTarget *This); +HRESULT STDMETHODCALLTYPE _glfwDropTarget_Drop(_GLFWdropTarget *This, IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect); diff --git a/src/win32_window.c b/src/win32_window.c index 8d9c95e7..22df2b38 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -30,6 +30,7 @@ #include "internal.h" #include +#include // TODO remove #include #include #include @@ -1407,7 +1408,15 @@ static int createNativeWindow(_GLFWwindow* window, } } - DragAcceptFiles(window->win32.handle, TRUE); + //DragAcceptFiles(window->win32.handle, TRUE); + window->win32.dropTarget.lpVtbl = &_glfw.win32.dropTargetVtbl; + window->win32.dropTarget.window = window; + if (FAILED(RegisterDragDrop(window->win32.handle, (LPDROPTARGET)&window->win32.dropTarget))) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to register window for drag & drop"); + return GLFW_FALSE; + } if (fbconfig->transparent) { @@ -1493,6 +1502,8 @@ void _glfwDestroyWindowWin32(_GLFWwindow* window) if (_glfw.win32.disabledCursorWindow == window) _glfw.win32.disabledCursorWindow = NULL; + RevokeDragDrop(window->win32.handle); + if (window->win32.handle) { RemovePropW(window->win32.handle, L"GLFW"); @@ -2452,3 +2463,119 @@ GLFWAPI HWND glfwGetWin32Window(GLFWwindow* handle) return window->win32.handle; } +/*static GLFWbool _glfwIsEqualIID(REFIID riid1, REFIID riid2) +{ + return riid1->Data1 == riid2->Data1 + && riid1->Data2 == riid2->Data2 + && riid1->Data3 == riid2->Data3 + && (memcmp(riid1->Data4, riid2->Data4, sizeof(riid1->Data4)) == 0); +} + +// IDropTarget vtable functions +HRESULT STDMETHODCALLTYPE _glfwDropTarget_QueryInterface(_GLFWdropTarget *This, REFIID riid, void **ppvObject) +{ + printf("DropTarget_QueryInterface\n"); + if(!ppvObject) + return E_POINTER; + + if (_glfwIsEqualIID(riid, &IID_IUnknown) || _glfwIsEqualIID(riid, &IID_IDropTarget)) + { + *ppvObject = This; + This->lpVtbl->AddRef(This); + return S_OK; + } + + *ppvObject = 0; + return E_NOINTERFACE; +}*/ + +// IDropTarget vtable functions +ULONG STDMETHODCALLTYPE _glfwDropTarget_AddRef(_GLFWdropTarget *This) +{ + printf("DropTarget_AddRef\n"); + return ++This->cRefCount; +} + +ULONG STDMETHODCALLTYPE _glfwDropTarget_Release(_GLFWdropTarget *This) +{ + printf("DropTarget_Release\n"); + return --This->cRefCount; +} + +HRESULT STDMETHODCALLTYPE _glfwDropTarget_DragEnter(_GLFWdropTarget *This, IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) +{ + FORMATETC fmt = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; + + printf("DropTarget_DragEnter\n"); + if(SUCCEEDED(IDataObject_QueryGetData(pDataObj, &fmt))) + { + printf("QueryGetData OK\n"); + *pdwEffect = DROPEFFECT_COPY; + } + else + *pdwEffect = DROPEFFECT_NONE; + + return S_OK; +} + +HRESULT STDMETHODCALLTYPE _glfwDropTarget_DragOver(_GLFWdropTarget *This, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) +{ + printf("DropTarget_DragOver\n"); + *pdwEffect = DROPEFFECT_COPY; + + return S_OK; +} + +HRESULT STDMETHODCALLTYPE _glfwDropTarget_DragLeave(_GLFWdropTarget *This) +{ + printf("DropTarget_DragLeave\n"); + return S_OK; +} + +HRESULT STDMETHODCALLTYPE _glfwDropTarget_Drop(_GLFWdropTarget *This, IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) +{ + FORMATETC fmt = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; + STGMEDIUM stgmed; + + printf("DropTarget_Drop\n"); + *pdwEffect = DROPEFFECT_NONE; + + if(SUCCEEDED(IDataObject_QueryGetData(pDataObj, &fmt))) + { + printf("QueryGetData OK\n"); + if(SUCCEEDED(IDataObject_GetData(pDataObj, &fmt, &stgmed))) + { + HDROP drop = (HDROP) GlobalLock(stgmed.hGlobal); + const int count = DragQueryFileW(drop, 0xffffffff, NULL, 0); + char** paths = _glfw_calloc(count, sizeof(char*)); + int i; + + printf("GetData OK\n"); + _glfwInputCursorPos(This->window, pt.x, pt.y); + + for (i = 0; i < count; ++i) + { + const UINT length = DragQueryFileW(drop, i, NULL, 0); + WCHAR* buffer = _glfw_calloc((size_t) length + 1, sizeof(WCHAR)); + + DragQueryFileW(drop, i, buffer, length + 1); + paths[i] = _glfwCreateUTF8FromWideStringWin32(buffer); + + _glfw_free(buffer); + } + + _glfwInputDrop(This->window, count, (const char**) paths); + + for (i = 0; i < count; ++i) + _glfw_free(paths[i]); + _glfw_free(paths); + + GlobalUnlock(stgmed.hGlobal); + ReleaseStgMedium(&stgmed); + + *pdwEffect = DROPEFFECT_COPY; + } + } + + return S_OK; +} From 2933c00e54cb39581b0f00966d08a9c94559dcf8 Mon Sep 17 00:00:00 2001 From: Florian Albrechtskirchinger Date: Fri, 17 Jun 2022 12:00:32 +0200 Subject: [PATCH 6/8] Win32: Implement extended drag & drop API --- src/win32_init.c | 12 +-- src/win32_platform.h | 31 ++++--- src/win32_window.c | 201 +++++++++++++++++++++++++++++++++---------- 3 files changed, 178 insertions(+), 66 deletions(-) diff --git a/src/win32_init.c b/src/win32_init.c index c2806166..9a289e80 100644 --- a/src/win32_init.c +++ b/src/win32_init.c @@ -458,12 +458,12 @@ static GLFWbool initDragDrop() } //_glfw.win32.dropTargetVtbl.QueryInterface = _glfwDropTarget_QueryInterface; - _glfw.win32.dropTargetVtbl.AddRef = (PFN_IDropTarget_AddRef)_glfwDropTarget_AddRef; - _glfw.win32.dropTargetVtbl.Release = (PFN_IDropTarget_Release)_glfwDropTarget_Release; - _glfw.win32.dropTargetVtbl.DragEnter = (PFN_IDropTarget_DragEnter)_glfwDropTarget_DragEnter; - _glfw.win32.dropTargetVtbl.DragOver = (PFN_IDropTarget_DragOver)_glfwDropTarget_DragOver; - _glfw.win32.dropTargetVtbl.DragLeave = (PFN_IDropTarget_DragLeave)_glfwDropTarget_DragLeave; - _glfw.win32.dropTargetVtbl.Drop = (PFN_IDropTarget_Drop)_glfwDropTarget_Drop; + _glfw.win32.dropTargetVtbl.AddRef = _glfwDropTarget_AddRef; + _glfw.win32.dropTargetVtbl.Release = _glfwDropTarget_Release; + _glfw.win32.dropTargetVtbl.DragEnter = _glfwDropTarget_DragEnter; + _glfw.win32.dropTargetVtbl.DragOver = _glfwDropTarget_DragOver; + _glfw.win32.dropTargetVtbl.DragLeave = _glfwDropTarget_DragLeave; + _glfw.win32.dropTargetVtbl.Drop = _glfwDropTarget_Drop; return GLFW_TRUE; } diff --git a/src/win32_platform.h b/src/win32_platform.h index f91b0424..e732c52d 100644 --- a/src/win32_platform.h +++ b/src/win32_platform.h @@ -325,12 +325,6 @@ typedef HRESULT (WINAPI * PFN_OleInitialize)(LPVOID); typedef VOID (WINAPI * PFN_OleUninitialize)(VOID); typedef HRESULT (WINAPI * PFN_RegisterDragDrop)(HWND,LPDROPTARGET); typedef HRESULT (WINAPI * PFN_RevokeDragDrop)(HWND); -typedef ULONG (STDMETHODCALLTYPE * PFN_IDropTarget_AddRef)(LPDROPTARGET); -typedef ULONG (STDMETHODCALLTYPE * PFN_IDropTarget_Release)(LPDROPTARGET); -typedef HRESULT (STDMETHODCALLTYPE * PFN_IDropTarget_DragEnter)(LPDROPTARGET,IDataObject*,DWORD,POINTL,DWORD*); -typedef HRESULT (STDMETHODCALLTYPE * PFN_IDropTarget_DragOver)(LPDROPTARGET,DWORD,POINTL,DWORD*); -typedef HRESULT (STDMETHODCALLTYPE * PFN_IDropTarget_DragLeave)(LPDROPTARGET); -typedef HRESULT (STDMETHODCALLTYPE * PFN_IDropTarget_Drop)(LPDROPTARGET,IDataObject*,DWORD,POINTL,DWORD*); #define OleInitialize _glfw.win32.ole32.OleInitialize_ #define OleUninitialize _glfw.win32.ole32.OleUninitialize_ #define RegisterDragDrop _glfw.win32.ole32.RegisterDragDrop_ @@ -428,12 +422,17 @@ typedef struct _GLFWlibraryWGL GLFWbool ARB_context_flush_control; } _GLFWlibraryWGL; -typedef struct _GLFWdropTarget +typedef struct _GLFWdroptarget { IDropTargetVtbl* lpVtbl; ULONG cRefCount; _GLFWwindow* window; -} _GLFWdropTarget; + int availableFormats; + int chosenFormat; + int availableActions; + int proposedAction; + int chosenAction; +} _GLFWdroptarget; // Win32-specific per-window data // @@ -460,7 +459,7 @@ typedef struct _GLFWwindowWin32 // The last received high surrogate when decoding pairs of UTF-16 messages WCHAR highSurrogate; - _GLFWdropTarget dropTarget; + _GLFWdroptarget dropTarget; } _GLFWwindowWin32; // Win32-specific global data @@ -660,10 +659,10 @@ GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, const _GLFWfbconfig* fbconfig); // IDropTarget vtable functions -//HRESULT STDMETHODCALLTYPE _glfwDropTarget_QueryInterface(_GLFWdropTarget *This, REFIID riid, void **ppvObject); -ULONG STDMETHODCALLTYPE _glfwDropTarget_AddRef(_GLFWdropTarget *This); -ULONG STDMETHODCALLTYPE _glfwDropTarget_Release(_GLFWdropTarget *This); -HRESULT STDMETHODCALLTYPE _glfwDropTarget_DragEnter(_GLFWdropTarget *This, IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect); -HRESULT STDMETHODCALLTYPE _glfwDropTarget_DragOver(_GLFWdropTarget *This, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect); -HRESULT STDMETHODCALLTYPE _glfwDropTarget_DragLeave(_GLFWdropTarget *This); -HRESULT STDMETHODCALLTYPE _glfwDropTarget_Drop(_GLFWdropTarget *This, IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect); +//HRESULT STDMETHODCALLTYPE _glfwDropTarget_QueryInterface(IDropTarget *_This, REFIID riid, void **ppvObject); +ULONG STDMETHODCALLTYPE _glfwDropTarget_AddRef(IDropTarget *_This); +ULONG STDMETHODCALLTYPE _glfwDropTarget_Release(IDropTarget *_This); +HRESULT STDMETHODCALLTYPE _glfwDropTarget_DragEnter(IDropTarget *_This, IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect); +HRESULT STDMETHODCALLTYPE _glfwDropTarget_DragOver(IDropTarget *_This, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect); +HRESULT STDMETHODCALLTYPE _glfwDropTarget_DragLeave(IDropTarget *_This); +HRESULT STDMETHODCALLTYPE _glfwDropTarget_Drop(IDropTarget *_This, IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect); diff --git a/src/win32_window.c b/src/win32_window.c index 22df2b38..893b65d9 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -2472,8 +2472,9 @@ GLFWAPI HWND glfwGetWin32Window(GLFWwindow* handle) } // IDropTarget vtable functions -HRESULT STDMETHODCALLTYPE _glfwDropTarget_QueryInterface(_GLFWdropTarget *This, REFIID riid, void **ppvObject) +HRESULT STDMETHODCALLTYPE _glfwDropTarget_QueryInterface(IDropTarget *_This, REFIID riid, void **ppvObject) { + _GLFWdroptarget *This = (_GLFWdroptarget*) _This; printf("DropTarget_QueryInterface\n"); if(!ppvObject) return E_POINTER; @@ -2489,93 +2490,205 @@ HRESULT STDMETHODCALLTYPE _glfwDropTarget_QueryInterface(_GLFWdropTarget *This, return E_NOINTERFACE; }*/ -// IDropTarget vtable functions -ULONG STDMETHODCALLTYPE _glfwDropTarget_AddRef(_GLFWdropTarget *This) +// IDropTarget utility functions +static void getActions(_GLFWdroptarget *This, DWORD dwEffect) { + This->availableActions = GLFW_DND_NONE; + This->proposedAction = GLFW_DND_NONE; + if ((dwEffect & DROPEFFECT_COPY) == DROPEFFECT_COPY) + { + This->availableActions |= GLFW_DND_COPY; + if (!This->proposedAction) + This->proposedAction = GLFW_DND_COPY; + } + if ((dwEffect & DROPEFFECT_LINK) == DROPEFFECT_LINK) + { + This->availableActions |= GLFW_DND_LINK; + if (!This->proposedAction) + This->proposedAction = GLFW_DND_LINK; + } + if ((dwEffect & DROPEFFECT_MOVE) == DROPEFFECT_MOVE) + { + This->availableActions |= GLFW_DND_MOVE; + if (!This->proposedAction) + This->proposedAction = GLFW_DND_MOVE; + } +} + +static void setActions(_GLFWdroptarget *This, DWORD *pdwEffect) +{ + if((This->chosenAction & GLFW_DND_COPY) == GLFW_DND_COPY) + *pdwEffect = DROPEFFECT_COPY; + else if((This->chosenAction & GLFW_DND_LINK) == GLFW_DND_LINK) + *pdwEffect = DROPEFFECT_LINK; + else if((This->chosenAction & GLFW_DND_MOVE) == GLFW_DND_MOVE) + *pdwEffect = DROPEFFECT_MOVE; + else + *pdwEffect = DROPEFFECT_NONE; +} + +static FORMATETC *withClipFormat(FORMATETC *pFmt, CLIPFORMAT cf) +{ + pFmt->cfFormat = cf; + return pFmt; +} + +// IDropTarget vtable functions +ULONG STDMETHODCALLTYPE _glfwDropTarget_AddRef(IDropTarget *_This) +{ + _GLFWdroptarget *This = (_GLFWdroptarget*) _This; printf("DropTarget_AddRef\n"); return ++This->cRefCount; } -ULONG STDMETHODCALLTYPE _glfwDropTarget_Release(_GLFWdropTarget *This) +ULONG STDMETHODCALLTYPE _glfwDropTarget_Release(IDropTarget *_This) { + _GLFWdroptarget *This = (_GLFWdroptarget*) _This; printf("DropTarget_Release\n"); return --This->cRefCount; } -HRESULT STDMETHODCALLTYPE _glfwDropTarget_DragEnter(_GLFWdropTarget *This, IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) +HRESULT STDMETHODCALLTYPE _glfwDropTarget_DragEnter(IDropTarget *_This, IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) { - FORMATETC fmt = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; + _GLFWdroptarget *This = (_GLFWdroptarget*) _This; + FORMATETC fmt = { 0, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; + STGMEDIUM stgmed; printf("DropTarget_DragEnter\n"); - if(SUCCEEDED(IDataObject_QueryGetData(pDataObj, &fmt))) + + getActions(This, *pdwEffect); + + This->availableFormats = GLFW_DND_NONE; + + if (SUCCEEDED(IDataObject_QueryGetData(pDataObj, withClipFormat(&fmt, CF_HDROP))) + && SUCCEEDED(IDataObject_GetData(pDataObj, &fmt, &stgmed))) { - printf("QueryGetData OK\n"); - *pdwEffect = DROPEFFECT_COPY; + This->availableFormats |= GLFW_DND_PATHS; + printf("paths\n"); } - else - *pdwEffect = DROPEFFECT_NONE; + if (SUCCEEDED(IDataObject_QueryGetData(pDataObj, withClipFormat(&fmt, CF_TEXT))) + && SUCCEEDED(IDataObject_GetData(pDataObj, &fmt, &stgmed))) + { + This->availableFormats |= GLFW_DND_TEXT; + printf("text\n"); + ReleaseStgMedium(&stgmed); + } + + This->window->dndDragging = GLFW_TRUE; + + This->chosenFormat = This->availableFormats; + This->chosenAction = This->proposedAction; + _glfwInputDrag(This->window, GLFW_DND_ENTER, pt.x, pt.y, + This->availableFormats, + &This->chosenFormat, + This->availableActions, + &This->chosenAction); return S_OK; } -HRESULT STDMETHODCALLTYPE _glfwDropTarget_DragOver(_GLFWdropTarget *This, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) +HRESULT STDMETHODCALLTYPE _glfwDropTarget_DragOver(IDropTarget *_This, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) { + _GLFWdroptarget *This = (_GLFWdroptarget*) _This; printf("DropTarget_DragOver\n"); - *pdwEffect = DROPEFFECT_COPY; + + getActions(This, *pdwEffect); + + _glfwInputCursorPos(This->window, pt.x, pt.y); + + This->chosenAction = This->proposedAction; + _glfwInputDrag(This->window, GLFW_DND_DRAG, pt.x, pt.y, + This->availableFormats, + &This->chosenFormat, + This->availableActions, + &This->chosenAction); + + setActions(This, pdwEffect); return S_OK; } -HRESULT STDMETHODCALLTYPE _glfwDropTarget_DragLeave(_GLFWdropTarget *This) +HRESULT STDMETHODCALLTYPE _glfwDropTarget_DragLeave(IDropTarget *_This) { + _GLFWdroptarget *This = (_GLFWdroptarget*) _This; printf("DropTarget_DragLeave\n"); + + This->window->dndDragging = GLFW_FALSE; + + This->chosenFormat = + This->availableFormats = GLFW_DND_NONE; + This->chosenAction = + This->proposedAction = + This->availableActions = GLFW_DND_NONE; + _glfwInputDrag(This->window, GLFW_DND_LEAVE, 0.0, 0.0, + This->availableFormats, + &This->chosenFormat, + This->availableActions, + &This->chosenAction); + return S_OK; } -HRESULT STDMETHODCALLTYPE _glfwDropTarget_Drop(_GLFWdropTarget *This, IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) +HRESULT STDMETHODCALLTYPE _glfwDropTarget_Drop(IDropTarget *_This, IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) { - FORMATETC fmt = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; + _GLFWdroptarget *This = (_GLFWdroptarget*) _This; + FORMATETC fmt = { 0, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; STGMEDIUM stgmed; printf("DropTarget_Drop\n"); *pdwEffect = DROPEFFECT_NONE; - if(SUCCEEDED(IDataObject_QueryGetData(pDataObj, &fmt))) - { - printf("QueryGetData OK\n"); - if(SUCCEEDED(IDataObject_GetData(pDataObj, &fmt, &stgmed))) - { - HDROP drop = (HDROP) GlobalLock(stgmed.hGlobal); - const int count = DragQueryFileW(drop, 0xffffffff, NULL, 0); - char** paths = _glfw_calloc(count, sizeof(char*)); - int i; + This->window->dndDragging = GLFW_FALSE; - printf("GetData OK\n"); - _glfwInputCursorPos(This->window, pt.x, pt.y); + if ((This->chosenFormat & GLFW_DND_PATHS) == GLFW_DND_PATHS + && SUCCEEDED(IDataObject_GetData(pDataObj, withClipFormat(&fmt, CF_HDROP), &stgmed))) + { + HDROP drop = (HDROP) GlobalLock(stgmed.hGlobal); + const int count = DragQueryFileW(drop, 0xffffffff, NULL, 0); + char** paths = _glfw_calloc(count, sizeof(char*)); + int i; - for (i = 0; i < count; ++i) - { - const UINT length = DragQueryFileW(drop, i, NULL, 0); - WCHAR* buffer = _glfw_calloc((size_t) length + 1, sizeof(WCHAR)); + printf("GetData OK (paths)\n"); + _glfwInputCursorPos(This->window, pt.x, pt.y); - DragQueryFileW(drop, i, buffer, length + 1); - paths[i] = _glfwCreateUTF8FromWideStringWin32(buffer); + for (i = 0; i < count; ++i) + { + const UINT length = DragQueryFileW(drop, i, NULL, 0); + WCHAR* buffer = _glfw_calloc((size_t) length + 1, sizeof(WCHAR)); - _glfw_free(buffer); - } + DragQueryFileW(drop, i, buffer, length + 1); + paths[i] = _glfwCreateUTF8FromWideStringWin32(buffer); - _glfwInputDrop(This->window, count, (const char**) paths); + _glfw_free(buffer); + } - for (i = 0; i < count; ++i) - _glfw_free(paths[i]); - _glfw_free(paths); + _glfwInputDrop(This->window, count, (const char**) paths); - GlobalUnlock(stgmed.hGlobal); - ReleaseStgMedium(&stgmed); + _glfwInputDropEx(This->window, GLFW_DND_PATHS, count, paths, + &This->chosenAction); - *pdwEffect = DROPEFFECT_COPY; - } - } + for (i = 0; i < count; ++i) + _glfw_free(paths[i]); + _glfw_free(paths); + } + else if ((This->chosenFormat & GLFW_DND_TEXT) == GLFW_DND_TEXT + && SUCCEEDED(IDataObject_GetData(pDataObj, withClipFormat(&fmt, CF_TEXT), &stgmed))) + { + char *text = (char*) GlobalLock(stgmed.hGlobal); + + printf("GetData OK (text)\n"); + _glfwInputCursorPos(This->window, pt.x, pt.y); + + _glfwInputDropEx(This->window, GLFW_DND_TEXT, -1, text, + &This->chosenAction); + } + else + return S_OK; + + GlobalUnlock(stgmed.hGlobal); + ReleaseStgMedium(&stgmed); + + setActions(This, pdwEffect); return S_OK; } From 0a757737d317fc9db78e2dd21e74740a37ab4ac5 Mon Sep 17 00:00:00 2001 From: Florian Albrechtskirchinger Date: Fri, 10 Jun 2022 13:30:12 +0200 Subject: [PATCH 7/8] X11: Implement extended drag & drop API --- src/x11_init.c | 21 +++++-- src/x11_platform.h | 25 ++++++-- src/x11_window.c | 146 ++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 165 insertions(+), 27 deletions(-) diff --git a/src/x11_init.c b/src/x11_init.c index 11aeb9e5..36450c17 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -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 diff --git a/src/x11_platform.h b/src/x11_platform.h index ecaa0fa4..8018c706 100644 --- a/src/x11_platform.h +++ b/src/x11_platform.h @@ -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 { diff --git a/src/x11_window.c b/src/x11_window.c index 98f990b2..316327f3 100644 --- a/src/x11_window.c +++ b/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); From d010d61f87540880ce2e9e4b2053e1eded635fc9 Mon Sep 17 00:00:00 2001 From: Florian Albrechtskirchinger Date: Fri, 10 Jun 2022 13:33:57 +0200 Subject: [PATCH 8/8] [DELETE ME] dev/test code --- examples/triangle-opengl.c | 87 +++++++++++++++++++++++++++++++++++++- 1 file changed, 86 insertions(+), 1 deletion(-) diff --git a/examples/triangle-opengl.c b/examples/triangle-opengl.c index ff9e7d3b..05d9b3dd 100644 --- a/examples/triangle-opengl.c +++ b/examples/triangle-opengl.c @@ -80,8 +80,89 @@ static void key_callback(GLFWwindow* window, int key, int scancode, int action, glfwSetWindowShouldClose(window, GLFW_TRUE); } +void drag_callback(GLFWwindow* window, int event, double xpos, double ypos, int availableFormats, int *format, int availableActions, int *action) +{ + int width, height; + glfwGetWindowSize(window, &width, &height); + switch(event) + { + case GLFW_DND_ENTER: + { + printf("ENTER (%d, %d)\n", (int)xpos, (int)ypos); + break; + } + case GLFW_DND_DRAG: + { + printf("DRAG (%d, %d)\n", (int)xpos, (int)ypos); + + if ((int) xpos < (width / 2)) + { + if ((availableFormats & GLFW_DND_PATHS) == GLFW_DND_PATHS) + { + printf("accepting paths\n"); + *format = GLFW_DND_PATHS; + } + else + { + printf("reject\n"); + *action = GLFW_DND_NONE; + } + } + else { + if ((availableFormats & GLFW_DND_TEXT) == GLFW_DND_TEXT) + { + printf("accepting text\n"); + *format = GLFW_DND_TEXT; + } + else + { + printf("reject\n"); + *action = GLFW_DND_NONE; + } + } + + break; + } + case GLFW_DND_LEAVE: + { + printf("LEAVE\n"); + break; + } + } +} + +void drop_callback(GLFWwindow* window, int count, const char** paths) +{ + for (int i = 0; i < count; ++i) + printf("path[%d]: %s\n", i, paths[i]); +} + +void dropex_callback(GLFWwindow* window, int format, int data_count, void *data, int *action) +{ + printf("DROP\n"); + + switch(format) + { + case GLFW_DND_PATHS: + { + const char **paths = (const char **) data; + for(int i = 0; i < data_count; ++i) + printf("path[%d]: %s\n", i, paths[i]); + break; + } + case GLFW_DND_TEXT: + printf("text: %s\n", (const char *) data); + break; + default: + break; + } +} + int main(void) { + (void)setvbuf(stdout, NULL, _IONBF, 0); + (void)setvbuf(stderr, NULL, _IONBF, 0); + glfwSetErrorCallback(error_callback); if (!glfwInit()) @@ -99,6 +180,9 @@ int main(void) } glfwSetKeyCallback(window, key_callback); + glfwSetDragCallback(window, drag_callback); + glfwSetDropCallback(window, drop_callback); + glfwSetDropCallbackEx(window, dropex_callback); glfwMakeContextCurrent(window); gladLoadGL(glfwGetProcAddress); @@ -149,7 +233,8 @@ int main(void) mat4x4 m, p, mvp; mat4x4_identity(m); - mat4x4_rotate_Z(m, m, (float) glfwGetTime()); + if (glfwGetWindowAttrib(window, GLFW_DND_DRAGGING)) + mat4x4_rotate_Z(m, m, (float) glfwGetTime()); mat4x4_ortho(p, -ratio, ratio, -1.f, 1.f, 1.f, -1.f); mat4x4_mul(mvp, p, m);