From 808c312b2acf5596a6960bb367dc77e1f2e9d0b2 Mon Sep 17 00:00:00 2001 From: Dragon-Baroque <74261498+Dragon-Baroque@users.noreply.github.com> Date: Tue, 5 Sep 2023 09:15:12 +0200 Subject: [PATCH] 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 ) --- src/video/windows/SDL_windowsvideo.c | 26 ++ src/video/windows/SDL_windowsvideo.h | 3 + src/video/windows/SDL_windowswindow.c | 516 ++++++++++++++++++++++++-- src/video/windows/SDL_windowswindow.h | 11 + 4 files changed, 530 insertions(+), 26 deletions(-) diff --git a/src/video/windows/SDL_windowsvideo.c b/src/video/windows/SDL_windowsvideo.c index 5fd38ad2b..118717b9a 100644 --- a/src/video/windows/SDL_windowsvideo.c +++ b/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) diff --git a/src/video/windows/SDL_windowsvideo.h b/src/video/windows/SDL_windowsvideo.h index ea0867beb..fa3aba980 100644 --- a/src/video/windows/SDL_windowsvideo.h +++ b/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*/ diff --git a/src/video/windows/SDL_windowswindow.c b/src/video/windows/SDL_windowswindow.c index 66299c6aa..c0f3692f1 100644 --- a/src/video/windows/SDL_windowswindow.c +++ b/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) diff --git a/src/video/windows/SDL_windowswindow.h b/src/video/windows/SDL_windowswindow.h index 3cf0f833e..1b3ebfb0b 100644 --- a/src/video/windows/SDL_windowswindow.h +++ b/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);