From 2933c00e54cb39581b0f00966d08a9c94559dcf8 Mon Sep 17 00:00:00 2001 From: Florian Albrechtskirchinger Date: Fri, 17 Jun 2022 12:00:32 +0200 Subject: [PATCH] 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; }