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:
2026-02-26 02:31:52 -06:00
commit 3aee55b49c
306 changed files with 177789 additions and 0 deletions

View 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