From 3cd7c8314b98b34db5ddca5d3fd77227fb69a51c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Madar=C3=A1sz?= Date: Sun, 29 Dec 2024 10:49:31 +0100 Subject: [PATCH] feat: drop ultralight and use fw instead --- code/client/CMakeLists.txt | 6 +- code/client/src/core/application.cpp | 15 +- code/client/src/core/application.h | 6 +- code/client/src/core/ui/web/clipboard.cpp | 55 -- code/client/src/core/ui/web/clipboard.h | 13 - code/client/src/core/ui/web/manager.cpp | 145 ----- code/client/src/core/ui/web/manager.h | 58 -- code/client/src/core/ui/web/sdk.cpp | 70 --- code/client/src/core/ui/web/sdk.h | 53 -- code/client/src/core/ui/web/view.cpp | 682 ---------------------- code/client/src/core/ui/web/view.h | 98 ---- 11 files changed, 17 insertions(+), 1184 deletions(-) delete mode 100644 code/client/src/core/ui/web/clipboard.cpp delete mode 100644 code/client/src/core/ui/web/clipboard.h delete mode 100644 code/client/src/core/ui/web/manager.cpp delete mode 100644 code/client/src/core/ui/web/manager.h delete mode 100644 code/client/src/core/ui/web/sdk.cpp delete mode 100644 code/client/src/core/ui/web/sdk.h delete mode 100644 code/client/src/core/ui/web/view.cpp delete mode 100644 code/client/src/core/ui/web/view.h diff --git a/code/client/CMakeLists.txt b/code/client/CMakeLists.txt index 4283e79e..9aa3ad16 100644 --- a/code/client/CMakeLists.txt +++ b/code/client/CMakeLists.txt @@ -13,10 +13,6 @@ set(MAFIAMP_CLIENT_FILES src/core/ui/devs/debug_world.cpp src/core/ui/devs/entity_browser.cpp src/core/ui/devs/network_stats.cpp - src/core/ui/web/manager.cpp - src/core/ui/web/clipboard.cpp - src/core/ui/web/sdk.cpp - src/core/ui/web/view.cpp src/core/states/initialize.cpp src/core/states/main_menu.cpp src/core/states/session_connected.cpp @@ -131,7 +127,7 @@ set(MAFIAMP_SDK_FILES add_library(MafiaMPClient SHARED ${MAFIAMP_CLIENT_FILES} ${MAFIAMP_SDK_FILES}) target_include_directories(MafiaMPClient PRIVATE src) -target_link_libraries(MafiaMPClient Framework FrameworkClient shlwapi Lua54 DirectXTK UltralightSDK JavaScriptCorePP) +target_link_libraries(MafiaMPClient Framework FrameworkClient shlwapi Lua54 DirectXTK) target_link_options(MafiaMPClient PRIVATE /SAFESEH:NO /MANIFEST:NO) add_custom_command(TARGET MafiaMPClient POST_BUILD diff --git a/code/client/src/core/application.cpp b/code/client/src/core/application.cpp index 56f9192a..db3147d4 100644 --- a/code/client/src/core/application.cpp +++ b/code/client/src/core/application.cpp @@ -35,6 +35,8 @@ #include "modules/human.h" #include "modules/vehicle.h" +#include "game/module.h" + namespace MafiaMP::Core { std::unique_ptr gApplication = nullptr; @@ -62,10 +64,19 @@ namespace MafiaMP::Core { _input = std::make_shared(); _console = std::make_shared(_commandProcessor); _chat = std::make_shared(); - _webManager = std::make_shared(); + _webManager = std::make_shared(); if (_webManager) { - if (!_webManager->Init()) { + Framework::GUI::ViewportConfiguration vhConfiguration; + { + RECT vhRect; + GetClientRect(Game::gGlobals.window, &vhRect); + vhConfiguration = { + vhRect.right - vhRect.left, + vhRect.bottom - vhRect.top, + }; + } + if (!_webManager->Init(gProjectPath, vhConfiguration, GetRenderer())) { Framework::Logging::GetLogger("Web")->error("Failed to initialize web manager"); return false; } diff --git a/code/client/src/core/application.h b/code/client/src/core/application.h index d1ed7cd6..110f993e 100644 --- a/code/client/src/core/application.h +++ b/code/client/src/core/application.h @@ -10,7 +10,6 @@ #include "luavm.h" #include "ui/chat.h" #include "ui/console.h" -#include "ui/web/manager.h" #include "dev_features.h" @@ -19,6 +18,7 @@ #include #include #include +#include namespace MafiaMP::Core { class Application: public Framework::Integrations::Client::Instance { @@ -26,7 +26,7 @@ namespace MafiaMP::Core { std::shared_ptr _stateMachine; std::shared_ptr _console; std::shared_ptr _chat; - std::shared_ptr _webManager; + std::shared_ptr _webManager; std::shared_ptr _entityFactory; std::shared_ptr _commandProcessor; std::shared_ptr _input; @@ -98,7 +98,7 @@ namespace MafiaMP::Core { return _luaVM; } - std::shared_ptr GetWebManager() const { + std::shared_ptr GetWebManager() const { return _webManager; } diff --git a/code/client/src/core/ui/web/clipboard.cpp b/code/client/src/core/ui/web/clipboard.cpp deleted file mode 100644 index dedaf7b6..00000000 --- a/code/client/src/core/ui/web/clipboard.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include "clipboard.h" - -#include -#include - -namespace MafiaMP::Core::UI::Web { - void SystemClipboard::Clear() { - // win32 clear clipboard - OpenClipboard(nullptr); - EmptyClipboard(); - CloseClipboard(); - } - ultralight::String SystemClipboard::ReadPlainText() { - // Try to open the clipboard - if (!OpenClipboard(nullptr)) - return ""; // If opening clipboard failed, return an empty string - - // Get the clipboard data in CF_TEXT format - const HANDLE hData = GetClipboardData(CF_TEXT); - if (hData == nullptr) { - CloseClipboard(); // Close the clipboard if getting data failed - return ""; - } - - // Lock the handle to get the actual text pointer - const auto pszText = static_cast(GlobalLock(hData)); - if (pszText == nullptr) { - CloseClipboard(); // Close the clipboard if lock failed - return ""; - } - - // Copy the text data to a std::string instance - const std::string text(pszText); - - // Unlock the handle and close the clipboard - GlobalUnlock(hData); - CloseClipboard(); - - return text.c_str(); - } - void SystemClipboard::WritePlainText(const ultralight::String &text) { - if (!OpenClipboard(nullptr)) { - return; - } - EmptyClipboard(); - HGLOBAL hClipboardData; - const size_t size = text.utf8().length() + 1; - hClipboardData = GlobalAlloc(GMEM_DDESHARE, size); - const auto pchData = static_cast(GlobalLock(hClipboardData)); - memcpy(pchData, text.utf8().data(), size); - GlobalUnlock(hClipboardData); - SetClipboardData(CF_TEXT, hClipboardData); - CloseClipboard(); - } -} // namespace MafiaMP::Core::UI::Web diff --git a/code/client/src/core/ui/web/clipboard.h b/code/client/src/core/ui/web/clipboard.h deleted file mode 100644 index f77a0f26..00000000 --- a/code/client/src/core/ui/web/clipboard.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once -#include - -#include - -namespace MafiaMP::Core::UI::Web { - class SystemClipboard final: public ultralight::Clipboard { - public: - void Clear() override; - ultralight::String ReadPlainText() override; - void WritePlainText(const ultralight::String &text) override; - }; -} // namespace MafiaMP::Core::UI::Web diff --git a/code/client/src/core/ui/web/manager.cpp b/code/client/src/core/ui/web/manager.cpp deleted file mode 100644 index 82d0fdb3..00000000 --- a/code/client/src/core/ui/web/manager.cpp +++ /dev/null @@ -1,145 +0,0 @@ -#include "manager.h" - -#include - -#include "game/module.h" - -namespace MafiaMP::Core::UI::Web { - Manager::Manager() { - _clipboard = std::make_unique(); - } - - Manager::~Manager() { - // Destroy the views - for (auto &view : _views) { - view.reset(); - } - - // Destroy the renderer - if (_renderer) { - _renderer->Release(); - } - } - - bool Manager::Init() { - // Initialize the viewport configuration - RECT vhRect; - GetClientRect(Game::gGlobals.window, &vhRect); - _viewportConfiguration = { - vhRect.right - vhRect.left, - vhRect.bottom - vhRect.top, - }; - - // Initialize the configuration - ultralight::Config rendererConfig; - rendererConfig.cache_path = (gProjectPath + "/cache").c_str(); - - // Initialize the platform - ultralight::Platform::instance().set_config(rendererConfig); - ultralight::Platform::instance().set_clipboard(_clipboard.get()); - ultralight::Platform::instance().set_font_loader(ultralight::GetPlatformFontLoader()); - ultralight::Platform::instance().set_file_system(ultralight::GetPlatformFileSystem(gProjectPath.c_str())); - ultralight::Platform::instance().set_logger(ultralight::GetDefaultLogger((gProjectPath + "/logs/web_manager.log").c_str())); - - // Initialize the renderer - _renderer = ultralight::Renderer::Create(); - if (!_renderer) { - Framework::Logging::GetLogger("Web")->error("Failed to initialize renderer"); - return false; - } - - return true; - } - - void Manager::Update() { - if (!_renderer) { - return; - } - - // Update the renderer - std::lock_guard lock(_renderMutex); - _renderer->Update(); - _renderer->Render(); - _renderer->RefreshDisplay(0); - - // Update the views - for (auto &view : _views) { - view->Update(); - } - } - - void Manager::Render() { - if (!_renderer) { - return; - } - - // Render the views - for (auto &view : _views) { - view->Render(); - } - } - - void Manager::ProcessMouseEvent(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) const { - for (auto &view : _views) { - view->ProcessMouseEvent(hWnd, msg, wParam, lParam); - } - } - - void Manager::ProcessKeyboardEvent(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) const { - for (auto &view : _views) { - view->ProcessKeyboardEvent(hWnd, msg, wParam, lParam); - } - } - - int Manager::CreateView(std::string url, int width, int height) { - if (!_renderer) { - Framework::Logging::GetLogger("Web")->error("Failed to create view: Renderer is not initialized"); - return -1; - } - - // Create the view - auto view = std::make_unique(_renderer.get()); - if (!view || !view.get()) { - Framework::Logging::GetLogger("Web")->error("Failed to create view: failed"); - return -1; - } - - if (!view->Init(url, width, height)) { - Framework::Logging::GetLogger("Web")->error("Failed to create view: initialization failed"); - return -1; - } - - // Add the view to the list - _views.push_back(std::move(view)); - - // Return the view id - const auto viewId = _views.size() - 1; - - // Log the view creation - Framework::Logging::GetLogger("Web")->debug("Created view with id {}", viewId); - return viewId; - } - - bool Manager::DestroyView(int id) { - if (!_renderer) { - Framework::Logging::GetLogger("Web")->error("Failed to destroy view: Renderer is not initialized"); - return false; - } - - // Check if the view exists - if (id < 0 || id >= _views.size()) { - Framework::Logging::GetLogger("Web")->error("Failed to destroy view: View does not exist"); - return false; - } - - // Destroy the view - _views[id].reset(); - - // Remove the view from the list - _views.erase(_views.begin() + id); - - Framework::Logging::GetLogger("Web")->debug("Destroyed view with id {}", id); - - return true; - } -} // namespace MafiaMP::Core::UI::Web diff --git a/code/client/src/core/ui/web/manager.h b/code/client/src/core/ui/web/manager.h deleted file mode 100644 index 22c3c56d..00000000 --- a/code/client/src/core/ui/web/manager.h +++ /dev/null @@ -1,58 +0,0 @@ -#pragma once - -#include - -#include -#include -#include - -#include -#include - -#include "clipboard.h" -#include "view.h" - -namespace MafiaMP::Core::UI::Web { - struct ViewportConfiguration { - int width; - int height; - }; - - class Manager { - private: - ViewportConfiguration _viewportConfiguration; - ultralight::RefPtr _renderer; - - std::recursive_mutex _renderMutex; - - std::vector> _views; - std::unique_ptr _clipboard; - - public: - Manager(); - ~Manager(); - - bool Init(); - - int CreateView(std::string, int, int); - bool DestroyView(int); - - void Update(); - void Render(); - - void ProcessMouseEvent(HWND, UINT, WPARAM, LPARAM) const; - void ProcessKeyboardEvent(HWND, UINT, WPARAM, LPARAM) const; - - void SetViewportConfiguration(const ViewportConfiguration &viewportConfiguration) { - _viewportConfiguration = viewportConfiguration; - } - - ViewportConfiguration GetViewportConfiguration() const { - return _viewportConfiguration; - } - - View *GetView(int id) const { - return _views[id].get(); - } - }; -} // namespace MafiaMP::Core::UI::Web diff --git a/code/client/src/core/ui/web/sdk.cpp b/code/client/src/core/ui/web/sdk.cpp deleted file mode 100644 index ea3711bb..00000000 --- a/code/client/src/core/ui/web/sdk.cpp +++ /dev/null @@ -1,70 +0,0 @@ -#include "sdk.h" - -#include - -#include - -namespace MafiaMP::Core::UI::Web { - bool SDK::Init(ultralight::View* caller) { - // Bind the view reference - _view = caller; - - // Initialize our context - auto context = _view->LockJSContext(); - _context = (*context); - - // Bind our global object - _globalObject = JSContextGetGlobalObject(_context); - - // Initialize the builtin SDK - InitEventHandlers(); - return true; - } - - bool SDK::Shutdown() { - return true; - } - - void SDK::InitEventHandlers() { - // Grab the context - auto context = JavaScriptCorePP::JSContext(_context); - auto obj = context.GetGlobalObject(); - - // Bind the callEvent method - obj["callEvent"] = [this](const JavaScriptCorePP::JSContext &context, const std::vector &args, JavaScriptCorePP::JSValue &returnValue, JavaScriptCorePP::JSValue &returnException) { - // Make sure there is only two arguments - if (args.size() != 2) { - returnException = context.CreateString("Invalid argument count: callEvent(string, string | null)"); - return; - } - - std::string eventName; - std::string eventPayload; - - // Grab the event name - must be a string - if (args[0].IsString()) { - eventName = args[0].GetString(); - } - else { - returnException = context.CreateString("First argument must be a string"); - return; - } - - // Grab the event payload - must be a string or null - if (args[1].IsString()) { - eventPayload = args[1].GetString(); - } - else if (args[1].IsNull() || args[1].IsUndefined()) { - eventPayload = ""; - } - else { - returnException = context.CreateString("Second argument must be a string or null"); - return; - } - - // Process all event callbacks - BroadcastEvent(eventName, eventPayload); - returnValue = context.CreateBoolean(true); - }; - } -} diff --git a/code/client/src/core/ui/web/sdk.h b/code/client/src/core/ui/web/sdk.h deleted file mode 100644 index 2642974c..00000000 --- a/code/client/src/core/ui/web/sdk.h +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include -#include - -namespace MafiaMP::Core::UI::Web { - using EventCallbackProc = std::function; - - class SDK { - private: - ultralight::View *_view; - std::unordered_map _eventListeners; - - JSContextRef _context; - JSObjectRef _globalObject; - - public: - bool Init(ultralight::View *); - bool Shutdown(); - - inline void AddEventListener(std::string eventName, EventCallbackProc proc) { - _eventListeners[eventName] = proc; - } - - inline void RemoveEventListener(std::string eventName) { - // Make sure the event exist - if (_eventListeners.find(eventName) == _eventListeners.end()) { - return; - } - - // Remove the event - _eventListeners.erase(eventName); - } - - inline void BroadcastEvent(std::string eventName, std::string eventPayload) { - // Make sure the event exist - if (_eventListeners.find(eventName) == _eventListeners.end()) { - return; - } - - // Process all the event callback - _eventListeners[eventName](eventPayload); - } - - private: - void InitEventHandlers(); - }; -} diff --git a/code/client/src/core/ui/web/view.cpp b/code/client/src/core/ui/web/view.cpp deleted file mode 100644 index a9862dff..00000000 --- a/code/client/src/core/ui/web/view.cpp +++ /dev/null @@ -1,682 +0,0 @@ -#include "view.h" - -#include "game/module.h" - -// DirectX -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below. -#endif - -#include - -struct CursorData { - void *pixels; - int w, h; -}; - -struct WebData { - ID3D11Buffer *pVB {}; - ID3D11Buffer *pIB {}; - ID3D11VertexShader *pVertexShader {}; - ID3D11InputLayout *pInputLayout {}; - ID3D11PixelShader *pPixelShader {}; - ID3D11SamplerState *pSampler {}; - ID3D11RasterizerState *pRasterizerState {}; - ID3D11BlendState *pBlendState {}; - ID3D11DepthStencilState *pDepthStencilState {}; - std::unordered_map _cursors; -}; - -static WebData *bd = new WebData; - -struct VERTEX { - float x, y; - float u, v; -}; - -namespace MafiaMP::Core::UI::Web { - View::View(ultralight::RefPtr renderer): _renderer(renderer), _pixelData(nullptr), _d3dInitialized(false), _width(0), _height(0) { - _sdk = new SDK; - } - - View::~View() { - if (_sdk) { - _sdk->Shutdown(); - delete _sdk; - } - } - - bool View::Init(std::string &path, int width, int height) { - // Load default cursor - LoadCursorData(ultralight::kCursor_Pointer); - - // Initialize a view configuration - ultralight::ViewConfig config; - config.is_accelerated = false; - config.is_transparent = true; - config.initial_focus = false; - - // Initialize the internal view - _internalView = _renderer->CreateView(width, height, config, nullptr); - if (!_internalView || !_internalView.get()) { - return false; - } - - // Bind the listeners to the internal view - _internalView->set_view_listener(this); - _internalView->set_load_listener(this); - - // Load the initial URL - _internalView->LoadURL(path.c_str()); - - // Store the width/height - _width = width; - _height = height; - return true; - } - - void View::InitD3D() { - // No need to initialize twice - if (_d3dInitialized) { - return; - } - - // Acquire the rendering devices from the application - const auto device = gApplication->GetRenderer()->GetD3D11Backend()->GetDevice(); - const auto renderCtx = gApplication->GetRenderer()->GetD3D11Backend()->GetContext(); - if (!device || !renderCtx) { - return; - } - - { - static const char *vertexShader = "struct VS_INPUT\ - {\ - float2 pos : POSITION;\ - float2 uv : TEXCOORD0;\ - };\ - \ - struct PS_INPUT\ - {\ - float4 pos : SV_POSITION;\ - float2 uv : TEXCOORD0;\ - };\ - \ - PS_INPUT main(VS_INPUT input)\ - {\ - PS_INPUT output;\ - output.pos = float4(input.pos.xy, 0.f, 1.f);\ - output.uv = input.uv;\ - return output;\ - }"; - - ID3DBlob *vertexShaderBlob; - if (FAILED(D3DCompile(vertexShader, strlen(vertexShader), NULL, NULL, NULL, "main", "vs_4_0", 0, 0, &vertexShaderBlob, NULL))) - return; // NB: Pass ID3DBlob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob! - if (device->CreateVertexShader(vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(), NULL, &bd->pVertexShader) != S_OK) { - vertexShaderBlob->Release(); - return; - } - - // Create the input layout - D3D11_INPUT_ELEMENT_DESC local_layout[] = { - {"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)0, D3D11_INPUT_PER_VERTEX_DATA, 0}, - {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)8, D3D11_INPUT_PER_VERTEX_DATA, 0}, - }; - if (device->CreateInputLayout(local_layout, 2, vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(), &bd->pInputLayout) != S_OK) { - vertexShaderBlob->Release(); - return; - } - vertexShaderBlob->Release(); - } - - // Create the pixel shader - { - static const char *pixelShader = "struct PS_INPUT\ - {\ - float4 pos : SV_POSITION;\ - float2 uv : TEXCOORD0;\ - };\ - sampler sampler0;\ - Texture2D texture0;\ - \ - float4 main(PS_INPUT input) : SV_Target\ - {\ - float4 out_col = texture0.Sample(sampler0, input.uv); \ - return out_col; \ - }"; - - ID3DBlob *pixelShaderBlob; - if (FAILED(D3DCompile(pixelShader, strlen(pixelShader), NULL, NULL, NULL, "main", "ps_4_0", 0, 0, &pixelShaderBlob, NULL))) - return; // NB: Pass ID3DBlob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob! - if (device->CreatePixelShader(pixelShaderBlob->GetBufferPointer(), pixelShaderBlob->GetBufferSize(), NULL, &bd->pPixelShader) != S_OK) { - pixelShaderBlob->Release(); - return; - } - pixelShaderBlob->Release(); - } - - // Create the blending setup - { - D3D11_BLEND_DESC desc; - ZeroMemory(&desc, sizeof(desc)); - desc.AlphaToCoverageEnable = false; - desc.RenderTarget[0].BlendEnable = true; - desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA; - desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA; - desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; - desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE; - desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA; - desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; - desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; - device->CreateBlendState(&desc, &bd->pBlendState); - } - - // Create the rasterizer state - { - D3D11_RASTERIZER_DESC desc; - ZeroMemory(&desc, sizeof(desc)); - desc.FillMode = D3D11_FILL_SOLID; - desc.CullMode = D3D11_CULL_NONE; - desc.ScissorEnable = true; - desc.DepthClipEnable = true; - device->CreateRasterizerState(&desc, &bd->pRasterizerState); - } - - // Create depth-stencil State - { - D3D11_DEPTH_STENCIL_DESC desc; - ZeroMemory(&desc, sizeof(desc)); - desc.DepthEnable = false; - desc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL; - desc.DepthFunc = D3D11_COMPARISON_ALWAYS; - desc.StencilEnable = false; - desc.FrontFace.StencilFailOp = desc.FrontFace.StencilDepthFailOp = desc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP; - desc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS; - desc.BackFace = desc.FrontFace; - device->CreateDepthStencilState(&desc, &bd->pDepthStencilState); - } - - // Create vertex/index buffers - D3D11_BUFFER_DESC desc; - memset(&desc, 0, sizeof(D3D11_BUFFER_DESC)); - desc.Usage = D3D11_USAGE_DYNAMIC; - desc.ByteWidth = 6 * sizeof(VERTEX); - desc.BindFlags = D3D11_BIND_VERTEX_BUFFER; - desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; - desc.MiscFlags = 0; - if (device->CreateBuffer(&desc, NULL, &bd->pVB) < 0) - return; - - memset(&desc, 0, sizeof(D3D11_BUFFER_DESC)); - desc.Usage = D3D11_USAGE_DYNAMIC; - desc.ByteWidth = 6 * sizeof(unsigned); - desc.BindFlags = D3D11_BIND_INDEX_BUFFER; - desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; - if (device->CreateBuffer(&desc, NULL, &bd->pIB) < 0) - return; - - // Upload vertex/index data into a single contiguous GPU buffer - D3D11_MAPPED_SUBRESOURCE vtx_resource, idx_resource; - if (renderCtx->Map(bd->pVB, 0, D3D11_MAP_WRITE_DISCARD, 0, &vtx_resource) != S_OK) - return; - if (renderCtx->Map(bd->pIB, 0, D3D11_MAP_WRITE_DISCARD, 0, &idx_resource) != S_OK) - return; - - static VERTEX vertices[] = { - // First triangle - {-1.0f, -1.0f, 0.0f, 1.0f}, // Bottom left - {1.0f, -1.0f, 1.0f, 1.0f}, // Bottom right - {-1.0f, 1.0f, 0.0f, 0.0f}, // Top left - - // Second triangle - {1.0f, -1.0f, 1.0f, 1.0f}, // Bottom right - {1.0f, 1.0f, 1.0f, 0.0f}, // Top right - {-1.0f, 1.0f, 0.0f, 0.0f}, // Top left - }; - - static unsigned short indices[] = { - 0, 1, 2, // Indices for the first triangle - 3, 4, 5 // Indices for the second triangle - }; - - VERTEX *vtx_dst = (VERTEX *)vtx_resource.pData; - unsigned *idx_dst = (unsigned *)idx_resource.pData; - memcpy(vtx_dst, vertices, 6 * sizeof(VERTEX)); - memcpy(idx_dst, indices, 6 * sizeof(unsigned short)); - renderCtx->Unmap(bd->pVB, 0); - renderCtx->Unmap(bd->pIB, 0); - - // Create texture sampler - { - D3D11_SAMPLER_DESC desc; - ZeroMemory(&desc, sizeof(desc)); - desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; - desc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP; - desc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP; - desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; - desc.MipLODBias = 0.f; - desc.ComparisonFunc = D3D11_COMPARISON_ALWAYS; - desc.MinLOD = 0.f; - desc.MaxLOD = 0.f; - device->CreateSamplerState(&desc, &bd->pSampler); - } - - _d3dInitialized = true; - } - - void View::ResetTextures() { - std::lock_guard lock(_renderMutex); - - // Grab the device & context - const auto device = gApplication->GetRenderer()->GetD3D11Backend()->GetDevice(); - const auto renderCtx = gApplication->GetRenderer()->GetD3D11Backend()->GetContext(); - - if (!device || !renderCtx) { - return; - } - - // TODO: Destroy the texture if window size has changed - if (!_texture) { - D3D11_TEXTURE2D_DESC textureDesc; - ZeroMemory(&textureDesc, sizeof(textureDesc)); - textureDesc.Width = _width; // width of the texture - textureDesc.Height = _height; // height of the texture - textureDesc.MipLevels = 1; - textureDesc.ArraySize = 1; - textureDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; // texture format - textureDesc.SampleDesc.Count = 1; - textureDesc.SampleDesc.Quality = 0; - textureDesc.Usage = D3D11_USAGE_DEFAULT; - textureDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE; // bind to shader - textureDesc.CPUAccessFlags = 0; - textureDesc.MiscFlags = 0; - - // Step 2: Create the texture - device->CreateTexture2D(&textureDesc, nullptr, &_texture); - - // Step 3: Create a shader-resource view - if (FAILED(device->CreateShaderResourceView(_texture, nullptr, &_textureView))) { - Framework::Logging::GetLogger("Web")->error("Failed to create texture view"); - return; - } - } - - D3D11_BOX destRegion; - destRegion.left = 0; - destRegion.right = _width; - destRegion.top = 0; - destRegion.bottom = _height; - destRegion.front = 0; - destRegion.back = 1; - const auto rowPitch = _width * 4; - const auto depthPitch = rowPitch * _height; - - renderCtx->UpdateSubresource(_texture, 0, &destRegion, _pixelData, rowPitch, depthPitch); - } - - void View::LoadCursorData(ultralight::Cursor cursor) { - using namespace ultralight; - - if (bd->_cursors.find(cursor) != bd->_cursors.end()) - return; - - auto cursorId = IDC_ARROW; - - switch (cursor) { - case kCursor_Pointer: cursorId = IDC_ARROW; break; - case kCursor_Cross: cursorId = IDC_CROSS; break; - case kCursor_Hand: cursorId = IDC_HAND; break; - case kCursor_IBeam: cursorId = IDC_IBEAM; break; - case kCursor_Wait: cursorId = IDC_WAIT; break; - case kCursor_Help: cursorId = IDC_HELP; break; - case kCursor_EastResize: cursorId = IDC_SIZEWE; break; - case kCursor_NorthResize: cursorId = IDC_SIZENS; break; - case kCursor_NorthEastResize: cursorId = IDC_SIZENESW; break; - case kCursor_NorthWestResize: cursorId = IDC_SIZENWSE; break; - case kCursor_SouthResize: cursorId = IDC_SIZENS; break; - case kCursor_SouthEastResize: cursorId = IDC_SIZENWSE; break; - case kCursor_SouthWestResize: cursorId = IDC_SIZENESW; break; - case kCursor_WestResize: cursorId = IDC_SIZEWE; break; - case kCursor_NorthSouthResize: cursorId = IDC_SIZENS; break; - case kCursor_EastWestResize: cursorId = IDC_SIZEWE; break; - case kCursor_NorthEastSouthWestResize: cursorId = IDC_SIZENESW; break; - case kCursor_NorthWestSouthEastResize: cursorId = IDC_SIZENWSE; break; - // The following cursors might not have direct Win32 equivalents. - case kCursor_ColumnResize: - case kCursor_RowResize: - case kCursor_MiddlePanning: - case kCursor_EastPanning: - case kCursor_NorthPanning: - case kCursor_NorthEastPanning: - case kCursor_NorthWestPanning: - case kCursor_SouthPanning: - case kCursor_SouthEastPanning: - case kCursor_SouthWestPanning: - case kCursor_WestPanning: - case kCursor_Move: - case kCursor_VerticalText: - case kCursor_Cell: - case kCursor_ContextMenu: - case kCursor_Alias: - case kCursor_Progress: - case kCursor_NoDrop: - case kCursor_Copy: - case kCursor_None: - case kCursor_NotAllowed: - case kCursor_ZoomIn: - case kCursor_ZoomOut: - case kCursor_Grab: - case kCursor_Grabbing: - case kCursor_Custom: - // Use a custom cursor or a default one like IDC_ARROW - cursorId = IDC_ARROW; - break; - default: cursorId = IDC_ARROW; break; - } - - // Load the system cursor - HCURSOR hCursor = (HCURSOR)LoadCursor(0, cursorId); - if (hCursor == NULL) { - DWORD dwError = GetLastError(); - printf("LoadImage failed with error %lu\n", dwError); - if (cursor != kCursor_Pointer) { - // Load fallback arrow cursor if we can't map the one requested. - bd->_cursors[cursor] = bd->_cursors[kCursor_Pointer]; - } - return; - } - - ICONINFO iconInfo = {0}; - GetIconInfo(hCursor, &iconInfo); - - HDC hdc = GetDC(nullptr); - - BITMAP bmpColor, bmpMask; - if (iconInfo.hbmColor) - GetObject(iconInfo.hbmColor, sizeof(BITMAP), &bmpColor); - if (iconInfo.hbmMask) { - GetObject(iconInfo.hbmMask, sizeof(BITMAP), &bmpMask); - /*For monochrome icons, the hbmMask is twice the height of the icon (with the AND mask on top and the XOR mask on the bottom), and hbmColor is NULL. - Also, in this case the height should be an even multiple of two.*/ - bmpMask.bmHeight /= 2; - } - - BITMAP *bmp = iconInfo.hbmColor ? &bmpColor : &bmpMask; - - // Prepare a buffer to receive the bitmap data - BITMAPINFO biColor = {0}; - biColor.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); - biColor.bmiHeader.biWidth = bmp->bmWidth; - biColor.bmiHeader.biHeight = -bmp->bmHeight; - biColor.bmiHeader.biPlanes = 1; - biColor.bmiHeader.biBitCount = 32; - biColor.bmiHeader.biCompression = BI_RGB; - - // Get the actual bitmap data - BYTE *pPixels = new BYTE[bmp->bmWidth * bmp->bmHeight * 4]; - memset(pPixels, 0x00, bmp->bmWidth * bmp->bmHeight * 4); - - if (bmp == &bmpMask) { - char *pPixelMask = new char[bmp->bmWidth * bmp->bmHeight * 8]; - GetDIBits(hdc, iconInfo.hbmMask, 0, bmp->bmHeight, &pPixelMask[0], &biColor, DIB_RGB_COLORS); - - for (int y = 0; y < bmp->bmHeight; y++) { - for (int x = 0; x < bmp->bmWidth; x++) { - int i = (y * bmp->bmWidth + x) * 4; - - bool andMask = pPixelMask[i] != 0; - bool xorMask = pPixelMask[i + (bmp->bmHeight * bmp->bmWidthBytes)] != 0; - - if (!andMask) { - *(DWORD *)&pPixels[i] = !!xorMask ? 0xFFFFFFFF : 0xFF000000; - } - } - } - - delete[] pPixelMask; - } - else { - GetDIBits(hdc, iconInfo.hbmColor, 0, bmp->bmHeight, &pPixels[0], &biColor, DIB_RGB_COLORS); - } - - // Now we can populate CursorData - CursorData cursorData; - cursorData.pixels = pPixels; - cursorData.w = bmp->bmWidth; - cursorData.h = bmp->bmHeight; - bd->_cursors[cursor] = cursorData; - - // Perform cleanup - ReleaseDC(NULL, hdc); - DeleteObject(iconInfo.hbmColor); - DeleteObject(iconInfo.hbmMask); - DestroyIcon(hCursor); - } - - void View::Update() { - if (!_internalView || !_shouldDisplay) { - return; - } - - std::lock_guard lock(_renderMutex); - - // Update the view content - auto surface = (ultralight::BitmapSurface *)_internalView->surface(); - void *pixels = surface->LockPixels(); - int size = surface->size(); // TODO: calc from res - // TODO: Realloc if size changes - if (!_pixelData) { - _pixelData = new uint8_t[size]; - } - memcpy(_pixelData, pixels, size); - surface->UnlockPixels(); - - // Render the cursor - RenderCursor(); - } - - void View::Render() { - if (!_internalView || !_shouldDisplay) { - return; - } - - std::lock_guard lock(_renderMutex); - - // Make sure we have D3D setup - InitD3D(); - - // Make sure we have textures - if (!_pixelData) { - return; - } - - // Grab the device & context - const auto device = gApplication->GetRenderer()->GetD3D11Backend()->GetDevice(); - const auto renderCtx = gApplication->GetRenderer()->GetD3D11Backend()->GetContext(); - - // Reset the textures - ResetTextures(); - - if (!_texture || !_textureView) { - return; - } - - // Apply scissor/clipping rectangle - const D3D11_RECT r = {0, 0, _width, _height}; - renderCtx->RSSetScissorRects(1, &r); - - // Setup shader and vertex buffers - unsigned int stride = sizeof(VERTEX); - unsigned int offset = 0; - renderCtx->IASetInputLayout(bd->pInputLayout); - renderCtx->IASetVertexBuffers(0, 1, &bd->pVB, &stride, &offset); - renderCtx->IASetIndexBuffer(bd->pIB, DXGI_FORMAT_R16_UINT, 0); - renderCtx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); - renderCtx->VSSetShader(bd->pVertexShader, NULL, 0); - renderCtx->VSSetConstantBuffers(0, 0, NULL); - renderCtx->PSSetShader(bd->pPixelShader, NULL, 0); - renderCtx->PSSetSamplers(0, 1, &bd->pSampler); - renderCtx->GSSetShader(NULL, NULL, 0); - renderCtx->HSSetShader(NULL, NULL, 0); // In theory we should backup and restore this as well.. very infrequently used.. - renderCtx->DSSetShader(NULL, NULL, 0); // In theory we should backup and restore this as well.. very infrequently used.. - renderCtx->CSSetShader(NULL, NULL, 0); // In theory we should backup and restore this as well.. very infrequently used.. - - // Setup blend state - const float blend_factor[4] = {0.f, 0.f, 0.f, 0.f}; - renderCtx->OMSetBlendState(bd->pBlendState, blend_factor, 0xffffffff); - renderCtx->OMSetDepthStencilState(bd->pDepthStencilState, 0); - renderCtx->RSSetState(bd->pRasterizerState); - renderCtx->PSSetShaderResources(0, 1, &_textureView); - renderCtx->DrawIndexed(6, 0, 0); - } - - void View::RenderCursor() { - if (bd->_cursors.find(_cursor) == bd->_cursors.end()) - return; - - const auto &cursorData = bd->_cursors[_cursor]; - for (int y = 0; y < cursorData.h; y++) { - for (int x = 0; x < cursorData.w; x++) { - int i = (y * cursorData.w + x) * 4; - int bx = _cursorPos.x + x; - int by = _cursorPos.y + y; - - if (bx >= 0 && bx < _width && by >= 0 && by < _height) { - int bi = (by * _width + bx) * 4; - char *cursorPixels = (char *)cursorData.pixels; - if (cursorPixels[i + 3]) { - _pixelData[bi] = cursorPixels[i + 0]; // B - _pixelData[bi + 1] = cursorPixels[i + 1]; // G - _pixelData[bi + 2] = cursorPixels[i + 2]; // R - } - } - } - } - } - - void View::ProcessMouseEvent(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { - if (!_internalView || !_shouldDisplay) { - return; - } - - if (!_internalView->HasFocus()) { - return; - } - - // Handle the mouse wheel event as separate from the other mouse events - if (msg == WM_MOUSEWHEEL) { - ultralight::ScrollEvent ev; - ev.type = ultralight::ScrollEvent::kType_ScrollByPixel; - ev.delta_x = 0; - ev.delta_y = GET_WHEEL_DELTA_WPARAM(wParam) * 0.8; - _internalView->FireScrollEvent(ev); - return; - } - - // Handle other classic mouse events - ultralight::MouseEvent ev; - ev.x = LOWORD(lParam); // TODO: revamp this because it fails on multiple monitors setups - ev.y = HIWORD(lParam); // TODO: revamp this because it fails on multiple monitors setups - switch (msg) { - case WM_MOUSEMOVE: { - ev.type = ultralight::MouseEvent::kType_MouseMoved; - ev.button = ultralight::MouseEvent::kButton_None; - _cursorPos = {ev.x, ev.y}; - } break; - case WM_LBUTTONDOWN: { - ev.type = ultralight::MouseEvent::kType_MouseDown; - ev.button = ultralight::MouseEvent::kButton_Left; - } break; - case WM_LBUTTONUP: { - ev.type = ultralight::MouseEvent::kType_MouseUp; - ev.button = ultralight::MouseEvent::kButton_Left; - } break; - case WM_RBUTTONDOWN: { - ev.type = ultralight::MouseEvent::kType_MouseDown; - ev.button = ultralight::MouseEvent::kButton_Right; - } break; - case WM_RBUTTONUP: { - ev.type = ultralight::MouseEvent::kType_MouseUp; - ev.button = ultralight::MouseEvent::kButton_Right; - } break; - case WM_MBUTTONDOWN: { - ev.type = ultralight::MouseEvent::kType_MouseDown; - ev.button = ultralight::MouseEvent::kButton_Middle; - } break; - case WM_MBUTTONUP: { - ev.type = ultralight::MouseEvent::kType_MouseUp; - ev.button = ultralight::MouseEvent::kButton_Middle; - } break; - } - - _internalView->FireMouseEvent(ev); - } - - void View::ProcessKeyboardEvent(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { - if (!_internalView || !_shouldDisplay) { - return; - } - - if (!_internalView->HasFocus()) { - return; - } - - ultralight::KeyEvent ev; - switch (msg) { - case WM_KEYDOWN: { - ev.type = ultralight::KeyEvent::kType_RawKeyDown; - } break; - case WM_KEYUP: { - ev.type = ultralight::KeyEvent::kType_KeyUp; - } break; - case WM_CHAR: { - char key[2] = {(char)wParam, 0}; - ev.type = ultralight::KeyEvent::kType_Char; - ev.text = key; - ev.unmodified_text = ev.text; - - // Make sure that pressing enter does not trigger this event - if (key[0] == 13) { - return; - } - } break; - } - - ev.virtual_key_code = wParam; - ev.native_key_code = lParam; - - const bool ctrlPressed = GetKeyState(VK_CONTROL) & 0x8000; - const bool shiftPressed = GetKeyState(VK_SHIFT) & 0x8000; - const bool altPressed = GetKeyState(VK_MENU) & 0x8000; - ev.modifiers = (ctrlPressed ? ultralight::KeyEvent::kMod_CtrlKey : 0) | (shiftPressed ? ultralight::KeyEvent::kMod_ShiftKey : 0) | (altPressed ? ultralight::KeyEvent::kMod_AltKey : 0); - - ultralight::GetKeyIdentifierFromVirtualKeyCode(ev.virtual_key_code, ev.key_identifier); - _internalView->FireKeyEvent(ev); - } - - void View::OnAddConsoleMessage(ultralight::View *caller, const ultralight::ConsoleMessage &message) { - Framework::Logging::GetLogger("Web")->debug("Console message: {}:{}:{}:{}", message.message().utf8().data(), message.line_number(), message.column_number(), message.source_id().utf8().data()); - } - - void View::OnDOMReady(ultralight::View *caller, uint64_t frame_id, bool is_main_frame, const ultralight::String &url) { - Framework::Logging::GetLogger("Web")->debug("DOM ready"); - } - - void View::OnWindowObjectReady(ultralight::View *caller, uint64_t frame_id, bool is_main_frame, const ultralight::String &url) { - Framework::Logging::GetLogger("Web")->debug("Window object ready"); - - // Bind the SDK - _sdk->Init(caller); - } - - void View::OnChangeCursor(ultralight::View *caller, ultralight::Cursor cursor) { - _cursor = cursor; - - if (bd->_cursors.find(cursor) == bd->_cursors.end()) { - LoadCursorData(cursor); - } - } -} // namespace MafiaMP::Core::UI::Web diff --git a/code/client/src/core/ui/web/view.h b/code/client/src/core/ui/web/view.h deleted file mode 100644 index 794b66a4..00000000 --- a/code/client/src/core/ui/web/view.h +++ /dev/null @@ -1,98 +0,0 @@ -#pragma once - -#include - -#include -#include -#include -#include -#include - -#include - -#include - -#include "sdk.h" - -namespace MafiaMP::Core::UI::Web { - - class View - : public ultralight::ViewListener - , ultralight::LoadListener { - private: - ultralight::RefPtr _renderer; - ultralight::RefPtr _internalView = nullptr; - - SDK *_sdk = nullptr; - - uint8_t *_pixelData; - int _width; - int _height; - bool _d3dInitialized; - bool _shouldDisplay = false; - - ID3D11Texture2D *_texture = nullptr; - ID3D11ShaderResourceView *_textureView = nullptr; - std::recursive_mutex _renderMutex; - ultralight::Cursor _cursor = ultralight::kCursor_Pointer; - glm::vec2 _cursorPos {}; - - private: - void OnAddConsoleMessage(ultralight::View *caller, const ultralight::ConsoleMessage &message) override; - void OnDOMReady(ultralight::View *, uint64_t, bool, const ultralight::String &) override; - void OnWindowObjectReady(ultralight::View *, uint64_t, bool, const ultralight::String &) override; - void OnChangeCursor(ultralight::View *caller, ultralight::Cursor cursor) override; - - void InitD3D(); - void ResetTextures(); - void LoadCursorData(ultralight::Cursor cursor); - - public: - View(ultralight::RefPtr); - ~View(); - - bool Init(std::string &, int, int); - - void Update(); - void Render(); - void RenderCursor(); - - void ProcessMouseEvent(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); - void ProcessKeyboardEvent(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); - - void Focus(bool enable) { - if (!_internalView) { - return; - } - - if (enable) { - _internalView->Focus(); - ShowCursor(true); - } - else { - _internalView->Unfocus(); - ShowCursor(false); - } - } - - void Display(bool enable) { - _shouldDisplay = enable; - } - - inline void AddEventListener(std::string eventName, const EventCallbackProc &proc) { - if (!_sdk) { - return; - } - - _sdk->AddEventListener(eventName, proc); - } - - inline void RemoveEventListener(std::string eventName) { - if (!_sdk) { - return; - } - - _sdk->RemoveEventListener(eventName); - } - }; -} // namespace MafiaMP::Core::UI::Web