ObsidianDragon - DragonX ImGui Wallet
Full-node GUI wallet for DragonX cryptocurrency. Built with Dear ImGui, SDL3, and OpenGL3/DX11. Features: - Send/receive shielded and transparent transactions - Autoshield with merged transaction display - Built-in CPU mining (xmrig) - Peer management and network monitoring - Wallet encryption with PIN lock - QR code generation for receive addresses - Transaction history with pagination - Console for direct RPC commands - Cross-platform (Linux, Windows)
This commit is contained in:
351
src/platform/dx11_context.cpp
Normal file
351
src/platform/dx11_context.cpp
Normal file
@@ -0,0 +1,351 @@
|
||||
// DragonX Wallet - ImGui Edition
|
||||
// Copyright 2024-2026 The Hush Developers
|
||||
// Released under the GPLv3
|
||||
|
||||
#include "dx11_context.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <dcomp.h>
|
||||
#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,
|
||||
};
|
||||
|
||||
UINT createDeviceFlags = 0;
|
||||
#ifdef DRAGONX_DEBUG
|
||||
createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
|
||||
#endif
|
||||
|
||||
// Need BGRA support for DirectComposition
|
||||
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<UINT>(rc.right - rc.left);
|
||||
h = static_cast<UINT>(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<UINT>(rc.right - rc.left);
|
||||
UINT clientH = static_cast<UINT>(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<int>(clientW), static_cast<int>(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
|
||||
Reference in New Issue
Block a user