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)
303 lines
8.8 KiB
C++
303 lines
8.8 KiB
C++
// DragonX Wallet - ImGui Edition
|
|
// Copyright 2024-2026 The Hush Developers
|
|
// Released under the GPLv3
|
|
|
|
#include "framebuffer.h"
|
|
|
|
#ifdef DRAGONX_HAS_GLAD
|
|
#include <glad/gl.h>
|
|
#include <cstdio>
|
|
#include "../../util/logger.h"
|
|
|
|
// GL 3.0 constants — may be missing from some GLAD configurations
|
|
#ifndef GL_RGBA16F
|
|
#define GL_RGBA16F 0x881A
|
|
#endif
|
|
#ifndef GL_HALF_FLOAT
|
|
#define GL_HALF_FLOAT 0x140B
|
|
#endif
|
|
|
|
namespace dragonx {
|
|
namespace ui {
|
|
namespace effects {
|
|
|
|
// ============================================================================
|
|
// Framebuffer Implementation (GLEW Available)
|
|
// ============================================================================
|
|
|
|
Framebuffer::Framebuffer() = default;
|
|
|
|
Framebuffer::~Framebuffer()
|
|
{
|
|
destroy();
|
|
}
|
|
|
|
Framebuffer::Framebuffer(Framebuffer&& other) noexcept
|
|
: fbo_(other.fbo_)
|
|
, colorTexture_(other.colorTexture_)
|
|
, depthRbo_(other.depthRbo_)
|
|
, width_(other.width_)
|
|
, height_(other.height_)
|
|
, isComplete_(other.isComplete_)
|
|
{
|
|
other.fbo_ = 0;
|
|
other.colorTexture_ = 0;
|
|
other.depthRbo_ = 0;
|
|
other.width_ = 0;
|
|
other.height_ = 0;
|
|
other.isComplete_ = false;
|
|
}
|
|
|
|
Framebuffer& Framebuffer::operator=(Framebuffer&& other) noexcept
|
|
{
|
|
if (this != &other) {
|
|
destroy();
|
|
|
|
fbo_ = other.fbo_;
|
|
colorTexture_ = other.colorTexture_;
|
|
depthRbo_ = other.depthRbo_;
|
|
width_ = other.width_;
|
|
height_ = other.height_;
|
|
isComplete_ = other.isComplete_;
|
|
|
|
other.fbo_ = 0;
|
|
other.colorTexture_ = 0;
|
|
other.depthRbo_ = 0;
|
|
other.width_ = 0;
|
|
other.height_ = 0;
|
|
other.isComplete_ = false;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
bool Framebuffer::init(int width, int height, bool useFloat16)
|
|
{
|
|
if (width <= 0 || height <= 0) {
|
|
DEBUG_LOGF("Framebuffer::init - Invalid dimensions: %dx%d\n", width, height);
|
|
return false;
|
|
}
|
|
|
|
// Clean up existing resources
|
|
cleanup();
|
|
|
|
width_ = width;
|
|
height_ = height;
|
|
useFloat16_ = useFloat16;
|
|
|
|
// Generate framebuffer
|
|
glGenFramebuffers(1, &fbo_);
|
|
glBindFramebuffer(GL_FRAMEBUFFER, fbo_);
|
|
|
|
// Create color texture — use RGBA16F for blur intermediates to avoid banding
|
|
glGenTextures(1, &colorTexture_);
|
|
glBindTexture(GL_TEXTURE_2D, colorTexture_);
|
|
if (useFloat16) {
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, width, height, 0, GL_RGBA, GL_HALF_FLOAT, nullptr);
|
|
} else {
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
|
}
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
|
|
// Attach color texture to framebuffer
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTexture_, 0);
|
|
|
|
// Create depth renderbuffer (optional, but good for completeness)
|
|
glGenRenderbuffers(1, &depthRbo_);
|
|
glBindRenderbuffer(GL_RENDERBUFFER, depthRbo_);
|
|
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
|
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthRbo_);
|
|
|
|
// Check completeness
|
|
isComplete_ = checkComplete();
|
|
|
|
// Unbind
|
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
|
|
|
if (!isComplete_) {
|
|
DEBUG_LOGF("Framebuffer::init - Framebuffer is not complete!\n");
|
|
cleanup();
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Framebuffer::resize(int width, int height)
|
|
{
|
|
if (width == width_ && height == height_) {
|
|
return true; // No change needed
|
|
}
|
|
return init(width, height, useFloat16_);
|
|
}
|
|
|
|
void Framebuffer::destroy()
|
|
{
|
|
cleanup();
|
|
}
|
|
|
|
void Framebuffer::cleanup()
|
|
{
|
|
if (colorTexture_) {
|
|
glDeleteTextures(1, &colorTexture_);
|
|
colorTexture_ = 0;
|
|
}
|
|
if (depthRbo_) {
|
|
glDeleteRenderbuffers(1, &depthRbo_);
|
|
depthRbo_ = 0;
|
|
}
|
|
if (fbo_) {
|
|
glDeleteFramebuffers(1, &fbo_);
|
|
fbo_ = 0;
|
|
}
|
|
width_ = 0;
|
|
height_ = 0;
|
|
isComplete_ = false;
|
|
}
|
|
|
|
bool Framebuffer::checkComplete()
|
|
{
|
|
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
|
if (status != GL_FRAMEBUFFER_COMPLETE) {
|
|
const char* errorStr = "Unknown";
|
|
switch (status) {
|
|
case GL_FRAMEBUFFER_UNDEFINED: errorStr = "UNDEFINED"; break;
|
|
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: errorStr = "INCOMPLETE_ATTACHMENT"; break;
|
|
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: errorStr = "INCOMPLETE_MISSING_ATTACHMENT"; break;
|
|
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: errorStr = "INCOMPLETE_DRAW_BUFFER"; break;
|
|
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: errorStr = "INCOMPLETE_READ_BUFFER"; break;
|
|
case GL_FRAMEBUFFER_UNSUPPORTED: errorStr = "UNSUPPORTED"; break;
|
|
case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: errorStr = "INCOMPLETE_MULTISAMPLE"; break;
|
|
case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS: errorStr = "INCOMPLETE_LAYER_TARGETS"; break;
|
|
}
|
|
DEBUG_LOGF("Framebuffer error: %s (0x%X)\n", errorStr, status);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void Framebuffer::bind()
|
|
{
|
|
glBindFramebuffer(GL_FRAMEBUFFER, fbo_);
|
|
glViewport(0, 0, width_, height_);
|
|
}
|
|
|
|
void Framebuffer::unbind()
|
|
{
|
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
}
|
|
|
|
void Framebuffer::clear(float r, float g, float b, float a)
|
|
{
|
|
glClearColor(r, g, b, a);
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
}
|
|
|
|
void Framebuffer::blitFrom(GLuint srcFbo, int srcX, int srcY, int srcWidth, int srcHeight,
|
|
int dstX, int dstY)
|
|
{
|
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, srcFbo);
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_);
|
|
|
|
glBlitFramebuffer(
|
|
srcX, srcY, srcX + srcWidth, srcY + srcHeight, // Source rect
|
|
dstX, dstY, dstX + srcWidth, dstY + srcHeight, // Dest rect
|
|
GL_COLOR_BUFFER_BIT,
|
|
GL_LINEAR
|
|
);
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
}
|
|
|
|
void Framebuffer::captureScreen(int viewportWidth, int viewportHeight)
|
|
{
|
|
// Blit from default framebuffer (0) to this framebuffer
|
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_);
|
|
|
|
glBlitFramebuffer(
|
|
0, 0, viewportWidth, viewportHeight, // Source (screen)
|
|
0, 0, width_, height_, // Dest (this FBO)
|
|
GL_COLOR_BUFFER_BIT,
|
|
GL_LINEAR
|
|
);
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
}
|
|
|
|
// ============================================================================
|
|
// FramebufferPingPong Implementation
|
|
// ============================================================================
|
|
|
|
bool FramebufferPingPong::init(int width, int height, bool useFloat16)
|
|
{
|
|
if (!buffers_[0].init(width, height, useFloat16)) return false;
|
|
if (!buffers_[1].init(width, height, useFloat16)) return false;
|
|
currentSource_ = 0;
|
|
return true;
|
|
}
|
|
|
|
bool FramebufferPingPong::resize(int width, int height)
|
|
{
|
|
if (!buffers_[0].resize(width, height)) return false;
|
|
if (!buffers_[1].resize(width, height)) return false;
|
|
return true;
|
|
}
|
|
|
|
void FramebufferPingPong::destroy()
|
|
{
|
|
buffers_[0].destroy();
|
|
buffers_[1].destroy();
|
|
currentSource_ = 0;
|
|
}
|
|
|
|
void FramebufferPingPong::swap()
|
|
{
|
|
currentSource_ = 1 - currentSource_;
|
|
}
|
|
|
|
} // namespace effects
|
|
} // namespace ui
|
|
} // namespace dragonx
|
|
|
|
#else // !DRAGONX_HAS_GLAD
|
|
|
|
// ============================================================================
|
|
// Stub Implementation (No GLAD - Acrylic effects disabled)
|
|
// ============================================================================
|
|
|
|
namespace dragonx {
|
|
namespace ui {
|
|
namespace effects {
|
|
|
|
Framebuffer::Framebuffer() = default;
|
|
Framebuffer::~Framebuffer() = default;
|
|
Framebuffer::Framebuffer(Framebuffer&&) noexcept = default;
|
|
Framebuffer& Framebuffer::operator=(Framebuffer&&) noexcept = default;
|
|
|
|
bool Framebuffer::init(int, int, bool) { return false; }
|
|
bool Framebuffer::resize(int, int) { return false; }
|
|
void Framebuffer::destroy() {}
|
|
void Framebuffer::cleanup() {}
|
|
bool Framebuffer::checkComplete() { return false; }
|
|
void Framebuffer::bind() {}
|
|
void Framebuffer::unbind() {}
|
|
void Framebuffer::clear(float, float, float, float) {}
|
|
void Framebuffer::blitFrom(unsigned int, int, int, int, int, int, int) {}
|
|
void Framebuffer::captureScreen(int, int) {}
|
|
|
|
bool FramebufferPingPong::init(int, int, bool) { return false; }
|
|
bool FramebufferPingPong::resize(int, int) { return false; }
|
|
void FramebufferPingPong::destroy() {}
|
|
void FramebufferPingPong::swap() {}
|
|
|
|
} // namespace effects
|
|
} // namespace ui
|
|
} // namespace dragonx
|
|
|
|
#endif // DRAGONX_HAS_GLAD
|