// DragonX Wallet - ImGui Edition // Copyright 2024-2026 The Hush Developers // Released under the GPLv3 #include "dx11_context.h" #ifdef _WIN32 #include #include #include "../util/logger.h" // Not defined in older MinGW SDK headers #ifndef WS_EX_NOREDIRECTIONBITMAP #define WS_EX_NOREDIRECTIONBITMAP 0x00200000L #endif namespace dragonx { namespace platform { // Helper to get HWND from SDL window static HWND getHWND(SDL_Window* window) { if (!window) return nullptr; SDL_PropertiesID props = SDL_GetWindowProperties(window); if (props == 0) return nullptr; return (HWND)SDL_GetPointerProperty(props, SDL_PROP_WINDOW_WIN32_HWND_POINTER, nullptr); } bool DX11Context::init(SDL_Window* window) { HWND hwnd = getHWND(window); if (!hwnd) { DEBUG_LOGF("DX11: Failed to get HWND from SDL window\n"); return false; } hwnd_ = hwnd; // Ensure WS_EX_NOREDIRECTIONBITMAP is set. The main code creates the // HWND with this style, but if someone passes a different window we try // to set it here as a fallback. We also call SetWindowPos with // SWP_FRAMECHANGED so the DWM re-evaluates the extended style. LONG_PTR exStyle = GetWindowLongPtrW(hwnd, GWL_EXSTYLE); if (!(exStyle & WS_EX_NOREDIRECTIONBITMAP)) { SetWindowLongPtrW(hwnd, GWL_EXSTYLE, exStyle | WS_EX_NOREDIRECTIONBITMAP); SetWindowPos(hwnd, nullptr, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); LONG_PTR verify = GetWindowLongPtrW(hwnd, GWL_EXSTYLE); if (verify & WS_EX_NOREDIRECTIONBITMAP) DEBUG_LOGF("DX11: WS_EX_NOREDIRECTIONBITMAP set via fallback path\n"); else DEBUG_LOGF("DX11: WARNING - WS_EX_NOREDIRECTIONBITMAP could NOT be set!\n"); } else { DEBUG_LOGF("DX11: WS_EX_NOREDIRECTIONBITMAP already present (good)\n"); } fflush(stdout); // Create D3D11 device and context D3D_FEATURE_LEVEL featureLevel; const D3D_FEATURE_LEVEL featureLevelArray[] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_0, }; // Need BGRA support for DirectComposition UINT createDeviceFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; HRESULT hr = D3D11CreateDevice( nullptr, // Default adapter D3D_DRIVER_TYPE_HARDWARE, // Hardware rendering nullptr, // No software module createDeviceFlags, featureLevelArray, 2, D3D11_SDK_VERSION, &device_, &featureLevel, &deviceContext_ ); if (FAILED(hr)) { DEBUG_LOGF("DX11: D3D11CreateDevice failed (HRESULT 0x%08lx)\n", hr); return false; } DEBUG_LOGF("DX11: Device created (feature level %d.%d)\n", (featureLevel >> 12) & 0xF, (featureLevel >> 8) & 0xF); // Get DXGI device for factory access and DirectComposition IDXGIDevice* dxgiDevice = nullptr; hr = device_->QueryInterface(IID_PPV_ARGS(&dxgiDevice)); if (FAILED(hr)) { DEBUG_LOGF("DX11: QueryInterface IDXGIDevice failed\n"); shutdown(); return false; } IDXGIAdapter* dxgiAdapter = nullptr; hr = dxgiDevice->GetAdapter(&dxgiAdapter); if (FAILED(hr)) { dxgiDevice->Release(); DEBUG_LOGF("DX11: GetAdapter failed\n"); shutdown(); return false; } IDXGIFactory2* dxgiFactory = nullptr; hr = dxgiAdapter->GetParent(IID_PPV_ARGS(&dxgiFactory)); dxgiAdapter->Release(); if (FAILED(hr)) { dxgiDevice->Release(); DEBUG_LOGF("DX11: GetParent IDXGIFactory2 failed\n"); shutdown(); return false; } // --------------------------------------------------------------- // Strategy: CreateSwapChainForComposition + DirectComposition // // DXGI_ALPHA_MODE_PREMULTIPLIED only works with composition swap chains. // CreateSwapChainForHwnd does NOT support premultiplied alpha. // We must use DirectComposition to bind the composition swap chain // to the window. This is how Windows Terminal achieves transparency. // --------------------------------------------------------------- DXGI_SWAP_CHAIN_DESC1 sd = {}; sd.Width = 0; // Auto-detect from HWND sd.Height = 0; sd.Format = DXGI_FORMAT_B8G8R8A8_UNORM; sd.Stereo = FALSE; sd.SampleDesc.Count = 1; sd.SampleDesc.Quality = 0; sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; sd.BufferCount = 2; sd.Scaling = DXGI_SCALING_STRETCH; sd.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; sd.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED; sd.Flags = 0; // CreateSwapChainForComposition needs explicit width/height RECT clientRect; GetClientRect(hwnd, &clientRect); sd.Width = clientRect.right - clientRect.left; sd.Height = clientRect.bottom - clientRect.top; if (sd.Width == 0) sd.Width = 1400; if (sd.Height == 0) sd.Height = 900; hr = dxgiFactory->CreateSwapChainForComposition( device_, &sd, nullptr, &swapChain_ ); if (SUCCEEDED(hr)) { DEBUG_LOGF("DX11: Composition swap chain created (%ux%u, PREMULTIPLIED alpha)\n", sd.Width, sd.Height); // Create DirectComposition device and bind swap chain to HWND hr = DCompositionCreateDevice(dxgiDevice, IID_PPV_ARGS(&dcompDevice_)); if (SUCCEEDED(hr)) { hr = dcompDevice_->CreateTargetForHwnd(hwnd, TRUE, &dcompTarget_); } if (SUCCEEDED(hr)) { hr = dcompDevice_->CreateVisual(&dcompVisual_); } if (SUCCEEDED(hr)) { hr = dcompVisual_->SetContent(swapChain_); } if (SUCCEEDED(hr)) { hr = dcompTarget_->SetRoot(dcompVisual_); } if (SUCCEEDED(hr)) { hr = dcompDevice_->Commit(); } if (SUCCEEDED(hr)) { hasAlpha_ = true; DEBUG_LOGF("DX11: DirectComposition bound to HWND (alpha compositing active)\n"); fflush(stdout); } else { DEBUG_LOGF("DX11: DirectComposition setup failed (0x%08lx), falling back\n", hr); fflush(stderr); // Clean up the composition objects and swap chain if (dcompVisual_) { dcompVisual_->Release(); dcompVisual_ = nullptr; } if (dcompTarget_) { dcompTarget_->Release(); dcompTarget_ = nullptr; } if (dcompDevice_) { dcompDevice_->Release(); dcompDevice_ = nullptr; } swapChain_->Release(); swapChain_ = nullptr; } } else { DEBUG_LOGF("DX11: CreateSwapChainForComposition failed (0x%08lx)\n", hr); } // Fallback: standard HWND swap chain (no alpha transparency) if (!swapChain_) { sd.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED; sd.Width = 0; // Auto-detect sd.Height = 0; hr = dxgiFactory->CreateSwapChainForHwnd( device_, hwnd, &sd, nullptr, nullptr, &swapChain_ ); if (SUCCEEDED(hr)) { DEBUG_LOGF("DX11: HWND swap chain created (opaque, no alpha)\n"); } } dxgiFactory->Release(); dxgiDevice->Release(); if (FAILED(hr) || !swapChain_) { DEBUG_LOGF("DX11: All swap chain creation paths failed\n"); shutdown(); return false; } createRenderTarget(); return true; } void DX11Context::shutdown() { cleanupRenderTarget(); // Release DirectComposition objects if (dcompVisual_) { dcompVisual_->Release(); dcompVisual_ = nullptr; } if (dcompTarget_) { dcompTarget_->Release(); dcompTarget_ = nullptr; } if (dcompDevice_) { dcompDevice_->Release(); dcompDevice_ = nullptr; } if (swapChain_) { swapChain_->Release(); swapChain_ = nullptr; } if (deviceContext_) { deviceContext_->Release(); deviceContext_ = nullptr; } if (device_) { device_->Release(); device_ = nullptr; } hasAlpha_ = false; } void DX11Context::resize(int width, int height) { (void)width; (void)height; if (!swapChain_ || !deviceContext_) return; cleanupRenderTarget(); // Unbind the render target from the pipeline — ResizeBuffers requires // ALL outstanding references to back-buffer resources to be released. // Without this, ResizeBuffers fails with DXGI_ERROR_INVALID_CALL when // growing the window (shrinking appears fine because the old larger // buffer simply gets cropped by DWM). deviceContext_->OMSetRenderTargets(0, nullptr, nullptr); deviceContext_->Flush(); // For composition swap chains (CreateSwapChainForComposition), // passing 0,0 means "keep current size" — NOT auto-detect from HWND. // We must pass the actual pixel dimensions from GetClientRect. UINT w = 0, h = 0; if (hwnd_) { RECT rc; GetClientRect(hwnd_, &rc); w = static_cast(rc.right - rc.left); h = static_cast(rc.bottom - rc.top); } if (w == 0 || h == 0) return; HRESULT hr = swapChain_->ResizeBuffers(0, w, h, DXGI_FORMAT_UNKNOWN, 0); if (FAILED(hr)) { DEBUG_LOGF("DX11: ResizeBuffers(%u x %u) failed (0x%08lx)\n", w, h, hr); } createRenderTarget(); // Commit DirectComposition so it picks up the new buffer size if (dcompDevice_) { dcompDevice_->Commit(); } } void DX11Context::ensureSize() { if (!swapChain_ || !hwnd_) return; // Query current swap chain buffer dimensions DXGI_SWAP_CHAIN_DESC1 desc; if (FAILED(swapChain_->GetDesc1(&desc))) return; // Query actual HWND client area RECT rc; GetClientRect(hwnd_, &rc); UINT clientW = static_cast(rc.right - rc.left); UINT clientH = static_cast(rc.bottom - rc.top); // Resize only when there's a mismatch if (clientW > 0 && clientH > 0 && (desc.Width != clientW || desc.Height != clientH)) { resize(static_cast(clientW), static_cast(clientH)); } } void DX11Context::clear(float r, float g, float b, float a) { if (!deviceContext_ || !renderTargetView_) return; const float clearColor[4] = { r, g, b, a }; deviceContext_->ClearRenderTargetView(renderTargetView_, clearColor); } void DX11Context::present(int vsync) { if (!swapChain_) return; swapChain_->Present(vsync ? 1 : 0, 0); } void DX11Context::createRenderTarget() { if (!swapChain_ || !device_) return; ID3D11Texture2D* backBuffer = nullptr; swapChain_->GetBuffer(0, IID_PPV_ARGS(&backBuffer)); if (backBuffer) { device_->CreateRenderTargetView(backBuffer, nullptr, &renderTargetView_); backBuffer->Release(); } } void DX11Context::cleanupRenderTarget() { if (renderTargetView_) { renderTargetView_->Release(); renderTargetView_ = nullptr; } } } // namespace platform } // namespace dragonx #endif // _WIN32