diff --git a/Source/Engine/Engine/Engine.cpp b/Source/Engine/Engine/Engine.cpp index 1a57c4f10..c0cfad34c 100644 --- a/Source/Engine/Engine/Engine.cpp +++ b/Source/Engine/Engine/Engine.cpp @@ -20,7 +20,6 @@ #include "Engine/Threading/MainThreadTask.h" #include "Engine/Threading/ThreadRegistry.h" #include "Engine/Graphics/GPUDevice.h" -#include "Engine/Scripting/ManagedCLR/MCore.h" #include "Engine/Scripting/ScriptingType.h" #include "Engine/Content/Content.h" #include "Engine/Content/JsonAsset.h" @@ -327,14 +326,6 @@ void Engine::OnUpdate() // Update services EngineService::OnUpdate(); - -#ifdef USE_NETCORE - // Force GC to run in background periodically to avoid large blocking collections causing hitches - if (Time::Update.TicksCount % 60 == 0) - { - MCore::GC::Collect(MCore::GC::MaxGeneration(), MGCCollectionMode::Forced, false, false); - } -#endif } void Engine::OnLateUpdate() diff --git a/Source/Engine/Platform/Windows/WindowsWindow.DragDrop.cpp b/Source/Engine/Platform/Windows/WindowsWindow.DragDrop.cpp deleted file mode 100644 index b6e636697..000000000 --- a/Source/Engine/Platform/Windows/WindowsWindow.DragDrop.cpp +++ /dev/null @@ -1,698 +0,0 @@ -// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. - -#if PLATFORM_WINDOWS - -#include "WindowsWindow.h" - -#if USE_EDITOR - -#include "Engine/Core/Collections/Array.h" -#include "Engine/Engine/Engine.h" -#include "Engine/Platform/IGuiData.h" -#include "Engine/Input/Input.h" -#include "Engine/Input/Mouse.h" -#include "Engine/Threading/ThreadPoolTask.h" -#include "Engine/Threading/ThreadPool.h" -#include "Engine/Scripting/Scripting.h" -#include "Engine/Scripting/ManagedCLR/MDomain.h" -#include "../Win32/IncludeWindowsHeaders.h" -#include -#include - -HGLOBAL duplicateGlobalMem(HGLOBAL hMem) -{ - auto len = GlobalSize(hMem); - auto source = GlobalLock(hMem); - auto dest = GlobalAlloc(GMEM_FIXED, len); - Platform::MemoryCopy(dest, source, len); - GlobalUnlock(hMem); - return dest; -} - -DWORD dropEffect2OleEnum(DragDropEffect effect) -{ - DWORD result; - switch (effect) - { - case DragDropEffect::None: - result = DROPEFFECT_NONE; - break; - case DragDropEffect::Copy: - result = DROPEFFECT_COPY; - break; - case DragDropEffect::Move: - result = DROPEFFECT_MOVE; - break; - case DragDropEffect::Link: - result = DROPEFFECT_LINK; - break; - default: - result = DROPEFFECT_NONE; - break; - } - return result; -} - -DragDropEffect dropEffectFromOleEnum(DWORD effect) -{ - DragDropEffect result; - switch (effect) - { - case DROPEFFECT_NONE: - result = DragDropEffect::None; - break; - case DROPEFFECT_COPY: - result = DragDropEffect::Copy; - break; - case DROPEFFECT_MOVE: - result = DragDropEffect::Move; - break; - case DROPEFFECT_LINK: - result = DragDropEffect::Link; - break; - default: - result = DragDropEffect::None; - break; - } - return result; -} - -HANDLE StringToHandle(const StringView& str) -{ - // Allocate and lock a global memory buffer. - // Make it fixed data so we don't have to use GlobalLock - const int32 length = str.Length(); - char* ptr = static_cast(GlobalAlloc(GMEM_FIXED, length + 1)); - - // Copy the string into the buffer as ANSI text - StringUtils::ConvertUTF162ANSI(str.Get(), ptr, length); - ptr[length] = '\0'; - - return ptr; -} - -void DeepCopyFormatEtc(FORMATETC* dest, FORMATETC* source) -{ - // Copy the source FORMATETC into dest - *dest = *source; - - if (source->ptd) - { - // Allocate memory for the DVTARGETDEVICE if necessary - dest->ptd = static_cast(CoTaskMemAlloc(sizeof(DVTARGETDEVICE))); - - // Copy the contents of the source DVTARGETDEVICE into dest->ptd - *(dest->ptd) = *(source->ptd); - } -} - -HRESULT CreateEnumFormatEtc(UINT nNumFormats, FORMATETC* pFormatEtc, IEnumFORMATETC** ppEnumFormatEtc); - -/// -/// GUI data for Windows platform -/// -class WindowsGuiData : public IGuiData -{ -private: - - Type _type; - Array _data; - -public: - - /// - /// Init - /// - WindowsGuiData() - : _type(Type::Unknown) - , _data(1) - { - } - -public: - - /// - /// Init from Ole IDataObject - /// - /// Object - void Init(IDataObject* pDataObj) - { - // Temporary data - FORMATETC fmtetc; - STGMEDIUM stgmed; - - // Clear - _type = Type::Unknown; - _data.Clear(); - - // Check type - fmtetc = { CF_TEXT, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; - if (pDataObj->GetData(&fmtetc, &stgmed) == S_OK) - { - // Text - _type = Type::Text; - - // Get data - char* text = static_cast(GlobalLock(stgmed.hGlobal)); - _data.Add(String(text)); - GlobalUnlock(stgmed.hGlobal); - ReleaseStgMedium(&stgmed); - } - else - { - fmtetc = { CF_UNICODETEXT, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; - if (pDataObj->GetData(&fmtetc, &stgmed) == S_OK) - { - // Unicode Text - _type = Type::Text; - - // Get data - Char* text = static_cast(GlobalLock(stgmed.hGlobal)); - _data.Add(String(text)); - GlobalUnlock(stgmed.hGlobal); - ReleaseStgMedium(&stgmed); - } - else - { - fmtetc = { CF_HDROP, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; - if (pDataObj->GetData(&fmtetc, &stgmed) == S_OK) - { - // Files - _type = Type::Files; - - // Get data - Char item[MAX_PATH]; - HDROP hdrop = static_cast(GlobalLock(stgmed.hGlobal)); - UINT filesCount = DragQueryFileW(hdrop, 0xFFFFFFFF, nullptr, 0); - for (UINT i = 0; i < filesCount; i++) - { - if (DragQueryFileW(hdrop, i, item, MAX_PATH) != 0) - { - _data.Add(String(item)); - } - } - GlobalUnlock(stgmed.hGlobal); - ReleaseStgMedium(&stgmed); - } - } - } - } - -public: - - // [IGuiData] - Type GetType() const override - { - return _type; - } - - String GetAsText() const override - { - String result; - if (_type == Type::Text) - { - result = _data[0]; - } - return result; - } - - void GetAsFiles(Array* files) const override - { - if (_type == Type::Files) - { - files->Add(_data); - } - } -}; - -/// -/// Tool class for Windows Ole support -/// -class WindowsEnumFormatEtc : public IEnumFORMATETC -{ -private: - - ULONG _refCount; - ULONG _index; - ULONG _formatsCount; - FORMATETC* _formatEtc; - -public: - - WindowsEnumFormatEtc(FORMATETC* pFormatEtc, int32 nNumFormats) - : _refCount(1) - , _index(0) - , _formatsCount(nNumFormats) - , _formatEtc(nullptr) - { - // Allocate memory - _formatEtc = new FORMATETC[nNumFormats]; - - // Copy the FORMATETC structures - for (int32 i = 0; i < nNumFormats; i++) - { - DeepCopyFormatEtc(&_formatEtc[i], &pFormatEtc[i]); - } - } - - ~WindowsEnumFormatEtc() - { - if (_formatEtc) - { - for (uint32 i = 0; i < _formatsCount; i++) - { - if (_formatEtc[i].ptd) - { - CoTaskMemFree(_formatEtc[i].ptd); - } - } - - delete[] _formatEtc; - } - } - -public: - - HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, _COM_Outptr_ void __RPC_FAR *__RPC_FAR * ppvObject) override - { - // Check to see what interface has been requested - if (riid == IID_IEnumFORMATETC || riid == IID_IUnknown) - { - AddRef(); - *ppvObject = this; - return S_OK; - } - - // No interface - *ppvObject = nullptr; - return E_NOINTERFACE; - } - - ULONG STDMETHODCALLTYPE AddRef() override - { - _InterlockedIncrement(&_refCount); - return _refCount; - } - - ULONG STDMETHODCALLTYPE Release() override - { - ULONG ulRefCount = _InterlockedDecrement(&_refCount); - if (_refCount == 0) - { - delete this; - } - return ulRefCount; - } - - // [IEnumFormatEtc] - HRESULT STDMETHODCALLTYPE Next(ULONG celt, FORMATETC* pFormatEtc, ULONG* pceltFetched) override - { - ULONG copied = 0; - - // validate arguments - if (celt == 0 || pFormatEtc == nullptr) - return E_INVALIDARG; - - // copy FORMATETC structures into caller's buffer - while (_index < _formatsCount && copied < celt) - { - DeepCopyFormatEtc(&pFormatEtc[copied], &_formatEtc[_index]); - copied++; - _index++; - } - - // store result - if (pceltFetched != nullptr) - *pceltFetched = copied; - - // did we copy all that was requested? - return (copied == celt) ? S_OK : S_FALSE; - } - - HRESULT STDMETHODCALLTYPE Skip(ULONG celt) override - { - _index += celt; - return (_index <= _formatsCount) ? S_OK : S_FALSE; - } - - HRESULT STDMETHODCALLTYPE Reset() override - { - _index = 0; - return S_OK; - } - - HRESULT STDMETHODCALLTYPE Clone(IEnumFORMATETC** ppEnumFormatEtc) override - { - HRESULT result; - - // Make a duplicate enumerator - result = CreateEnumFormatEtc(_formatsCount, _formatEtc, ppEnumFormatEtc); - - if (result == S_OK) - { - // Manually set the index state - static_cast(*ppEnumFormatEtc)->_index = _index; - } - - return result; - } -}; - -HRESULT CreateEnumFormatEtc(UINT nNumFormats, FORMATETC* pFormatEtc, IEnumFORMATETC** ppEnumFormatEtc) -{ - if (nNumFormats == 0 || pFormatEtc == nullptr || ppEnumFormatEtc == nullptr) - return E_INVALIDARG; - - *ppEnumFormatEtc = new WindowsEnumFormatEtc(pFormatEtc, nNumFormats); - - return (*ppEnumFormatEtc) ? S_OK : E_OUTOFMEMORY; -} - -/// -/// Drag drop source and data container for Ole -/// -class WindowsDragSource : public IDataObject, public IDropSource -{ -private: - - ULONG _refCount; - int32 _formatsCount; - FORMATETC* _formatEtc; - STGMEDIUM* _stgMedium; - -public: - - WindowsDragSource(FORMATETC* fmtetc, STGMEDIUM* stgmed, int32 count) - : _refCount(1) - , _formatsCount(count) - , _formatEtc(nullptr) - , _stgMedium(nullptr) - { - // Allocate memory - _formatEtc = new FORMATETC[count]; - _stgMedium = new STGMEDIUM[count]; - - // Copy descriptors - for (int32 i = 0; i < count; i++) - { - _formatEtc[i] = fmtetc[i]; - _stgMedium[i] = stgmed[i]; - } - } - - virtual ~WindowsDragSource() - { - if (_formatEtc) - delete[] _formatEtc; - if (_stgMedium) - delete[] _stgMedium; - } - -public: - - // [IUnknown] - HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, _COM_Outptr_ void __RPC_FAR *__RPC_FAR * ppvObject) override - { - // Check to see what interface has been requested - if (riid == IID_IDataObject || riid == IID_IUnknown || riid == IID_IDropSource) - { - AddRef(); - *ppvObject = this; - return S_OK; - } - - // No interface - *ppvObject = nullptr; - return E_NOINTERFACE; - } - - ULONG STDMETHODCALLTYPE AddRef() override - { - _InterlockedIncrement(&_refCount); - return _refCount; - } - - ULONG STDMETHODCALLTYPE Release() override - { - ULONG ulRefCount = _InterlockedDecrement(&_refCount); - if (_refCount == 0) - { - delete this; - } - return ulRefCount; - } - - // [IDropSource] - HRESULT STDMETHODCALLTYPE QueryContinueDrag(_In_ BOOL fEscapePressed, _In_ DWORD grfKeyState) override - { - // If the Escape key has been pressed since the last call, cancel the drop - if (fEscapePressed == TRUE || grfKeyState & MK_RBUTTON) - return DRAGDROP_S_CANCEL; - - // If the LeftMouse button has been released, then do the drop! - if ((grfKeyState & MK_LBUTTON) == 0) - return DRAGDROP_S_DROP; - - // Continue with the drag-drop - return S_OK; - } - - HRESULT STDMETHODCALLTYPE GiveFeedback(_In_ DWORD dwEffect) override - { - // TODO: allow to use custom mouse cursor during drop and drag operation - return DRAGDROP_S_USEDEFAULTCURSORS; - } - - // [IDataObject] - HRESULT STDMETHODCALLTYPE GetData(_In_ FORMATETC* pformatetcIn, _Out_ STGMEDIUM* pmedium) override - { - if (pformatetcIn == nullptr || pmedium == nullptr) - return E_INVALIDARG; - - // Try to match the specified FORMATETC with one of our supported formats - int32 index = lookupFormatEtc(pformatetcIn); - if (index == INVALID_INDEX) - return DV_E_FORMATETC; - - // Found a match - transfer data into supplied storage medium - pmedium->tymed = _formatEtc[index].tymed; - pmedium->pUnkForRelease = nullptr; - - // Copy the data into the caller's storage medium - switch (_formatEtc[index].tymed) - { - case TYMED_HGLOBAL: - pmedium->hGlobal = duplicateGlobalMem(_stgMedium[index].hGlobal); - break; - - default: - return DV_E_FORMATETC; - } - - return S_OK; - } - - HRESULT STDMETHODCALLTYPE GetDataHere(_In_ FORMATETC* pformatetc, _Inout_ STGMEDIUM* pmedium) override - { - return DATA_E_FORMATETC; - } - - HRESULT STDMETHODCALLTYPE QueryGetData(__RPC__in_opt FORMATETC* pformatetc) override - { - return lookupFormatEtc(pformatetc) == INVALID_INDEX ? DV_E_FORMATETC : S_OK; - } - - HRESULT STDMETHODCALLTYPE GetCanonicalFormatEtc(__RPC__in_opt FORMATETC* pformatectIn, __RPC__out FORMATETC* pformatetcOut) override - { - // Apparently we have to set this field to NULL even though we don't do anything else - pformatetcOut->ptd = nullptr; - return E_NOTIMPL; - } - - HRESULT STDMETHODCALLTYPE SetData(_In_ FORMATETC* pformatetc, _In_ STGMEDIUM* pmedium, BOOL fRelease) override - { - return E_NOTIMPL; - } - - HRESULT STDMETHODCALLTYPE EnumFormatEtc(DWORD dwDirection, __RPC__deref_out_opt IEnumFORMATETC** ppenumFormatEtc) override - { - // Only the get direction is supported for OLE - if (dwDirection == DATADIR_GET) - { - // TODO: use SHCreateStdEnumFmtEtc API call - return CreateEnumFormatEtc(_formatsCount, _formatEtc, ppenumFormatEtc); - } - - // The direction specified is not supported for drag+drop - return E_NOTIMPL; - } - - HRESULT STDMETHODCALLTYPE DAdvise(__RPC__in FORMATETC* pformatetc, DWORD advf, __RPC__in_opt IAdviseSink* pAdvSink, __RPC__out DWORD* pdwConnection) override - { - return OLE_E_ADVISENOTSUPPORTED; - } - - HRESULT STDMETHODCALLTYPE DUnadvise(DWORD dwConnection) override - { - return OLE_E_ADVISENOTSUPPORTED; - } - - HRESULT STDMETHODCALLTYPE EnumDAdvise(__RPC__deref_out_opt IEnumSTATDATA** ppenumAdvise) override - { - return OLE_E_ADVISENOTSUPPORTED; - } - -private: - - int32 lookupFormatEtc(FORMATETC* pFormatEtc) const - { - // Check each of our formats in turn to see if one matches - for (int32 i = 0; i < _formatsCount; i++) - { - if ((_formatEtc[i].tymed & pFormatEtc->tymed) && - _formatEtc[i].cfFormat == pFormatEtc->cfFormat && - _formatEtc[i].dwAspect == pFormatEtc->dwAspect) - { - // Return index of stored format - return i; - } - } - - // Format not found - return INVALID_INDEX; - } -}; - -WindowsGuiData GuiDragDropData; - -/// -/// Async DoDragDrop helper (used for rendering frames during main thread stall). -/// -class DoDragDropJob : public ThreadPoolTask -{ -public: - - int64 ExitFlag = 0; - - // [ThreadPoolTask] - bool Run() override - { - Scripting::GetScriptsDomain()->Dispatch(); - while (Platform::AtomicRead(&ExitFlag) == 0) - { - Engine::OnDraw(); - Platform::Sleep(20); - } - return false; - } -}; - -DragDropEffect WindowsWindow::DoDragDrop(const StringView& data) -{ - // Create background worker that will keep updating GUI (perform rendering) - const auto task = New(); - Task::StartNew(task); - while (task->GetState() == TaskState::Queued) - { - Platform::Sleep(1); - } - - // Create descriptors - FORMATETC fmtetc = { CF_TEXT, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; - STGMEDIUM stgmed = { TYMED_HGLOBAL, { nullptr }, nullptr }; - - // Create a HGLOBAL inside the storage medium - stgmed.hGlobal = StringToHandle(data); - - // Create drop source - auto dropSource = new WindowsDragSource(&fmtetc, &stgmed, 1); - - // Do the drag drop operation - DWORD dwEffect; - HRESULT result = ::DoDragDrop(dropSource, dropSource, DROPEFFECT_COPY | DROPEFFECT_MOVE | DROPEFFECT_LINK | DROPEFFECT_SCROLL, &dwEffect); - - // Wait for job end - Platform::AtomicStore(&task->ExitFlag, 1); - task->Wait(); - - // Release allocated data - dropSource->Release(); - ReleaseStgMedium(&stgmed); - - // Fix hanging mouse state (Windows doesn't send WM_LBUTTONUP when we end the drag and drop) - if (Input::GetMouseButton(MouseButton::Left)) - { - ::POINT point; - ::GetCursorPos(&point); - Input::Mouse->OnMouseUp(Float2((float)point.x, (float)point.y), MouseButton::Left, this); - } - - return SUCCEEDED(result) ? dropEffectFromOleEnum(dwEffect) : DragDropEffect::None; -} - -HRESULT WindowsWindow::DragEnter(Windows::IDataObject* pDataObj, Windows::DWORD grfKeyState, Windows::POINTL pt, Windows::DWORD* pdwEffect) -{ - // Call GUI - POINT p = { pt.x, pt.y }; - ::ScreenToClient(_handle, &p); - GuiDragDropData.Init((IDataObject*)pDataObj); - DragDropEffect effect = DragDropEffect::None; - OnDragEnter(&GuiDragDropData, Float2(static_cast(p.x), static_cast(p.y)), effect); - - // Focus - Focus(); - - // Translate effect into Ole Api type - *pdwEffect = dropEffect2OleEnum(effect); - - return S_OK; -} - -HRESULT WindowsWindow::DragOver(Windows::DWORD grfKeyState, Windows::POINTL pt, Windows::DWORD* pdwEffect) -{ - // Call GUI - POINT p = { pt.x, pt.y }; - ::ScreenToClient(_handle, &p); - DragDropEffect effect = DragDropEffect::None; - OnDragOver(&GuiDragDropData, Float2(static_cast(p.x), static_cast(p.y)), effect); - - // Translate effect into Ole Api type - *pdwEffect = dropEffect2OleEnum(effect); - - return S_OK; -} - -HRESULT WindowsWindow::DragLeave() -{ - // Call GUI - OnDragLeave(); - - return S_OK; -} - -HRESULT WindowsWindow::Drop(Windows::IDataObject* pDataObj, Windows::DWORD grfKeyState, Windows::POINTL pt, Windows::DWORD* pdwEffect) -{ - // Call GUI - POINT p = { pt.x, pt.y }; - ::ScreenToClient(_handle, &p); - GuiDragDropData.Init((IDataObject*)pDataObj); - DragDropEffect effect = DragDropEffect::None; - OnDragDrop(&GuiDragDropData, Float2(static_cast(p.x), static_cast(p.y)), effect); - - // Translate effect into Ole Api type - *pdwEffect = dropEffect2OleEnum(effect); - - return S_OK; -} - -#else - -DragDropEffect WindowsWindow::DoDragDrop(const StringView& data) -{ - // Not supported - return DragDropEffect::None; -} - -#endif - -#endif diff --git a/Source/Engine/Platform/Windows/WindowsWindow.cpp b/Source/Engine/Platform/Windows/WindowsWindow.cpp index 174160922..e29d0fc19 100644 --- a/Source/Engine/Platform/Windows/WindowsWindow.cpp +++ b/Source/Engine/Platform/Windows/WindowsWindow.cpp @@ -10,8 +10,23 @@ #include "Engine/Graphics/GPUSwapChain.h" #include "Engine/Graphics/RenderTask.h" #include "Engine/Graphics/GPUDevice.h" +#if USE_EDITOR +#include "Engine/Core/Collections/Array.h" +#include "Engine/Engine/Engine.h" +#include "Engine/Platform/IGuiData.h" +#include "Engine/Input/Input.h" +#include "Engine/Input/Mouse.h" +#include "Engine/Threading/ThreadPoolTask.h" +#include "Engine/Threading/ThreadPool.h" +#include "Engine/Scripting/Scripting.h" +#include "Engine/Scripting/ManagedCLR/MDomain.h" +#endif #include "../Win32/IncludeWindowsHeaders.h" #include +#if USE_EDITOR +#include +#include +#endif #define DefaultDPI 96 @@ -139,7 +154,7 @@ WindowsWindow::WindowsWindow(const CreateWindowSettings& settings) const HMODULE user32Dll = LoadLibraryW(L"user32.dll"); if (user32Dll) { - typedef UINT (STDAPICALLTYPE* GetDpiForWindowProc)(HWND hwnd); + typedef UINT (STDAPICALLTYPE*GetDpiForWindowProc)(HWND hwnd); const GetDpiForWindowProc getDpiForWindowProc = (GetDpiForWindowProc)GetProcAddress(user32Dll, "GetDpiForWindow"); if (getDpiForWindowProc) { @@ -262,7 +277,7 @@ void WindowsWindow::Maximize() void WindowsWindow::SetBorderless(bool isBorderless, bool maximized) { ASSERT(HasHWND()); - + if (IsFullscreen()) SetIsFullscreen(false); @@ -278,7 +293,7 @@ void WindowsWindow::SetBorderless(bool isBorderless, bool maximized) { LONG lStyle = GetWindowLong(_handle, GWL_STYLE); lStyle &= ~(WS_THICKFRAME | WS_SYSMENU | WS_OVERLAPPED | WS_BORDER | WS_CAPTION); - lStyle |= WS_POPUP; + lStyle |= WS_POPUP; lStyle |= WS_CLIPCHILDREN | WS_CLIPSIBLINGS; #if WINDOWS_USE_NEW_BORDER_LESS if (_settings.IsRegularWindow) @@ -289,8 +304,8 @@ void WindowsWindow::SetBorderless(bool isBorderless, bool maximized) #endif SetWindowLong(_handle, GWL_STYLE, lStyle); - SetWindowPos(_handle, HWND_TOP, 0, 0,0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); - + SetWindowPos(_handle, HWND_TOP, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); + if (maximized) { ShowWindow(_handle, SW_SHOWMAXIMIZED); @@ -311,10 +326,10 @@ void WindowsWindow::SetBorderless(bool isBorderless, bool maximized) if (_settings.HasSizingFrame) lStyle |= WS_THICKFRAME; lStyle |= WS_OVERLAPPED | WS_SYSMENU | WS_BORDER | WS_CAPTION; - + SetWindowLong(_handle, GWL_STYLE, lStyle); - SetWindowPos(_handle, nullptr, 0, 0, (int)_settings.Size.X, (int)_settings.Size.Y, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); - + SetWindowPos(_handle, nullptr, 0, 0, (int)_settings.Size.X, (int)_settings.Size.Y, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); + if (maximized) { Maximize(); @@ -727,7 +742,7 @@ void WindowsWindow::CheckForWindowResize() MONITORINFO monitorInfo; monitorInfo.cbSize = sizeof(MONITORINFO); GetMonitorInfoW(monitor, &monitorInfo); - + auto cwidth = monitorInfo.rcWork.right - monitorInfo.rcWork.left; auto cheight = monitorInfo.rcWork.bottom - monitorInfo.rcWork.top; if (width > cwidth && height > cheight) @@ -1297,4 +1312,633 @@ LRESULT WindowsWindow::WndProc(UINT msg, WPARAM wParam, LPARAM lParam) return DefWindowProc(_handle, msg, wParam, lParam); } +#if USE_EDITOR + +HGLOBAL duplicateGlobalMem(HGLOBAL hMem) +{ + auto len = GlobalSize(hMem); + auto source = GlobalLock(hMem); + auto dest = GlobalAlloc(GMEM_FIXED, len); + Platform::MemoryCopy(dest, source, len); + GlobalUnlock(hMem); + return dest; +} + +DWORD dropEffect2OleEnum(DragDropEffect effect) +{ + DWORD result; + switch (effect) + { + case DragDropEffect::None: + result = DROPEFFECT_NONE; + break; + case DragDropEffect::Copy: + result = DROPEFFECT_COPY; + break; + case DragDropEffect::Move: + result = DROPEFFECT_MOVE; + break; + case DragDropEffect::Link: + result = DROPEFFECT_LINK; + break; + default: + result = DROPEFFECT_NONE; + break; + } + return result; +} + +DragDropEffect dropEffectFromOleEnum(DWORD effect) +{ + DragDropEffect result; + switch (effect) + { + case DROPEFFECT_NONE: + result = DragDropEffect::None; + break; + case DROPEFFECT_COPY: + result = DragDropEffect::Copy; + break; + case DROPEFFECT_MOVE: + result = DragDropEffect::Move; + break; + case DROPEFFECT_LINK: + result = DragDropEffect::Link; + break; + default: + result = DragDropEffect::None; + break; + } + return result; +} + +HANDLE StringToHandle(const StringView& str) +{ + // Allocate and lock a global memory buffer. + // Make it fixed data so we don't have to use GlobalLock + const int32 length = str.Length(); + char* ptr = static_cast(GlobalAlloc(GMEM_FIXED, length + 1)); + + // Copy the string into the buffer as ANSI text + StringUtils::ConvertUTF162ANSI(str.Get(), ptr, length); + ptr[length] = '\0'; + + return ptr; +} + +void DeepCopyFormatEtc(FORMATETC* dest, FORMATETC* source) +{ + // Copy the source FORMATETC into dest + *dest = *source; + + if (source->ptd) + { + // Allocate memory for the DVTARGETDEVICE if necessary + dest->ptd = static_cast(CoTaskMemAlloc(sizeof(DVTARGETDEVICE))); + + // Copy the contents of the source DVTARGETDEVICE into dest->ptd + *(dest->ptd) = *(source->ptd); + } +} + +HRESULT CreateEnumFormatEtc(UINT nNumFormats, FORMATETC* pFormatEtc, IEnumFORMATETC** ppEnumFormatEtc); + +/// +/// GUI data for Windows platform +/// +class WindowsGuiData : public IGuiData +{ +private: + Type _type; + Array _data; + +public: + /// + /// Init + /// + WindowsGuiData() + : _type(Type::Unknown) + , _data(1) + { + } + +public: + /// + /// Init from Ole IDataObject + /// + /// Object + void Init(IDataObject* pDataObj) + { + // Temporary data + FORMATETC fmtetc; + STGMEDIUM stgmed; + + // Clear + _type = Type::Unknown; + _data.Clear(); + + // Check type + fmtetc = { CF_TEXT, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; + if (pDataObj->GetData(&fmtetc, &stgmed) == S_OK) + { + // Text + _type = Type::Text; + + // Get data + char* text = static_cast(GlobalLock(stgmed.hGlobal)); + _data.Add(String(text)); + GlobalUnlock(stgmed.hGlobal); + ReleaseStgMedium(&stgmed); + } + else + { + fmtetc = { CF_UNICODETEXT, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; + if (pDataObj->GetData(&fmtetc, &stgmed) == S_OK) + { + // Unicode Text + _type = Type::Text; + + // Get data + Char* text = static_cast(GlobalLock(stgmed.hGlobal)); + _data.Add(String(text)); + GlobalUnlock(stgmed.hGlobal); + ReleaseStgMedium(&stgmed); + } + else + { + fmtetc = { CF_HDROP, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; + if (pDataObj->GetData(&fmtetc, &stgmed) == S_OK) + { + // Files + _type = Type::Files; + + // Get data + Char item[MAX_PATH]; + HDROP hdrop = static_cast(GlobalLock(stgmed.hGlobal)); + UINT filesCount = DragQueryFileW(hdrop, 0xFFFFFFFF, nullptr, 0); + for (UINT i = 0; i < filesCount; i++) + { + if (DragQueryFileW(hdrop, i, item, MAX_PATH) != 0) + { + _data.Add(String(item)); + } + } + GlobalUnlock(stgmed.hGlobal); + ReleaseStgMedium(&stgmed); + } + } + } + } + +public: + // [IGuiData] + Type GetType() const override + { + return _type; + } + String GetAsText() const override + { + String result; + if (_type == Type::Text) + { + result = _data[0]; + } + return result; + } + void GetAsFiles(Array* files) const override + { + if (_type == Type::Files) + { + files->Add(_data); + } + } +}; + +/// +/// Tool class for Windows Ole support +/// +class WindowsEnumFormatEtc : public IEnumFORMATETC +{ +private: + ULONG _refCount; + ULONG _index; + ULONG _formatsCount; + FORMATETC* _formatEtc; + +public: + WindowsEnumFormatEtc(FORMATETC* pFormatEtc, int32 nNumFormats) + : _refCount(1) + , _index(0) + , _formatsCount(nNumFormats) + , _formatEtc(nullptr) + { + // Allocate memory + _formatEtc = new FORMATETC[nNumFormats]; + + // Copy the FORMATETC structures + for (int32 i = 0; i < nNumFormats; i++) + { + DeepCopyFormatEtc(&_formatEtc[i], &pFormatEtc[i]); + } + } + + ~WindowsEnumFormatEtc() + { + if (_formatEtc) + { + for (uint32 i = 0; i < _formatsCount; i++) + { + if (_formatEtc[i].ptd) + { + CoTaskMemFree(_formatEtc[i].ptd); + } + } + + delete[] _formatEtc; + } + } + +public: + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, _COM_Outptr_ void __RPC_FAR *__RPC_FAR * ppvObject) override + { + // Check to see what interface has been requested + if (riid == IID_IEnumFORMATETC || riid == IID_IUnknown) + { + AddRef(); + *ppvObject = this; + return S_OK; + } + + // No interface + *ppvObject = nullptr; + return E_NOINTERFACE; + } + ULONG STDMETHODCALLTYPE AddRef() override + { + _InterlockedIncrement(&_refCount); + return _refCount; + } + ULONG STDMETHODCALLTYPE Release() override + { + ULONG ulRefCount = _InterlockedDecrement(&_refCount); + if (_refCount == 0) + { + delete this; + } + return ulRefCount; + } + + // [IEnumFormatEtc] + HRESULT STDMETHODCALLTYPE Next(ULONG celt, FORMATETC* pFormatEtc, ULONG* pceltFetched) override + { + ULONG copied = 0; + + // validate arguments + if (celt == 0 || pFormatEtc == nullptr) + return E_INVALIDARG; + + // copy FORMATETC structures into caller's buffer + while (_index < _formatsCount && copied < celt) + { + DeepCopyFormatEtc(&pFormatEtc[copied], &_formatEtc[_index]); + copied++; + _index++; + } + + // store result + if (pceltFetched != nullptr) + *pceltFetched = copied; + + // did we copy all that was requested? + return (copied == celt) ? S_OK : S_FALSE; + } + HRESULT STDMETHODCALLTYPE Skip(ULONG celt) override + { + _index += celt; + return (_index <= _formatsCount) ? S_OK : S_FALSE; + } + HRESULT STDMETHODCALLTYPE Reset() override + { + _index = 0; + return S_OK; + } + HRESULT STDMETHODCALLTYPE Clone(IEnumFORMATETC** ppEnumFormatEtc) override + { + HRESULT result; + + // Make a duplicate enumerator + result = CreateEnumFormatEtc(_formatsCount, _formatEtc, ppEnumFormatEtc); + + if (result == S_OK) + { + // Manually set the index state + static_cast(*ppEnumFormatEtc)->_index = _index; + } + + return result; + } +}; + +HRESULT CreateEnumFormatEtc(UINT nNumFormats, FORMATETC* pFormatEtc, IEnumFORMATETC** ppEnumFormatEtc) +{ + if (nNumFormats == 0 || pFormatEtc == nullptr || ppEnumFormatEtc == nullptr) + return E_INVALIDARG; + *ppEnumFormatEtc = new WindowsEnumFormatEtc(pFormatEtc, nNumFormats); + return *ppEnumFormatEtc ? S_OK : E_OUTOFMEMORY; +} + +/// +/// Drag drop source and data container for Ole +/// +class WindowsDragSource : public IDataObject, public IDropSource +{ +private: + ULONG _refCount; + int32 _formatsCount; + FORMATETC* _formatEtc; + STGMEDIUM* _stgMedium; + +public: + WindowsDragSource(FORMATETC* fmtetc, STGMEDIUM* stgmed, int32 count) + : _refCount(1) + , _formatsCount(count) + , _formatEtc(nullptr) + , _stgMedium(nullptr) + { + // Allocate memory + _formatEtc = new FORMATETC[count]; + _stgMedium = new STGMEDIUM[count]; + + // Copy descriptors + for (int32 i = 0; i < count; i++) + { + _formatEtc[i] = fmtetc[i]; + _stgMedium[i] = stgmed[i]; + } + } + + virtual ~WindowsDragSource() + { + delete[] _formatEtc; + delete[] _stgMedium; + } + +public: + // [IUnknown] + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, _COM_Outptr_ void __RPC_FAR *__RPC_FAR * ppvObject) override + { + // Check to see what interface has been requested + if (riid == IID_IDataObject || riid == IID_IUnknown || riid == IID_IDropSource) + { + AddRef(); + *ppvObject = this; + return S_OK; + } + + // No interface + *ppvObject = nullptr; + return E_NOINTERFACE; + } + + ULONG STDMETHODCALLTYPE AddRef() override + { + _InterlockedIncrement(&_refCount); + return _refCount; + } + + ULONG STDMETHODCALLTYPE Release() override + { + ULONG ulRefCount = _InterlockedDecrement(&_refCount); + if (_refCount == 0) + { + delete this; + } + return ulRefCount; + } + + // [IDropSource] + HRESULT STDMETHODCALLTYPE QueryContinueDrag(_In_ BOOL fEscapePressed, _In_ DWORD grfKeyState) override + { + // If the Escape key has been pressed since the last call, cancel the drop + if (fEscapePressed == TRUE || grfKeyState & MK_RBUTTON) + return DRAGDROP_S_CANCEL; + + // If the LeftMouse button has been released, then do the drop! + if ((grfKeyState & MK_LBUTTON) == 0) + return DRAGDROP_S_DROP; + + // Continue with the drag-drop + return S_OK; + } + HRESULT STDMETHODCALLTYPE GiveFeedback(_In_ DWORD dwEffect) override + { + // TODO: allow to use custom mouse cursor during drop and drag operation + return DRAGDROP_S_USEDEFAULTCURSORS; + } + + // [IDataObject] + HRESULT STDMETHODCALLTYPE GetData(_In_ FORMATETC* pformatetcIn, _Out_ STGMEDIUM* pmedium) override + { + if (pformatetcIn == nullptr || pmedium == nullptr) + return E_INVALIDARG; + + // Try to match the specified FORMATETC with one of our supported formats + int32 index = lookupFormatEtc(pformatetcIn); + if (index == INVALID_INDEX) + return DV_E_FORMATETC; + + // Found a match - transfer data into supplied storage medium + pmedium->tymed = _formatEtc[index].tymed; + pmedium->pUnkForRelease = nullptr; + + // Copy the data into the caller's storage medium + switch (_formatEtc[index].tymed) + { + case TYMED_HGLOBAL: + pmedium->hGlobal = duplicateGlobalMem(_stgMedium[index].hGlobal); + break; + + default: + return DV_E_FORMATETC; + } + + return S_OK; + } + HRESULT STDMETHODCALLTYPE GetDataHere(_In_ FORMATETC* pformatetc, _Inout_ STGMEDIUM* pmedium) override + { + return DATA_E_FORMATETC; + } + HRESULT STDMETHODCALLTYPE QueryGetData(__RPC__in_opt FORMATETC* pformatetc) override + { + return lookupFormatEtc(pformatetc) == INVALID_INDEX ? DV_E_FORMATETC : S_OK; + } + HRESULT STDMETHODCALLTYPE GetCanonicalFormatEtc(__RPC__in_opt FORMATETC* pformatectIn, __RPC__out FORMATETC* pformatetcOut) override + { + // Apparently we have to set this field to NULL even though we don't do anything else + pformatetcOut->ptd = nullptr; + return E_NOTIMPL; + } + HRESULT STDMETHODCALLTYPE SetData(_In_ FORMATETC* pformatetc, _In_ STGMEDIUM* pmedium, BOOL fRelease) override + { + return E_NOTIMPL; + } + HRESULT STDMETHODCALLTYPE EnumFormatEtc(DWORD dwDirection, __RPC__deref_out_opt IEnumFORMATETC** ppenumFormatEtc) override + { + // Only the get direction is supported for OLE + if (dwDirection == DATADIR_GET) + { + // TODO: use SHCreateStdEnumFmtEtc API call + return CreateEnumFormatEtc(_formatsCount, _formatEtc, ppenumFormatEtc); + } + + // The direction specified is not supported for drag+drop + return E_NOTIMPL; + } + HRESULT STDMETHODCALLTYPE DAdvise(__RPC__in FORMATETC* pformatetc, DWORD advf, __RPC__in_opt IAdviseSink* pAdvSink, __RPC__out DWORD* pdwConnection) override + { + return OLE_E_ADVISENOTSUPPORTED; + } + HRESULT STDMETHODCALLTYPE DUnadvise(DWORD dwConnection) override + { + return OLE_E_ADVISENOTSUPPORTED; + } + HRESULT STDMETHODCALLTYPE EnumDAdvise(__RPC__deref_out_opt IEnumSTATDATA** ppenumAdvise) override + { + return OLE_E_ADVISENOTSUPPORTED; + } + +private: + int32 lookupFormatEtc(FORMATETC* pFormatEtc) const + { + // Check each of our formats in turn to see if one matches + for (int32 i = 0; i < _formatsCount; i++) + { + if ((_formatEtc[i].tymed & pFormatEtc->tymed) && + _formatEtc[i].cfFormat == pFormatEtc->cfFormat && + _formatEtc[i].dwAspect == pFormatEtc->dwAspect) + { + // Return index of stored format + return i; + } + } + + // Format not found + return INVALID_INDEX; + } +}; + +WindowsGuiData GuiDragDropData; + +/// +/// Async DoDragDrop helper (used for rendering frames during main thread stall). +/// +class DoDragDropJob : public ThreadPoolTask +{ +public: + int64 ExitFlag = 0; + + // [ThreadPoolTask] + bool Run() override + { + Scripting::GetScriptsDomain()->Dispatch(); + while (Platform::AtomicRead(&ExitFlag) == 0) + { + Engine::OnDraw(); + Platform::Sleep(20); + } + return false; + } +}; + +DragDropEffect WindowsWindow::DoDragDrop(const StringView& data) +{ + // Create background worker that will keep updating GUI (perform rendering) + const auto task = New(); + Task::StartNew(task); + while (task->GetState() == TaskState::Queued) + { + Platform::Sleep(1); + } + + // Create descriptors + FORMATETC fmtetc = { CF_TEXT, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; + STGMEDIUM stgmed = { TYMED_HGLOBAL, { nullptr }, nullptr }; + + // Create a HGLOBAL inside the storage medium + stgmed.hGlobal = StringToHandle(data); + + // Create drop source + auto dropSource = new WindowsDragSource(&fmtetc, &stgmed, 1); + + // Do the drag drop operation + DWORD dwEffect; + HRESULT result = ::DoDragDrop(dropSource, dropSource, DROPEFFECT_COPY | DROPEFFECT_MOVE | DROPEFFECT_LINK | DROPEFFECT_SCROLL, &dwEffect); + + // Wait for job end + Platform::AtomicStore(&task->ExitFlag, 1); + task->Wait(); + + // Release allocated data + dropSource->Release(); + ReleaseStgMedium(&stgmed); + + // Fix hanging mouse state (Windows doesn't send WM_LBUTTONUP when we end the drag and drop) + if (Input::GetMouseButton(MouseButton::Left)) + { + ::POINT point; + ::GetCursorPos(&point); + Input::Mouse->OnMouseUp(Float2((float)point.x, (float)point.y), MouseButton::Left, this); + } + + return SUCCEEDED(result) ? dropEffectFromOleEnum(dwEffect) : DragDropEffect::None; +} + +HRESULT WindowsWindow::DragEnter(Windows::IDataObject* pDataObj, Windows::DWORD grfKeyState, Windows::POINTL pt, Windows::DWORD* pdwEffect) +{ + POINT p = { pt.x, pt.y }; + ::ScreenToClient(_handle, &p); + GuiDragDropData.Init((IDataObject*)pDataObj); + DragDropEffect effect = DragDropEffect::None; + OnDragEnter(&GuiDragDropData, Float2(static_cast(p.x), static_cast(p.y)), effect); + Focus(); + *pdwEffect = dropEffect2OleEnum(effect); + return S_OK; +} + +HRESULT WindowsWindow::DragOver(Windows::DWORD grfKeyState, Windows::POINTL pt, Windows::DWORD* pdwEffect) +{ + POINT p = { pt.x, pt.y }; + ::ScreenToClient(_handle, &p); + DragDropEffect effect = DragDropEffect::None; + OnDragOver(&GuiDragDropData, Float2(static_cast(p.x), static_cast(p.y)), effect); + *pdwEffect = dropEffect2OleEnum(effect); + return S_OK; +} + +HRESULT WindowsWindow::DragLeave() +{ + OnDragLeave(); + return S_OK; +} + +HRESULT WindowsWindow::Drop(Windows::IDataObject* pDataObj, Windows::DWORD grfKeyState, Windows::POINTL pt, Windows::DWORD* pdwEffect) +{ + POINT p = { pt.x, pt.y }; + ::ScreenToClient(_handle, &p); + GuiDragDropData.Init((IDataObject*)pDataObj); + DragDropEffect effect = DragDropEffect::None; + OnDragDrop(&GuiDragDropData, Float2(static_cast(p.x), static_cast(p.y)), effect); + *pdwEffect = dropEffect2OleEnum(effect); + return S_OK; +} + +#else + +DragDropEffect WindowsWindow::DoDragDrop(const StringView& data) +{ + return DragDropEffect::None; +} + +#endif + #endif diff --git a/Source/Engine/Scripting/Scripting.cpp b/Source/Engine/Scripting/Scripting.cpp index a57833314..bdf9f60f5 100644 --- a/Source/Engine/Scripting/Scripting.cpp +++ b/Source/Engine/Scripting/Scripting.cpp @@ -28,8 +28,8 @@ #include "Engine/Content/Content.h" #include "Engine/Engine/EngineService.h" #include "Engine/Engine/Globals.h" +#include "Engine/Engine/Time.h" #include "Engine/Graphics/RenderTask.h" -#include "Engine/Platform/MemoryStats.h" #include "Engine/Serialization/JsonTools.h" extern void registerFlaxEngineInternalCalls(); @@ -193,6 +193,14 @@ void ScriptingService::Update() { PROFILE_CPU_NAMED("Scripting::Update"); INVOKE_EVENT(Update); + +#ifdef USE_NETCORE + // Force GC to run in background periodically to avoid large blocking collections causing hitches + if (Time::Update.TicksCount % 60 == 0) + { + MCore::GC::Collect(MCore::GC::MaxGeneration(), MGCCollectionMode::Forced, false, false); + } +#endif } void ScriptingService::LateUpdate()