Browse Source

Support SDL_EVENT_DROP_FILE in Windows with IDropTarget instead of WM_DROPFILES
Support SDL_EVENT_DROP_TEXT in Windows

src/video/windows/SDL_windowsvideo.c + .h
Connect to COM WIN_CoInitialize + OLE OleInitialize in WIN_VideoInit
Disconnect from COM WIN_CoUninitialize + OLE OleUninitialize in WIN_VideoQuit
src/video/windows/SDL_windowswindow.c + .h
Create / Destroy IDropTarget or use fallback WM_DROPFILES
depending on OleInitialize success in WIN_VideoInit
Handle text/uri-list, text/plain;charset=utf-8, CF_UNICODE_TEXT, CF_TEXT, CF_HDROP
Call terminating WIN_AcceptDragAndDrop from WIN_DestroyWindow ( CleanupVideoData )

Dragon-Baroque 1 year ago
parent
commit
808c312b2a

+ 26 - 0
src/video/windows/SDL_windowsvideo.c

@@ -467,6 +467,21 @@ static void WIN_InitDPIAwareness(SDL_VideoDevice *_this)
 int WIN_VideoInit(SDL_VideoDevice *_this)
 {
     SDL_VideoData *data = _this->internal;
+    HRESULT hr;
+
+    hr = WIN_CoInitialize();
+    if (SUCCEEDED(hr)) {
+        data->coinitialized = SDL_TRUE;
+
+        hr = OleInitialize(NULL);
+        if (SUCCEEDED(hr)) {
+            data->oleinitialized = SDL_TRUE;
+        } else {
+            SDL_LogInfo(SDL_LOG_CATEGORY_VIDEO, "OleInitialize() failed: 0x%.8x, using fallback drag-n-drop functionality\n", (unsigned int)hr);
+        }
+    } else {
+        SDL_LogInfo(SDL_LOG_CATEGORY_VIDEO, "CoInitialize() failed: 0x%.8x, using fallback drag-n-drop functionality\n", (unsigned int)hr);
+    }
 
     WIN_InitDPIAwareness(_this);
 
@@ -511,6 +526,8 @@ int WIN_VideoInit(SDL_VideoDevice *_this)
 
 void WIN_VideoQuit(SDL_VideoDevice *_this)
 {
+    SDL_VideoData *data = _this->internal;
+
 #if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
     WIN_QuitModes(_this);
     WIN_QuitDeviceNotification();
@@ -525,6 +542,15 @@ void WIN_VideoQuit(SDL_VideoDevice *_this)
 
     WIN_SetRawMouseEnabled(_this, SDL_FALSE);
     WIN_SetRawKeyboardEnabled(_this, SDL_FALSE);
+
+    if (data->oleinitialized) {
+        OleUninitialize();
+        data->oleinitialized = SDL_FALSE;
+    }
+    if (data->coinitialized) {
+        WIN_CoUninitialize();
+        data->coinitialized = SDL_FALSE;
+    }
 }
 
 #if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)

+ 3 - 0
src/video/windows/SDL_windowsvideo.h

@@ -379,6 +379,9 @@ struct SDL_VideoData
 {
     int render;
 
+    SDL_bool coinitialized;
+    SDL_bool oleinitialized;
+
     DWORD clipboard_count;
 
 #if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) /* Xbox doesn't support user32/shcore*/

+ 490 - 26
src/video/windows/SDL_windowswindow.c

@@ -24,12 +24,13 @@
 
 #include "../../core/windows/SDL_windows.h"
 
-#include "../SDL_sysvideo.h"
-#include "../SDL_pixels_c.h"
+#include "../../SDL_hints_c.h"
+#include "../../events/SDL_dropevents_c.h"
 #include "../../events/SDL_keyboard_c.h"
 #include "../../events/SDL_mouse_c.h"
 #include "../../events/SDL_windowevents_c.h"
-#include "../../SDL_hints_c.h"
+#include "../SDL_pixels_c.h"
+#include "../SDL_sysvideo.h"
 
 #include "SDL_windowsvideo.h"
 #include "SDL_windowswindow.h"
@@ -188,26 +189,27 @@ static int WIN_AdjustWindowRectWithStyle(SDL_Window *window, DWORD style, DWORD
 
     /* Client rect, in points */
     switch (rect_type) {
-        case SDL_WINDOWRECT_CURRENT:
-            SDL_RelativeToGlobalForWindow(window, window->x, window->y, x, y);
-            *width = window->w;
-            *height = window->h;
-            break;
-        case SDL_WINDOWRECT_WINDOWED:
-            SDL_RelativeToGlobalForWindow(window, window->windowed.x, window->windowed.y, x, y);
-            *width = window->windowed.w;
-            *height = window->windowed.h;
-            break;
-        case SDL_WINDOWRECT_FLOATING:
-            SDL_RelativeToGlobalForWindow(window, window->floating.x, window->floating.y, x, y);
-            *width = window->floating.w;
-            *height = window->floating.h;
-            break;
-        default:
-            /* Should never be here */
-            SDL_assert_release(SDL_FALSE);
-            *width = 0;
-            *height = 0;
+    case SDL_WINDOWRECT_CURRENT:
+        SDL_RelativeToGlobalForWindow(window, window->x, window->y, x, y);
+        *width = window->w;
+        *height = window->h;
+        break;
+    case SDL_WINDOWRECT_WINDOWED:
+        SDL_RelativeToGlobalForWindow(window, window->windowed.x, window->windowed.y, x, y);
+        *width = window->windowed.w;
+        *height = window->windowed.h;
+        break;
+    case SDL_WINDOWRECT_FLOATING:
+        SDL_RelativeToGlobalForWindow(window, window->floating.x, window->floating.y, x, y);
+        *width = window->floating.w;
+        *height = window->floating.h;
+        break;
+    default:
+        /* Should never be here */
+        SDL_assert_release(SDL_FALSE);
+        *width = 0;
+        *height = 0;
+        break;
     }
 
     /* Copy the client size in pixels into this rect structure,
@@ -587,6 +589,10 @@ static void CleanupWindowData(SDL_VideoDevice *_this, SDL_Window *window)
     if (data) {
         SDL_DelHintCallback(SDL_HINT_MOUSE_RELATIVE_MODE_CENTER, WIN_MouseRelativeModeCenterChanged, data);
 
+        if (data->drop_target) {
+            WIN_AcceptDragAndDrop(window, SDL_FALSE);
+        }
+
 #if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
         if (data->ICMFileName) {
             SDL_free(data->ICMFileName);
@@ -1136,7 +1142,7 @@ void WIN_MaximizeWindow(SDL_VideoDevice *_this, SDL_Window *window)
             SetWindowPos(hwnd, HWND_TOP, fx, fy, fw, fh, data->copybits_flag | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOACTIVATE);
             data->expected_resize = SDL_FALSE;
         }
-    }else {
+    } else {
         data->windowed_mode_was_maximized = SDL_TRUE;
     }
 }
@@ -1693,10 +1699,468 @@ int WIN_SetWindowOpacity(SDL_VideoDevice *_this, SDL_Window *window, float opaci
 }
 
 #if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
+
+static const char *SDLGetClipboardFormatName(UINT cf, char *text, int len)
+{
+    switch (cf) {
+    case CF_TEXT:
+        return "CF_TEXT";
+    case CF_BITMAP:
+        return "CF_BITMAP";
+    case CF_METAFILEPICT:
+        return "CF_METAFILEPICT";
+    case CF_SYLK:
+        return "CF_SYLK";
+    case CF_DIF:
+        return "CF_DIF";
+    case CF_TIFF:
+        return "CF_TIFF";
+    case CF_OEMTEXT:
+        return "CF_OEMTEXT";
+    case CF_DIB:
+        return "CF_DIB";
+    case CF_PALETTE:
+        return "CF_PALETTE";
+    case CF_PENDATA:
+        return "CF_PENDATA";
+    case CF_RIFF:
+        return "CF_RIFF";
+    case CF_WAVE:
+        return "CF_WAVE";
+    case CF_UNICODETEXT:
+        return "CF_UNICODETEXT";
+    case CF_ENHMETAFILE:
+        return "CF_ENHMETAFILE";
+    case CF_HDROP:
+        return "CF_HDROP";
+    case CF_LOCALE:
+        return "CF_LOCALE";
+    case CF_DIBV5:
+        return "CF_DIBV5";
+    case CF_OWNERDISPLAY:
+        return "CF_OWNERDISPLAY";
+    case CF_DSPTEXT:
+        return "CF_DSPTEXT";
+    case CF_DSPBITMAP:
+        return "CF_DSPBITMAP";
+    case CF_DSPMETAFILEPICT:
+        return "CF_DSPMETAFILEPICT";
+    case CF_DSPENHMETAFILE:
+        return "CF_DSPENHMETAFILE";
+    default:
+        if (GetClipboardFormatNameA(cf, text, len)) {
+            return text;
+        } else {
+            return NULL;
+        }
+    }
+}
+
+static STDMETHODIMP_(ULONG) SDLDropTarget_AddRef(SDLDropTarget *target)
+{
+    return ++target->refcount;
+}
+
+static STDMETHODIMP_(ULONG) SDLDropTarget_Release(SDLDropTarget *target)
+{
+    --target->refcount;
+    if (target->refcount == 0) {
+        SDL_free(target);
+        return 0;
+    }
+    return target->refcount;
+}
+
+static STDMETHODIMP SDLDropTarget_QueryInterface(SDLDropTarget *target, REFIID riid, PVOID *ppv)
+{
+    if (ppv == NULL) {
+        return E_INVALIDARG;
+    }
+
+    *ppv = NULL;
+    if (WIN_IsEqualIID(riid, &IID_IUnknown) ||
+        WIN_IsEqualIID(riid, &IID_IDropTarget)) {
+        *ppv = (void *)target;
+    }
+    if (*ppv) {
+        SDLDropTarget_AddRef(target);
+        return S_OK;
+    }
+    return E_NOINTERFACE;
+}
+
+static STDMETHODIMP SDLDropTarget_DragEnter(SDLDropTarget *target,
+                                            IDataObject *pDataObject, DWORD grfKeyState,
+                                            POINTL pt, DWORD *pdwEffect)
+{
+    SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,
+                 ". In DragEnter at %ld, %ld\n", pt.x, pt.y);
+    *pdwEffect = DROPEFFECT_COPY;
+    POINT pnt = { pt.x, pt.y };
+    if (ScreenToClient(target->hwnd, &pnt)) {
+        SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,
+                     ". In DragEnter at %ld, %ld => window %u at %ld, %ld\n", pt.x, pt.y, target->window->id, pnt.x, pnt.y);
+        SDL_SendDropPosition(target->window, pnt.x, pnt.y);
+    } else {
+        SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,
+                     ". In DragEnter at %ld, %ld => nil, nil\n", pt.x, pt.y);
+    }
+    return S_OK;
+}
+
+static STDMETHODIMP SDLDropTarget_DragOver(SDLDropTarget *target,
+                                           DWORD grfKeyState,
+                                           POINTL pt, DWORD *pdwEffect)
+{
+    SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,
+                 ". In DragOver at %ld, %ld\n", pt.x, pt.y);
+    *pdwEffect = DROPEFFECT_COPY;
+    POINT pnt = { pt.x, pt.y };
+    if (ScreenToClient(target->hwnd, &pnt)) {
+        SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,
+                     ". In DragOver at %ld, %ld => window %u at %ld, %ld\n", pt.x, pt.y, target->window->id, pnt.x, pnt.y);
+        SDL_SendDropPosition(target->window, pnt.x, pnt.y);
+    } else {
+        SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,
+                     ". In DragOver at %ld, %ld => nil, nil\n", pt.x, pt.y);
+    }
+    return S_OK;
+}
+
+static STDMETHODIMP SDLDropTarget_DragLeave(SDLDropTarget *target)
+{
+    SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,
+                 ". In DragLeave\n");
+    SDL_SendDropComplete(target->window);
+    return S_OK;
+}
+
+static STDMETHODIMP SDLDropTarget_Drop(SDLDropTarget *target,
+                                       IDataObject *pDataObject, DWORD grfKeyState,
+                                       POINTL pt, DWORD *pdwEffect)
+{
+    *pdwEffect = DROPEFFECT_COPY;
+    POINT pnt = { pt.x, pt.y };
+    if (ScreenToClient(target->hwnd, &pnt)) {
+        SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,
+                     ". In Drop at %ld, %ld => window %u at %ld, %ld\n", pt.x, pt.y, target->window->id, pnt.x, pnt.y);
+        SDL_SendDropPosition(target->window, pnt.x, pnt.y);
+    } else {
+        SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,
+                     ". In Drop at %ld, %ld => nil, nil\n", pt.x, pt.y);
+    }
+
+    {
+        IEnumFORMATETC *pEnumFormatEtc;
+        HRESULT hres;
+        hres = pDataObject->lpVtbl->EnumFormatEtc(pDataObject, DATADIR_GET, &pEnumFormatEtc);
+        SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,
+                     ". In Drop for EnumFormatEtc, HRESULT is %08lx\n", hres);
+        if (hres == S_OK) {
+            FORMATETC fetc;
+            while (pEnumFormatEtc->lpVtbl->Next(pEnumFormatEtc, 1, &fetc, NULL) == S_OK) {
+                char name[257] = { 0 };
+                const char *cfnm = SDLGetClipboardFormatName(fetc.cfFormat, name, 256);
+                if (cfnm) {
+                    SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,
+                                 ". In Drop, Supported format is %08x, '%s'\n", fetc.cfFormat, cfnm);
+                } else {
+                    SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,
+                                 ". In Drop, Supported format is %08x, Predefined\n", fetc.cfFormat);
+                }
+            }
+        }
+    }
+
+    {
+        FORMATETC fetc;
+        fetc.cfFormat = target->format_file;
+        fetc.ptd = NULL;
+        fetc.dwAspect = DVASPECT_CONTENT;
+        fetc.lindex = -1;
+        fetc.tymed = TYMED_HGLOBAL;
+        const char *format_mime = "text/uri-list";
+        if (SUCCEEDED(pDataObject->lpVtbl->QueryGetData(pDataObject, &fetc))) {
+            SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,
+                         ". In Drop File for QueryGetData, format %08x '%s', success\n",
+                         fetc.cfFormat, format_mime);
+            STGMEDIUM med;
+            HRESULT hres = pDataObject->lpVtbl->GetData(pDataObject, &fetc, &med);
+            SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,
+                         ". In Drop File for      GetData, format %08x '%s', HRESULT is %08lx\n",
+                         fetc.cfFormat, format_mime, hres);
+            if (SUCCEEDED(hres)) {
+                const size_t bsize = GlobalSize(med.hGlobal);
+                const void *buffer = (void *)GlobalLock(med.hGlobal);
+                SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,
+                             ". In Drop File for   GlobalLock, format %08x '%s', memory (%lu) %p\n",
+                             fetc.cfFormat, format_mime, (unsigned long)bsize, buffer);
+                if (buffer) {
+                    char *text = SDL_malloc(bsize + sizeof(Uint32));
+                    SDL_memcpy((Uint8 *)text, buffer, bsize);
+                    SDL_memset((Uint8 *)text + bsize, 0, sizeof(Uint32));
+                    char *saveptr = NULL;
+                    char *token = SDL_strtok_r(text, "\r\n", &saveptr);
+                    while (token != NULL) {
+                        if (SDL_URIToLocal(token, token) >= 0) {
+                            SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,
+                                         ". In Drop File, file (%lu of %lu) '%s'\n",
+                                         (unsigned long)strlen(token), (unsigned long)bsize, token);
+                            SDL_SendDropFile(target->window, NULL, token);
+                        }
+                        token = SDL_strtok_r(NULL, "\r\n", &saveptr);
+                    }
+                    SDL_free(text);
+                }
+                GlobalUnlock(med.hGlobal);
+                ReleaseStgMedium(&med);
+                SDL_SendDropComplete(target->window);
+                return S_OK;
+            }
+        }
+    }
+
+    {
+        FORMATETC fetc;
+        fetc.cfFormat = target->format_text;
+        fetc.ptd = NULL;
+        fetc.dwAspect = DVASPECT_CONTENT;
+        fetc.lindex = -1;
+        fetc.tymed = TYMED_HGLOBAL;
+        const char *format_mime = "text/plain;charset=utf-8";
+        if (SUCCEEDED(pDataObject->lpVtbl->QueryGetData(pDataObject, &fetc))) {
+            SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,
+                         ". In Drop Text for QueryGetData, format %08x '%s', success\n",
+                         fetc.cfFormat, format_mime);
+            STGMEDIUM med;
+            HRESULT hres = pDataObject->lpVtbl->GetData(pDataObject, &fetc, &med);
+            SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,
+                         ". In Drop Text for      GetData, format %08x '%s', HRESULT is %08lx\n",
+                         fetc.cfFormat, format_mime, hres);
+            if (SUCCEEDED(hres)) {
+                const size_t bsize = GlobalSize(med.hGlobal);
+                const void *buffer = (void *)GlobalLock(med.hGlobal);
+                SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,
+                             ". In Drop Text for   GlobalLock, format %08x '%s', memory (%lu) %p\n",
+                             fetc.cfFormat, format_mime, (unsigned long)bsize, buffer);
+                if (buffer) {
+                    char *text = SDL_malloc(bsize + sizeof(Uint32));
+                    SDL_memcpy((Uint8 *)text, buffer, bsize);
+                    SDL_memset((Uint8 *)text + bsize, 0, sizeof(Uint32));
+                    char *saveptr = NULL;
+                    char *token = SDL_strtok_r(text, "\r\n", &saveptr);
+                    while (token != NULL) {
+                        SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,
+                                     ". In Drop Text, text (%lu of %lu) '%s'\n",
+                                     (unsigned long)strlen(token), (unsigned long)bsize, token);
+                        SDL_SendDropText(target->window, (char *)token);
+                        token = SDL_strtok_r(NULL, "\r\n", &saveptr);
+                    }
+                    SDL_free(text);
+                }
+                GlobalUnlock(med.hGlobal);
+                ReleaseStgMedium(&med);
+                SDL_SendDropComplete(target->window);
+                return S_OK;
+            }
+        }
+    }
+
+    {
+        FORMATETC fetc;
+        fetc.cfFormat = CF_UNICODETEXT;
+        fetc.ptd = NULL;
+        fetc.dwAspect = DVASPECT_CONTENT;
+        fetc.lindex = -1;
+        fetc.tymed = TYMED_HGLOBAL;
+        const char *format_mime = "CF_UNICODETEXT";
+        if (SUCCEEDED(pDataObject->lpVtbl->QueryGetData(pDataObject, &fetc))) {
+            SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,
+                         ". In Drop Text for QueryGetData, format %08x '%s', success\n",
+                         fetc.cfFormat, format_mime);
+            STGMEDIUM med;
+            HRESULT hres = pDataObject->lpVtbl->GetData(pDataObject, &fetc, &med);
+            SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,
+                         ". In Drop Text for      GetData, format %08x '%s', HRESULT is %08lx\n",
+                         fetc.cfFormat, format_mime, hres);
+            if (SUCCEEDED(hres)) {
+                const size_t bsize = GlobalSize(med.hGlobal);
+                const void *buffer = (void *)GlobalLock(med.hGlobal);
+                SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,
+                             ". In Drop Text for   GlobalLock, format %08x '%s', memory (%lu) %p\n",
+                             fetc.cfFormat, format_mime, (unsigned long)bsize, buffer);
+                if (buffer) {
+                    buffer = WIN_StringToUTF8(buffer);
+                    if (buffer) {
+                        const size_t lbuffer = strlen(buffer);
+                        SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,
+                                     ". In Drop Text for StringToUTF8, format %08x '%s', memory (%lu) %p\n",
+                                     fetc.cfFormat, format_mime, (unsigned long)lbuffer, buffer);
+                        char *text = SDL_malloc(lbuffer + sizeof(Uint32));
+                        SDL_memcpy((Uint8 *)text, buffer, lbuffer);
+                        SDL_memset((Uint8 *)text + lbuffer, 0, sizeof(Uint32));
+                        char *saveptr = NULL;
+                        char *token = SDL_strtok_r(text, "\r\n", &saveptr);
+                        while (token != NULL) {
+                            SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,
+                                         ". In Drop Text, text (%lu of %lu) '%s'\n",
+                                         (unsigned long)strlen(token), (unsigned long)lbuffer, token);
+                            SDL_SendDropText(target->window, (char *)token);
+                            token = SDL_strtok_r(NULL, "\r\n", &saveptr);
+                        }
+                        SDL_free(text);
+                        SDL_free((void *)buffer);
+                    }
+                }
+                GlobalUnlock(med.hGlobal);
+                ReleaseStgMedium(&med);
+                SDL_SendDropComplete(target->window);
+                return S_OK;
+            }
+        }
+    }
+
+    {
+        FORMATETC fetc;
+        fetc.cfFormat = CF_TEXT;
+        fetc.ptd = NULL;
+        fetc.dwAspect = DVASPECT_CONTENT;
+        fetc.lindex = -1;
+        fetc.tymed = TYMED_HGLOBAL;
+        const char *format_mime = "CF_TEXT";
+        if (SUCCEEDED(pDataObject->lpVtbl->QueryGetData(pDataObject, &fetc))) {
+            SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,
+                         ". In Drop Text for QueryGetData, format %08x '%s', success\n",
+                         fetc.cfFormat, format_mime);
+            STGMEDIUM med;
+            HRESULT hres = pDataObject->lpVtbl->GetData(pDataObject, &fetc, &med);
+            SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,
+                         ". In Drop Text for      GetData, format %08x '%s', HRESULT is %08lx\n",
+                         fetc.cfFormat, format_mime, hres);
+            if (SUCCEEDED(hres)) {
+                const size_t bsize = GlobalSize(med.hGlobal);
+                const void *buffer = (void *)GlobalLock(med.hGlobal);
+                SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,
+                             ". In Drop Text for   GlobalLock, format %08x '%s', memory (%lu) %p\n",
+                             fetc.cfFormat, format_mime, (unsigned long)bsize, buffer);
+                if (buffer) {
+                    char *text = SDL_malloc(bsize + sizeof(Uint32));
+                    SDL_memcpy((Uint8 *)text, buffer, bsize);
+                    SDL_memset((Uint8 *)text + bsize, 0, sizeof(Uint32));
+                    char *saveptr = NULL;
+                    char *token = SDL_strtok_r(text, "\r\n", &saveptr);
+                    while (token != NULL) {
+                        SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,
+                                     ". In Drop Text, text (%lu of %lu) '%s'\n",
+                                     (unsigned long)strlen(token), (unsigned long)bsize, token);
+                        SDL_SendDropText(target->window, (char *)token);
+                        token = SDL_strtok_r(NULL, "\r\n", &saveptr);
+                    }
+                    SDL_free(text);
+                }
+                GlobalUnlock(med.hGlobal);
+                ReleaseStgMedium(&med);
+                SDL_SendDropComplete(target->window);
+                return S_OK;
+            }
+        }
+    }
+
+    {
+        FORMATETC fetc;
+        fetc.cfFormat = CF_HDROP;
+        fetc.ptd = NULL;
+        fetc.dwAspect = DVASPECT_CONTENT;
+        fetc.lindex = -1;
+        fetc.tymed = TYMED_HGLOBAL;
+        const char *format_mime = "CF_HDROP";
+        if (SUCCEEDED(pDataObject->lpVtbl->QueryGetData(pDataObject, &fetc))) {
+            SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,
+                         ". In Drop File for QueryGetData, format %08x '%s', success\n",
+                         fetc.cfFormat, format_mime);
+            STGMEDIUM med;
+            HRESULT hres = pDataObject->lpVtbl->GetData(pDataObject, &fetc, &med);
+            SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,
+                         ". In Drop File for      GetData, format %08x '%s', HRESULT is %08lx\n",
+                         fetc.cfFormat, format_mime, hres);
+            if (SUCCEEDED(hres)) {
+                const size_t bsize = GlobalSize(med.hGlobal);
+                HDROP drop = (HDROP)GlobalLock(med.hGlobal);
+                SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,
+                             ". In Drop File for   GlobalLock, format %08x '%s', memory (%lu) %p\n",
+                             fetc.cfFormat, format_mime, (unsigned long)bsize, drop);
+                UINT count = DragQueryFile(drop, 0xFFFFFFFF, NULL, 0);
+                for (UINT i = 0; i < count; ++i) {
+                    SDL_bool isstack;
+                    UINT size = DragQueryFile(drop, i, NULL, 0) + 1;
+                    LPTSTR buffer = SDL_small_alloc(TCHAR, size, &isstack);
+                    if (buffer) {
+                        if (DragQueryFile(drop, i, buffer, size)) {
+                            char *file = WIN_StringToUTF8(buffer);
+                            SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,
+                                         ". In Drop File, file (%lu of %lu) '%s'\n",
+                                         (unsigned long)strlen(file), (unsigned long)bsize, file);
+                            SDL_SendDropFile(target->window, NULL, file);
+                            SDL_free(file);
+                        }
+                        SDL_small_free(buffer, isstack);
+                    }
+                }
+                GlobalUnlock(med.hGlobal);
+                ReleaseStgMedium(&med);
+                SDL_SendDropComplete(target->window);
+                return S_OK;
+            }
+        }
+    }
+
+    SDL_SendDropComplete(target->window);
+    return S_OK;
+}
+
+static void *vtDropTarget[] = {
+    (void *)(SDLDropTarget_QueryInterface),
+    (void *)(SDLDropTarget_AddRef),
+    (void *)(SDLDropTarget_Release),
+    (void *)(SDLDropTarget_DragEnter),
+    (void *)(SDLDropTarget_DragOver),
+    (void *)(SDLDropTarget_DragLeave),
+    (void *)(SDLDropTarget_Drop)
+};
+
 void WIN_AcceptDragAndDrop(SDL_Window *window, SDL_bool accept)
 {
-    const SDL_WindowData *data = window->internal;
-    DragAcceptFiles(data->hwnd, accept ? TRUE : FALSE);
+    SDL_WindowData *data = window->internal;
+    if (data->videodata->oleinitialized) {
+        if (accept && !data->drop_target) {
+            SDLDropTarget *drop_target = (SDLDropTarget *)SDL_calloc(1, sizeof(SDLDropTarget));
+            if (drop_target != NULL) {
+                drop_target->lpVtbl = vtDropTarget;
+                drop_target->window = window;
+                drop_target->hwnd = data->hwnd;
+                drop_target->format_file = RegisterClipboardFormat(L"text/uri-list");
+                drop_target->format_text = RegisterClipboardFormat(L"text/plain;charset=utf-8");
+                data->drop_target = drop_target;
+                SDLDropTarget_AddRef(drop_target);
+                RegisterDragDrop(data->hwnd, (LPDROPTARGET)drop_target);
+                SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,
+                             ". In Accept Drag and Drop, window %u, enabled Full OLE IDropTarget\n",
+                             window->id);
+            }
+        } else if (!accept && data->drop_target) {
+            RevokeDragDrop(data->hwnd);
+            SDLDropTarget_Release(data->drop_target);
+            data->drop_target = NULL;
+            SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,
+                         ". In Accept Drag and Drop, window %u, disabled Full OLE IDropTarget\n",
+                         window->id);
+        }
+    } else {
+        DragAcceptFiles(data->hwnd, accept ? TRUE : FALSE);
+        SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,
+                     ". In Accept Drag and Drop, window %u, %s Fallback WM_DROPFILES\n",
+                     window->id, (accept ? "enabled" : "disabled"));
+    }
 }
 
 int WIN_FlashWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOperation operation)

+ 11 - 0
src/video/windows/SDL_windowswindow.h

@@ -48,6 +48,16 @@ typedef enum SDL_WindowEraseBackgroundMode
     SDL_ERASEBACKGROUNDMODE_ALWAYS,
 } SDL_WindowEraseBackgroundMode;
 
+typedef struct
+{
+    void **lpVtbl;
+    int refcount;
+    SDL_Window *window;
+    HWND hwnd;
+    UINT format_text;
+    UINT format_file;
+} SDLDropTarget;
+
 struct SDL_WindowData
 {
     SDL_Window *window;
@@ -89,6 +99,7 @@ struct SDL_WindowData
 
     /* Whether we retain the content of the window when changing state */
     UINT copybits_flag;
+    SDLDropTarget *drop_target;
 };
 
 extern int WIN_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID create_props);