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
|
||||
108
src/platform/dx11_context.h
Normal file
108
src/platform/dx11_context.h
Normal file
@@ -0,0 +1,108 @@
|
||||
// DragonX Wallet - ImGui Edition
|
||||
// Copyright 2024-2026 The Hush Developers
|
||||
// Released under the GPLv3
|
||||
|
||||
// DirectX 11 Rendering Context for Windows
|
||||
// Uses DXGI swap chain with premultiplied alpha + DirectComposition
|
||||
// for true per-pixel transparency with DWM Mica/Acrylic backdrops.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include <d3d11.h>
|
||||
#include <dxgi1_2.h>
|
||||
#include <cstdio>
|
||||
|
||||
// Forward declarations - DirectComposition interfaces
|
||||
struct IDCompositionDevice;
|
||||
struct IDCompositionTarget;
|
||||
struct IDCompositionVisual;
|
||||
|
||||
struct SDL_Window;
|
||||
|
||||
namespace dragonx {
|
||||
namespace platform {
|
||||
|
||||
class DX11Context {
|
||||
public:
|
||||
DX11Context() = default;
|
||||
~DX11Context() { shutdown(); }
|
||||
|
||||
// Non-copyable
|
||||
DX11Context(const DX11Context&) = delete;
|
||||
DX11Context& operator=(const DX11Context&) = delete;
|
||||
|
||||
/**
|
||||
* @brief Initialize D3D11 device and DXGI swap chain with alpha support
|
||||
* @param window SDL window to create the swap chain for
|
||||
* @return true on success
|
||||
*/
|
||||
bool init(SDL_Window* window);
|
||||
|
||||
/**
|
||||
* @brief Shut down and release all D3D11/DXGI resources
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* @brief Resize the swap chain buffers (call on window resize)
|
||||
* @param width New width (0 = auto-detect from swap chain)
|
||||
* @param height New height (0 = auto-detect from swap chain)
|
||||
*/
|
||||
void resize(int width, int height);
|
||||
|
||||
/**
|
||||
* @brief Ensure swap chain matches the current HWND client size.
|
||||
* Call once per frame to guarantee the buffers stay in sync.
|
||||
* No-op if the sizes already match.
|
||||
*/
|
||||
void ensureSize();
|
||||
|
||||
/**
|
||||
* @brief Clear the render target with a color
|
||||
* @param r Red (0-1)
|
||||
* @param g Green (0-1)
|
||||
* @param b Blue (0-1)
|
||||
* @param a Alpha (0-1), 0 = fully transparent for DWM
|
||||
*/
|
||||
void clear(float r, float g, float b, float a);
|
||||
|
||||
/**
|
||||
* @brief Present the frame (swap buffers)
|
||||
* @param vsync 1 = vsync on, 0 = vsync off
|
||||
*/
|
||||
void present(int vsync = 1);
|
||||
|
||||
/** @brief Whether premultiplied alpha compositing is active */
|
||||
bool hasAlphaCompositing() const { return hasAlpha_; }
|
||||
|
||||
// Accessors
|
||||
ID3D11Device* device() const { return device_; }
|
||||
ID3D11DeviceContext* deviceContext() const { return deviceContext_; }
|
||||
ID3D11RenderTargetView* renderTargetView() const { return renderTargetView_; }
|
||||
IDXGISwapChain1* swapChain() const { return swapChain_; }
|
||||
|
||||
private:
|
||||
void createRenderTarget();
|
||||
void cleanupRenderTarget();
|
||||
|
||||
// D3D11 core
|
||||
ID3D11Device* device_ = nullptr;
|
||||
ID3D11DeviceContext* deviceContext_ = nullptr;
|
||||
IDXGISwapChain1* swapChain_ = nullptr;
|
||||
ID3D11RenderTargetView* renderTargetView_ = nullptr;
|
||||
|
||||
// DirectComposition (for premultiplied alpha swap chain presentation)
|
||||
IDCompositionDevice* dcompDevice_ = nullptr;
|
||||
IDCompositionTarget* dcompTarget_ = nullptr;
|
||||
IDCompositionVisual* dcompVisual_ = nullptr;
|
||||
|
||||
bool hasAlpha_ = false;
|
||||
HWND hwnd_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace platform
|
||||
} // namespace dragonx
|
||||
|
||||
#endif // _WIN32
|
||||
182
src/platform/windows_backdrop.cpp
Normal file
182
src/platform/windows_backdrop.cpp
Normal file
@@ -0,0 +1,182 @@
|
||||
// DragonX Wallet - ImGui Edition
|
||||
// Copyright 2024-2026 The Hush Developers
|
||||
// Released under the GPLv3
|
||||
|
||||
#include "windows_backdrop.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include <windows.h>
|
||||
#include <dwmapi.h>
|
||||
#include <SDL3/SDL.h>
|
||||
#include <cstdio>
|
||||
#include "../util/logger.h"
|
||||
|
||||
// Link with dwmapi.lib
|
||||
#pragma comment(lib, "dwmapi.lib")
|
||||
|
||||
// DWM attribute values not in older SDK headers
|
||||
#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE
|
||||
#define DWMWA_USE_IMMERSIVE_DARK_MODE 20
|
||||
#endif
|
||||
|
||||
#ifndef DWMWA_SYSTEMBACKDROP_TYPE
|
||||
#define DWMWA_SYSTEMBACKDROP_TYPE 38
|
||||
#endif
|
||||
|
||||
#ifndef DWMWA_MICA_EFFECT
|
||||
#define DWMWA_MICA_EFFECT 1029
|
||||
#endif
|
||||
|
||||
// System backdrop types (Windows 11 22H2+)
|
||||
typedef enum {
|
||||
DWMSBT_AUTO = 0,
|
||||
DWMSBT_NONE = 1,
|
||||
DWMSBT_MAINWINDOW = 2, // Mica
|
||||
DWMSBT_TRANSIENTWINDOW = 3, // Acrylic
|
||||
DWMSBT_TABBEDWINDOW = 4 // Mica Alt
|
||||
} DWM_SYSTEMBACKDROP_TYPE;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
WindowsVersionInfo getWindowsVersion() {
|
||||
WindowsVersionInfo info = {};
|
||||
|
||||
// Use RtlGetVersion to get the real version (GetVersionEx is deprecated/lies)
|
||||
typedef NTSTATUS (WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW);
|
||||
|
||||
HMODULE ntdll = GetModuleHandleW(L"ntdll.dll");
|
||||
if (ntdll) {
|
||||
RtlGetVersionPtr rtlGetVersion = (RtlGetVersionPtr)GetProcAddress(ntdll, "RtlGetVersion");
|
||||
if (rtlGetVersion) {
|
||||
RTL_OSVERSIONINFOW osvi = {};
|
||||
osvi.dwOSVersionInfoSize = sizeof(osvi);
|
||||
if (rtlGetVersion(&osvi) == 0) {
|
||||
info.major = osvi.dwMajorVersion;
|
||||
info.minor = osvi.dwMinorVersion;
|
||||
info.build = osvi.dwBuildNumber;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
info.isWindows11 = (info.major >= 10 && info.build >= 22000);
|
||||
info.isWindows10 = (info.major >= 10 && info.build >= 10240);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
bool isWindowsBackdropAvailable() {
|
||||
WindowsVersionInfo ver = getWindowsVersion();
|
||||
// Backdrop effects require at least Windows 10
|
||||
return ver.isWindows10;
|
||||
}
|
||||
|
||||
bool enableWindowsBackdrop(SDL_Window* window, WindowsBackdrop type) {
|
||||
HWND hwnd = getHWND(window);
|
||||
if (!hwnd) {
|
||||
DEBUG_LOGF("WindowsBackdrop: Failed to get HWND\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
WindowsVersionInfo ver = getWindowsVersion();
|
||||
DEBUG_LOGF("WindowsBackdrop: Windows %d.%d.%d detected\n", ver.major, ver.minor, ver.build);
|
||||
|
||||
if (!ver.isWindows10) {
|
||||
DEBUG_LOGF("WindowsBackdrop: Windows 10+ required\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enable dark mode for title bar (looks better with acrylic)
|
||||
BOOL useDarkMode = TRUE;
|
||||
DwmSetWindowAttribute(hwnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &useDarkMode, sizeof(useDarkMode));
|
||||
|
||||
// Auto-detect best backdrop type
|
||||
if (type == WindowsBackdrop::Auto) {
|
||||
if (ver.isWindows11 && ver.build >= 22621) {
|
||||
// Windows 11 22H2+ - use Mica Alt for dark themed apps
|
||||
type = WindowsBackdrop::MicaAlt;
|
||||
} else if (ver.isWindows11) {
|
||||
// Windows 11 21H2 - use Mica
|
||||
type = WindowsBackdrop::Mica;
|
||||
} else {
|
||||
// Windows 10 - use Acrylic
|
||||
type = WindowsBackdrop::Acrylic;
|
||||
}
|
||||
}
|
||||
|
||||
// Extend DWM frame into entire client area - required for all backdrop types
|
||||
// This lets the DWM composition show through where we render with alpha = 0
|
||||
MARGINS margins = {-1, -1, -1, -1};
|
||||
DwmExtendFrameIntoClientArea(hwnd, &margins);
|
||||
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
if (ver.build >= 22621) {
|
||||
// Windows 11 22H2+ - use DWMWA_SYSTEMBACKDROP_TYPE
|
||||
DWM_SYSTEMBACKDROP_TYPE backdrop;
|
||||
switch (type) {
|
||||
case WindowsBackdrop::Mica:
|
||||
backdrop = DWMSBT_MAINWINDOW;
|
||||
DEBUG_LOGF("WindowsBackdrop: Enabling Mica\n");
|
||||
break;
|
||||
case WindowsBackdrop::MicaAlt:
|
||||
backdrop = DWMSBT_TABBEDWINDOW;
|
||||
DEBUG_LOGF("WindowsBackdrop: Enabling Mica Alt\n");
|
||||
break;
|
||||
case WindowsBackdrop::Acrylic:
|
||||
backdrop = DWMSBT_TRANSIENTWINDOW;
|
||||
DEBUG_LOGF("WindowsBackdrop: Enabling Acrylic\n");
|
||||
break;
|
||||
default:
|
||||
backdrop = DWMSBT_NONE;
|
||||
break;
|
||||
}
|
||||
hr = DwmSetWindowAttribute(hwnd, DWMWA_SYSTEMBACKDROP_TYPE, &backdrop, sizeof(backdrop));
|
||||
}
|
||||
else if (ver.isWindows11) {
|
||||
// Windows 11 21H2 - use legacy DWMWA_MICA_EFFECT
|
||||
BOOL enableMica = (type == WindowsBackdrop::Mica || type == WindowsBackdrop::MicaAlt);
|
||||
hr = DwmSetWindowAttribute(hwnd, DWMWA_MICA_EFFECT, &enableMica, sizeof(enableMica));
|
||||
DEBUG_LOGF("WindowsBackdrop: Enabling Mica (legacy API)\n");
|
||||
}
|
||||
else {
|
||||
// Windows 10 - frame already extended above, basic DWM transparency
|
||||
// Note: Full acrylic on Win10 requires undocumented APIs
|
||||
DEBUG_LOGF("WindowsBackdrop: Using extended frame (Win10)\n");
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
DEBUG_LOGF("WindowsBackdrop: Successfully enabled\n");
|
||||
return true;
|
||||
} else {
|
||||
DEBUG_LOGF("WindowsBackdrop: DWM call failed with HRESULT 0x%08lx\n", hr);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void disableWindowsBackdrop(SDL_Window* window) {
|
||||
HWND hwnd = getHWND(window);
|
||||
if (!hwnd) return;
|
||||
|
||||
DWM_SYSTEMBACKDROP_TYPE backdrop = DWMSBT_NONE;
|
||||
DwmSetWindowAttribute(hwnd, DWMWA_SYSTEMBACKDROP_TYPE, &backdrop, sizeof(backdrop));
|
||||
|
||||
MARGINS margins = {0, 0, 0, 0};
|
||||
DwmExtendFrameIntoClientArea(hwnd, &margins);
|
||||
}
|
||||
|
||||
} // namespace platform
|
||||
} // namespace dragonx
|
||||
|
||||
#endif // _WIN32
|
||||
68
src/platform/windows_backdrop.h
Normal file
68
src/platform/windows_backdrop.h
Normal file
@@ -0,0 +1,68 @@
|
||||
// DragonX Wallet - ImGui Edition
|
||||
// Copyright 2024-2026 The Hush Developers
|
||||
// Released under the GPLv3
|
||||
|
||||
#pragma once
|
||||
|
||||
// Windows-specific backdrop blur (Mica/Acrylic) support
|
||||
// Uses DWM (Desktop Window Manager) APIs available on Windows 10/11
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
namespace dragonx {
|
||||
namespace platform {
|
||||
|
||||
// Backdrop types available on Windows
|
||||
enum class WindowsBackdrop {
|
||||
None, // No system backdrop
|
||||
Mica, // Windows 11 Mica material
|
||||
MicaAlt, // Windows 11 Mica Alt (darker)
|
||||
Acrylic, // Windows 10/11 Acrylic (transient)
|
||||
Auto // Auto-detect best option
|
||||
};
|
||||
|
||||
// Enable Windows system backdrop blur
|
||||
// Returns true if successfully enabled
|
||||
bool enableWindowsBackdrop(SDL_Window* window, WindowsBackdrop type = WindowsBackdrop::Auto);
|
||||
|
||||
// Disable Windows system backdrop
|
||||
void disableWindowsBackdrop(SDL_Window* window);
|
||||
|
||||
// Check if system backdrop is available
|
||||
bool isWindowsBackdropAvailable();
|
||||
|
||||
// Get Windows version info
|
||||
struct WindowsVersionInfo {
|
||||
int major;
|
||||
int minor;
|
||||
int build;
|
||||
bool isWindows11; // Build >= 22000
|
||||
bool isWindows10; // Build >= 10240
|
||||
};
|
||||
|
||||
WindowsVersionInfo getWindowsVersion();
|
||||
|
||||
} // namespace platform
|
||||
} // namespace dragonx
|
||||
|
||||
#else // !_WIN32
|
||||
|
||||
// Stub for non-Windows platforms
|
||||
namespace dragonx {
|
||||
namespace platform {
|
||||
|
||||
enum class WindowsBackdrop { None, Mica, MicaAlt, Acrylic, Auto };
|
||||
|
||||
inline bool enableWindowsBackdrop(SDL_Window*, WindowsBackdrop = WindowsBackdrop::Auto) { return false; }
|
||||
inline void disableWindowsBackdrop(SDL_Window*) {}
|
||||
inline bool isWindowsBackdropAvailable() { return false; }
|
||||
|
||||
struct WindowsVersionInfo { int major = 0; int minor = 0; int build = 0; bool isWindows11 = false; bool isWindows10 = false; };
|
||||
inline WindowsVersionInfo getWindowsVersion() { return {}; }
|
||||
|
||||
} // namespace platform
|
||||
} // namespace dragonx
|
||||
|
||||
#endif // _WIN32
|
||||
Reference in New Issue
Block a user