Files
ObsidianDragon/CMakeLists.txt
DanS 779c9682d4 refactor(console): phase 3 — thread-safe ConsoleModel ingest queue; fix data race
Introduce ConsoleModel (console_model.{h,cpp}): an ImGui-free, thread-safe line
store. Producers on any thread call ingest(), which only briefly locks a small
pending queue; the main thread calls drain() once per frame to move pending
lines into the visible deque and enforce the line cap, reporting how many were
added and evicted from the front.

This fixes the real data race. Previously a single lines_mutex_ guarded both the
line container AND the UI-only interaction state (selection, scroll, auto-scroll
counters), and it was held across the whole render pass — yet render() touched
auto_scroll_ / new_lines_since_scroll_ outside that lock, racing the background
RPC-trace and app-logger producers that call addLine() on worker threads.

Now:
- addLine() only ingests (lock-free w.r.t. the model); safe from any thread.
- render() drains once per frame, then does the cap/selection/scroll bookkeeping
  on the main thread (new drainModel()).
- The visible deque + all selection/scroll fields are main-thread-only, so every
  lines_mutex_ lock is gone — including the one wrapping the entire renderOutput.

Adds testConsoleModel() covering ingest/drain ordering, the line-cap eviction
counts, view-only clear (queued lines survive), and an 8-thread concurrent
ingest/drain stress with no lost lines. Full-node + lite build clean; ctest
green; source hygiene clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 18:35:25 -05:00

1111 lines
44 KiB
CMake

# DragonX Wallet - ImGui Edition
# Copyright 2024-2026 The Hush Developers
# Released under the GPLv3
cmake_minimum_required(VERSION 3.20)
# macOS: set deployment target and universal architectures BEFORE project()
# so they propagate to all targets, including FetchContent dependencies (SDL3, etc.)
if(APPLE)
set(CMAKE_OSX_DEPLOYMENT_TARGET "11.0" CACHE STRING "Minimum macOS version" FORCE)
# Build universal binary (Apple Silicon + Intel) unless the user explicitly set architectures
if(NOT DEFINED CMAKE_OSX_ARCHITECTURES OR CMAKE_OSX_ARCHITECTURES STREQUAL "")
set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64" CACHE STRING "macOS architectures" FORCE)
endif()
endif()
project(ObsidianDragon
VERSION 2.0.0
LANGUAGES C CXX
DESCRIPTION "DragonX Cryptocurrency Wallet"
)
# Pre-release suffix (e.g. "-rc1", "-beta2"). Leave empty for stable releases.
set(DRAGONX_VERSION_SUFFIX "")
# ObsidianDragonLite is versioned INDEPENDENTLY of the full-node app above. The active variant's
# version flows to the generated header, the Windows .rc/manifest, and build.sh's release names via
# DRAGONX_APP_VERSION* (resolved in the lite/full block below).
set(DRAGONX_LITE_VERSION "1.0.0")
set(DRAGONX_LITE_VERSION_SUFFIX "")
# C++17 standard
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# Build type
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE)
endif()
# Options
option(DRAGONX_USE_SYSTEM_SDL3 "Use system SDL3 instead of fetching" ON)
option(DRAGONX_ENABLE_EMBEDDED_DAEMON "Enable embedded dragonxd support" ON)
option(DRAGONX_BUILD_LITE "Build ObsidianDragonLite variant without full-node features" OFF)
option(DRAGONX_ENABLE_LITE_BACKEND "Enable real lite wallet backend integration" OFF)
option(DRAGONX_ENABLE_CHAT "Enable experimental HushChat protocol/UI integration" OFF)
set(DRAGONX_LITE_BACKEND_LIBRARY "" CACHE FILEPATH "Path to a prebuilt SDXL-compatible lite backend library")
set(DRAGONX_LITE_BACKEND_INCLUDE_DIR "" CACHE PATH "Optional include directory for SDXL-compatible lite backend headers")
set(DRAGONX_LITE_BACKEND_EXTRA_LIBS "" CACHE STRING "Additional libraries needed by the SDXL-compatible lite backend")
set(DRAGONX_LITE_BACKEND_LINK_MODE "imported" CACHE STRING "Lite backend link mode; Phase 1 supports imported only")
set_property(CACHE DRAGONX_LITE_BACKEND_LINK_MODE PROPERTY STRINGS imported)
set(DRAGONX_LITE_BACKEND_ABI "sdxl-c-v1" CACHE STRING "Expected lite backend C ABI version")
set(DRAGONX_LITE_BACKEND_SYMBOLS_FILE "" CACHE FILEPATH "Path to generated lite backend exported-symbol inventory")
set(DRAGONX_LITE_BACKEND_MANIFEST "" CACHE FILEPATH "Optional path to generated lite backend artifact manifest")
option(DRAGONX_LITE_BACKEND_REQUIRE_SIGNATURE "Require verified signature metadata in the lite backend artifact manifest" OFF)
set(DRAGONX_LITE_BACKEND_REQUIRED_SYMBOLS
litelib_wallet_exists
litelib_initialize_new
litelib_initialize_new_from_phrase
litelib_initialize_existing
litelib_execute
litelib_rust_free_string
litelib_check_server_online
litelib_shutdown
)
if(DRAGONX_BUILD_LITE)
set(DRAGONX_APP_NAME "ObsidianDragonLite")
set(DRAGONX_BINARY_NAME "ObsidianDragonLite")
# NOTE: do NOT FORCE-write DRAGONX_ENABLE_EMBEDDED_DAEMON=OFF into the cache here. A forced
# cache write persists into a later full-node reconfigure of the same build dir and silently
# disables the embedded daemon — the binary still embeds/extracts, but isUsingEmbeddedDaemon()
# returns false, so it "unpacks dragonxd but never starts" (the 1.3.0 regression). It is also
# redundant: makeWalletCapabilities() already forces the embedded-daemon capability off for any
# lite build via `fullNodeBuild && embeddedDaemonCompiled`, so lite never launches a daemon
# regardless of this flag. build.sh sets the flag explicitly per variant to defeat stale caches.
set(DRAGONX_APP_VERSION "${DRAGONX_LITE_VERSION}")
set(DRAGONX_APP_VERSION_SUFFIX "${DRAGONX_LITE_VERSION_SUFFIX}")
else()
set(DRAGONX_APP_NAME "ObsidianDragon")
set(DRAGONX_BINARY_NAME "ObsidianDragon")
set(DRAGONX_APP_VERSION "${PROJECT_VERSION}")
set(DRAGONX_APP_VERSION_SUFFIX "${DRAGONX_VERSION_SUFFIX}")
endif()
# Split the active version into numeric components for the generated header + Windows VERSIONINFO.
string(REPLACE "." ";" _dragonx_ver_parts "${DRAGONX_APP_VERSION}")
list(GET _dragonx_ver_parts 0 DRAGONX_APP_VERSION_MAJOR)
list(GET _dragonx_ver_parts 1 DRAGONX_APP_VERSION_MINOR)
list(GET _dragonx_ver_parts 2 DRAGONX_APP_VERSION_PATCH)
set(DRAGONX_LITE_BACKEND_READY OFF)
if(DRAGONX_ENABLE_LITE_BACKEND)
if(NOT DRAGONX_BUILD_LITE)
message(FATAL_ERROR "DRAGONX_ENABLE_LITE_BACKEND is only supported with DRAGONX_BUILD_LITE=ON")
endif()
if(NOT DRAGONX_LITE_BACKEND_LINK_MODE STREQUAL "imported")
message(FATAL_ERROR "DRAGONX_LITE_BACKEND_LINK_MODE currently supports only 'imported'; runtime dynamic loading is a later bridge-runtime phase")
endif()
if(NOT DRAGONX_LITE_BACKEND_ABI STREQUAL "sdxl-c-v1")
message(FATAL_ERROR "DRAGONX_LITE_BACKEND_ABI must be sdxl-c-v1")
endif()
if(NOT DRAGONX_LITE_BACKEND_LIBRARY)
message(FATAL_ERROR "DRAGONX_ENABLE_LITE_BACKEND requires DRAGONX_LITE_BACKEND_LIBRARY to point at an SDXL-compatible artifact")
endif()
if(NOT EXISTS "${DRAGONX_LITE_BACKEND_LIBRARY}")
message(FATAL_ERROR "DRAGONX_LITE_BACKEND_LIBRARY does not exist: ${DRAGONX_LITE_BACKEND_LIBRARY}")
endif()
if(NOT DRAGONX_LITE_BACKEND_SYMBOLS_FILE)
message(FATAL_ERROR "DRAGONX_ENABLE_LITE_BACKEND requires DRAGONX_LITE_BACKEND_SYMBOLS_FILE generated by scripts/build-lite-backend-artifact.sh")
endif()
if(NOT EXISTS "${DRAGONX_LITE_BACKEND_SYMBOLS_FILE}")
message(FATAL_ERROR "DRAGONX_LITE_BACKEND_SYMBOLS_FILE does not exist: ${DRAGONX_LITE_BACKEND_SYMBOLS_FILE}")
endif()
file(STRINGS "${DRAGONX_LITE_BACKEND_SYMBOLS_FILE}" DRAGONX_LITE_BACKEND_SYMBOL_LINES)
if(NOT DRAGONX_LITE_BACKEND_SYMBOL_LINES)
message(FATAL_ERROR "DRAGONX_LITE_BACKEND_SYMBOLS_FILE is empty: ${DRAGONX_LITE_BACKEND_SYMBOLS_FILE}")
endif()
foreach(DRAGONX_LITE_REQUIRED_SYMBOL IN LISTS DRAGONX_LITE_BACKEND_REQUIRED_SYMBOLS)
list(FIND DRAGONX_LITE_BACKEND_SYMBOL_LINES "${DRAGONX_LITE_REQUIRED_SYMBOL}" DRAGONX_LITE_SYMBOL_INDEX)
if(DRAGONX_LITE_SYMBOL_INDEX EQUAL -1)
message(FATAL_ERROR "DRAGONX_LITE_BACKEND_SYMBOLS_FILE is missing required symbol: ${DRAGONX_LITE_REQUIRED_SYMBOL}")
endif()
endforeach()
if(DRAGONX_LITE_BACKEND_MANIFEST AND NOT EXISTS "${DRAGONX_LITE_BACKEND_MANIFEST}")
message(FATAL_ERROR "DRAGONX_LITE_BACKEND_MANIFEST does not exist: ${DRAGONX_LITE_BACKEND_MANIFEST}")
endif()
if(DRAGONX_LITE_BACKEND_REQUIRE_SIGNATURE)
if(NOT DRAGONX_LITE_BACKEND_MANIFEST)
message(FATAL_ERROR "DRAGONX_LITE_BACKEND_REQUIRE_SIGNATURE requires DRAGONX_LITE_BACKEND_MANIFEST")
endif()
file(READ "${DRAGONX_LITE_BACKEND_MANIFEST}" DRAGONX_LITE_BACKEND_MANIFEST_JSON)
string(JSON DRAGONX_LITE_SIGNATURE_STATUS ERROR_VARIABLE DRAGONX_LITE_SIGNATURE_STATUS_ERROR GET "${DRAGONX_LITE_BACKEND_MANIFEST_JSON}" signature_verification verification_status)
if(DRAGONX_LITE_SIGNATURE_STATUS_ERROR)
message(FATAL_ERROR "DRAGONX_LITE_BACKEND_MANIFEST is missing signature verification status")
endif()
if(NOT DRAGONX_LITE_SIGNATURE_STATUS STREQUAL "verified")
message(FATAL_ERROR "DRAGONX_LITE_BACKEND_REQUIRE_SIGNATURE requires verified signature metadata")
endif()
string(JSON DRAGONX_LITE_SIGNATURE_VERIFIED_SHA ERROR_VARIABLE DRAGONX_LITE_SIGNATURE_VERIFIED_SHA_ERROR GET "${DRAGONX_LITE_BACKEND_MANIFEST_JSON}" signature_verification verified_artifact_sha256)
string(JSON DRAGONX_LITE_ARTIFACT_SHA ERROR_VARIABLE DRAGONX_LITE_ARTIFACT_SHA_ERROR GET "${DRAGONX_LITE_BACKEND_MANIFEST_JSON}" artifact sha256)
if(DRAGONX_LITE_SIGNATURE_VERIFIED_SHA_ERROR OR DRAGONX_LITE_ARTIFACT_SHA_ERROR)
message(FATAL_ERROR "DRAGONX_LITE_BACKEND_MANIFEST is missing artifact/signature SHA-256 metadata")
endif()
if(NOT DRAGONX_LITE_SIGNATURE_VERIFIED_SHA STREQUAL DRAGONX_LITE_ARTIFACT_SHA)
message(FATAL_ERROR "DRAGONX_LITE_BACKEND_MANIFEST signature metadata does not verify the artifact SHA-256")
endif()
string(JSON DRAGONX_LITE_SIGNATURE_PERFORMED ERROR_VARIABLE DRAGONX_LITE_SIGNATURE_PERFORMED_ERROR GET "${DRAGONX_LITE_BACKEND_MANIFEST_JSON}" signature_verification verification_performed)
if(DRAGONX_LITE_SIGNATURE_PERFORMED_ERROR OR NOT DRAGONX_LITE_SIGNATURE_PERFORMED)
message(FATAL_ERROR "DRAGONX_LITE_BACKEND_REQUIRE_SIGNATURE requires verification_performed=true")
endif()
endif()
add_library(dragonx_lite_backend UNKNOWN IMPORTED)
set_target_properties(dragonx_lite_backend PROPERTIES
IMPORTED_LOCATION "${DRAGONX_LITE_BACKEND_LIBRARY}"
)
if(DRAGONX_LITE_BACKEND_INCLUDE_DIR)
if(NOT IS_DIRECTORY "${DRAGONX_LITE_BACKEND_INCLUDE_DIR}")
message(FATAL_ERROR "DRAGONX_LITE_BACKEND_INCLUDE_DIR does not exist: ${DRAGONX_LITE_BACKEND_INCLUDE_DIR}")
endif()
set_target_properties(dragonx_lite_backend PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${DRAGONX_LITE_BACKEND_INCLUDE_DIR}"
)
endif()
set(DRAGONX_LITE_BACKEND_READY ON)
endif()
include(CTest)
# Output directories
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
# -----------------------------------------------------------------------------
# Dependencies
# -----------------------------------------------------------------------------
# OpenGL (Linux only - Windows uses DirectX 11, macOS uses frameworks)
if(NOT WIN32 AND NOT APPLE)
find_package(OpenGL REQUIRED)
endif()
# miniz - bundled zip library (MIT / public domain)
set(MINIZ_DIR ${CMAKE_SOURCE_DIR}/libs/miniz)
set(MINIZ_SOURCES
${MINIZ_DIR}/miniz.c
${MINIZ_DIR}/miniz_tdef.c
${MINIZ_DIR}/miniz_tinfl.c
${MINIZ_DIR}/miniz_zip.c
)
# GLAD - bundled OpenGL loader (Linux/macOS — Windows uses DX11)
set(GLAD_DIR ${CMAKE_SOURCE_DIR}/libs/glad)
if(NOT WIN32)
set(GLAD_SOURCES ${GLAD_DIR}/src/gl.c)
else()
set(GLAD_SOURCES "")
endif()
set(GLAD_INCLUDE ${GLAD_DIR}/include)
message(STATUS "Using bundled GLAD for OpenGL loading")
# SDL3 - try system first, then fetch
if(DRAGONX_USE_SYSTEM_SDL3)
find_package(SDL3 QUIET)
endif()
if(NOT SDL3_FOUND)
message(STATUS "SDL3 not found, will fetch from source...")
include(FetchContent)
FetchContent_Declare(
SDL3
GIT_REPOSITORY https://github.com/libsdl-org/SDL.git
GIT_TAG 267e681a
GIT_SHALLOW FALSE
)
set(SDL_SHARED OFF CACHE BOOL "" FORCE)
set(SDL_STATIC ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(SDL3)
endif()
# nlohmann/json (header-only) — used for RPC, language files, and legacy theme formats
include(FetchContent)
FetchContent_Declare(
json
GIT_REPOSITORY https://github.com/nlohmann/json.git
GIT_TAG v3.11.3
GIT_SHALLOW TRUE
)
FetchContent_MakeAvailable(json)
# toml++ (header-only) — used for UI schema / theme configuration files
FetchContent_Declare(
tomlplusplus
GIT_REPOSITORY https://github.com/marzer/tomlplusplus.git
GIT_TAG v3.4.0
GIT_SHALLOW TRUE
)
FetchContent_MakeAvailable(tomlplusplus)
# SQLite amalgamation - local Explorer block-summary cache
FetchContent_Declare(
sqlite3
URL https://www.sqlite.org/2024/sqlite-amalgamation-3450300.zip
URL_HASH SHA256=ea170e73e447703e8359308ca2e4366a3ae0c4304a8665896f068c736781c651
)
FetchContent_GetProperties(sqlite3)
if(NOT sqlite3_POPULATED)
FetchContent_Populate(sqlite3)
endif()
file(GLOB SQLITE3_AMALGAMATION_C CONFIGURE_DEPENDS
${sqlite3_SOURCE_DIR}/sqlite3.c
${sqlite3_SOURCE_DIR}/*/sqlite3.c
)
if(NOT SQLITE3_AMALGAMATION_C)
message(FATAL_ERROR "SQLite amalgamation source not found")
endif()
list(GET SQLITE3_AMALGAMATION_C 0 SQLITE3_SOURCE_FILE)
get_filename_component(SQLITE3_INCLUDE_DIR ${SQLITE3_SOURCE_FILE} DIRECTORY)
add_library(sqlite3_amalgamation STATIC ${SQLITE3_SOURCE_FILE})
target_include_directories(sqlite3_amalgamation PUBLIC ${SQLITE3_INCLUDE_DIR})
target_compile_definitions(sqlite3_amalgamation PRIVATE
SQLITE_THREADSAFE=1
SQLITE_OMIT_LOAD_EXTENSION
)
# libcurl for HTTPS RPC connections (more reliable than cpp-httplib with OpenSSL 3.x)
if(WIN32)
# For Windows cross-compilation, fetch and build libcurl statically
message(STATUS "Fetching libcurl for Windows build...")
FetchContent_Declare(
curl
URL https://github.com/curl/curl/releases/download/curl-8_5_0/curl-8.5.0.tar.gz
URL_HASH SHA256=05fc17ff25b793a437a0906e0484b82172a9f4de02be5ed447e0cab8c3475add
)
set(BUILD_CURL_EXE OFF CACHE BOOL "" FORCE)
set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
set(CURL_STATICLIB ON CACHE BOOL "" FORCE)
set(BUILD_TESTING OFF CACHE BOOL "" FORCE)
set(CURL_DISABLE_LDAP ON CACHE BOOL "" FORCE)
set(CURL_DISABLE_LDAPS ON CACHE BOOL "" FORCE)
set(CURL_USE_SCHANNEL ON CACHE BOOL "" FORCE) # Use Windows native SSL
set(CURL_USE_OPENSSL OFF CACHE BOOL "" FORCE)
set(CURL_USE_LIBSSH2 OFF CACHE BOOL "" FORCE)
set(CURL_ZLIB OFF CACHE BOOL "" FORCE)
set(HTTP_ONLY ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(curl)
set(CURL_LIBRARIES libcurl_static)
set(CURL_INCLUDE_DIRS ${curl_SOURCE_DIR}/include ${curl_BINARY_DIR}/lib/curl)
else()
find_package(CURL REQUIRED)
set(CURL_LIBRARIES CURL::libcurl)
set(CURL_INCLUDE_DIRS ${CURL_INCLUDE_DIR})
endif()
# libsodium - platform-specific
# Search order per platform:
# 1. Local pre-built in libs/libsodium{-mac,-win}/ (downloaded by scripts/fetch-libsodium.sh)
# 2. System libsodium via pkg-config
# 3. Auto-download from source (requires curl + C compiler for target)
set(SODIUM_LIBRARY "")
set(SODIUM_INCLUDE_DIR "")
if(WIN32)
# Windows (MinGW cross-compile) — no pkg-config fallback; host pkg-config
# returns Linux paths that can't be used by MinGW.
if(EXISTS ${CMAKE_SOURCE_DIR}/libs/libsodium-win/lib/libsodium.a)
set(SODIUM_LIBRARY ${CMAKE_SOURCE_DIR}/libs/libsodium-win/lib/libsodium.a)
set(SODIUM_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/libs/libsodium-win/include)
elseif(EXISTS ${CMAKE_SOURCE_DIR}/libs/libsodium/lib/libsodium.a)
set(SODIUM_LIBRARY ${CMAKE_SOURCE_DIR}/libs/libsodium/lib/libsodium.a)
set(SODIUM_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/libs/libsodium/include)
endif()
elseif(APPLE)
# macOS (native or osxcross cross-compile) — use local pre-built only;
# when cross-compiling, host pkg-config returns wrong paths.
if(EXISTS ${CMAKE_SOURCE_DIR}/libs/libsodium-mac/lib/libsodium.a)
set(SODIUM_LIBRARY ${CMAKE_SOURCE_DIR}/libs/libsodium-mac/lib/libsodium.a)
set(SODIUM_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/libs/libsodium-mac/include)
elseif(EXISTS ${CMAKE_SOURCE_DIR}/libs/libsodium/lib/libsodium.a)
set(SODIUM_LIBRARY ${CMAKE_SOURCE_DIR}/libs/libsodium/lib/libsodium.a)
set(SODIUM_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/libs/libsodium/include)
endif()
else()
# Linux: prefer system libsodium, fall back to local build
find_package(PkgConfig QUIET)
if(PKG_CONFIG_FOUND)
pkg_check_modules(SODIUM QUIET libsodium)
endif()
if(NOT SODIUM_FOUND AND EXISTS ${CMAKE_SOURCE_DIR}/libs/libsodium/lib/libsodium.a)
set(SODIUM_LIBRARY ${CMAKE_SOURCE_DIR}/libs/libsodium/lib/libsodium.a)
set(SODIUM_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/libs/libsodium/include)
endif()
endif()
# Bridge pkg-config variables to the ones used in target_link/include
if(SODIUM_FOUND AND NOT SODIUM_LIBRARY)
set(SODIUM_LIBRARY ${SODIUM_LIBRARIES})
set(SODIUM_INCLUDE_DIR ${SODIUM_INCLUDE_DIRS})
endif()
# Final check: if still not found, tell the user how to get it
if(NOT SODIUM_LIBRARY AND NOT SODIUM_FOUND)
message(WARNING
"libsodium not found. Encryption features require libsodium.\n"
" Linux: sudo apt install libsodium-dev (or equivalent)\n"
" macOS: ./scripts/fetch-libsodium.sh --mac\n"
" Windows: ./scripts/fetch-libsodium.sh --win")
endif()
message(STATUS "Sodium lib: ${SODIUM_LIBRARY}")
# -----------------------------------------------------------------------------
# ImGui
# -----------------------------------------------------------------------------
set(IMGUI_DIR ${CMAKE_SOURCE_DIR}/libs/imgui)
set(IMGUI_SOURCES
${IMGUI_DIR}/imgui.cpp
${IMGUI_DIR}/imgui_draw.cpp
${IMGUI_DIR}/imgui_tables.cpp
${IMGUI_DIR}/imgui_widgets.cpp
${IMGUI_DIR}/imgui_demo.cpp
${IMGUI_DIR}/backends/imgui_impl_sdl3.cpp
)
set(IMGUI_HEADERS
${IMGUI_DIR}/imgui.h
${IMGUI_DIR}/imconfig.h
${IMGUI_DIR}/imgui_internal.h
${IMGUI_DIR}/backends/imgui_impl_sdl3.h
)
# Platform-specific ImGui backend
if(WIN32)
list(APPEND IMGUI_SOURCES ${IMGUI_DIR}/backends/imgui_impl_dx11.cpp)
list(APPEND IMGUI_HEADERS ${IMGUI_DIR}/backends/imgui_impl_dx11.h)
else()
list(APPEND IMGUI_SOURCES ${IMGUI_DIR}/backends/imgui_impl_opengl3.cpp)
list(APPEND IMGUI_HEADERS ${IMGUI_DIR}/backends/imgui_impl_opengl3.h)
endif()
# -----------------------------------------------------------------------------
# QR Code library (bundled)
# -----------------------------------------------------------------------------
set(QRCODE_DIR ${CMAKE_SOURCE_DIR}/libs/qrcode)
set(QRCODE_SOURCES
${QRCODE_DIR}/BitBuffer.cpp
${QRCODE_DIR}/QrCode.cpp
${QRCODE_DIR}/QrSegment.cpp
)
# -----------------------------------------------------------------------------
# Application Sources
# -----------------------------------------------------------------------------
set(APP_SOURCES
src/main.cpp
src/app.cpp
src/app_network.cpp
src/app_security.cpp
src/app_wizard.cpp
src/services/network_refresh_service.cpp
src/services/refresh_scheduler.cpp
src/services/wallet_security_controller.cpp
src/services/wallet_security_workflow.cpp
src/services/wallet_security_workflow_executor.cpp
src/chat/chat_protocol.cpp
src/wallet/lite_owned_string.cpp
src/wallet/lite_rollout_policy.cpp
src/wallet/lite_client_bridge.cpp
src/wallet/lite_connection_service.cpp
src/wallet/lite_diagnostics.cpp
src/wallet/lite_wallet_controller.cpp
src/wallet/lite_result_parsers.cpp
src/wallet/lite_sync_service.cpp
src/wallet/lite_wallet_gateway.cpp
src/wallet/lite_wallet_state_mapper.cpp
src/wallet/lite_wallet_lifecycle_ui_adapter.cpp
src/wallet/lite_wallet_server_selection_adapter.cpp
src/wallet/lite_wallet_server_lifecycle_readiness.cpp
src/wallet/lite_wallet_lifecycle_service.cpp
src/data/wallet_state.cpp
src/data/transaction_history_cache.cpp
src/ui/theme.cpp
src/ui/theme_loader.cpp
src/ui/explorer/explorer_block_cache.cpp
src/ui/material/color_theme.cpp
src/ui/material/typography.cpp
src/ui/notifications.cpp
src/ui/windows/main_window.cpp
src/ui/windows/balance_tab.cpp
src/ui/windows/balance_components.cpp
src/ui/windows/balance_address_list.cpp
src/ui/windows/balance_recent_tx.cpp
src/ui/windows/balance_tab_helpers.cpp
src/ui/windows/send_tab.cpp
src/ui/windows/receive_tab.cpp
src/ui/windows/transactions_tab.cpp
src/ui/windows/mining_tab.cpp
src/ui/windows/mining_earnings.cpp
src/ui/windows/mining_stats.cpp
src/ui/windows/mining_controls.cpp
src/ui/windows/mining_mode_toggle.cpp
src/ui/windows/mining_benchmark.cpp
src/ui/windows/mining_pool_panel.cpp
src/ui/windows/mining_tab_helpers.cpp
src/ui/windows/peers_tab.cpp
src/ui/windows/network_tab.cpp
src/ui/windows/explorer_tab.cpp
src/ui/windows/market_tab.cpp
src/ui/windows/console_tab.cpp
src/ui/windows/console_command_executor.cpp
src/ui/windows/console_command_reference.cpp
src/ui/windows/console_input_model.cpp
src/ui/windows/console_model.cpp
src/ui/windows/console_output_model.cpp
src/ui/windows/console_tab_helpers.cpp
src/ui/windows/console_text_layout.cpp
src/ui/windows/settings_window.cpp
src/ui/pages/settings_page.cpp
src/ui/windows/about_dialog.cpp
src/ui/windows/key_export_dialog.cpp
src/ui/windows/transaction_details_dialog.cpp
src/ui/windows/qr_popup_dialog.cpp
src/ui/windows/validate_address_dialog.cpp
src/ui/windows/address_book_dialog.cpp
src/ui/windows/shield_dialog.cpp
src/ui/windows/request_payment_dialog.cpp
src/ui/windows/block_info_dialog.cpp
src/ui/windows/import_key_dialog.cpp
src/ui/windows/export_all_keys_dialog.cpp
src/ui/windows/export_transactions_dialog.cpp
src/ui/windows/backup_wallet_dialog.cpp
src/ui/widgets/qr_code.cpp
src/rpc/rpc_client.cpp
src/rpc/rpc_worker.cpp
src/rpc/connection.cpp
src/config/settings.cpp
src/data/address_book.cpp
src/data/exchange_info.cpp
src/util/logger.cpp
src/util/async_task_manager.cpp
src/util/amount_format.cpp
src/util/address_validation.cpp
src/util/base64.cpp
src/util/single_instance.cpp
src/util/i18n.cpp
src/util/text_format.cpp
src/util/platform.cpp
src/util/payment_uri.cpp
src/util/texture_loader.cpp
src/util/noise_texture.cpp
src/daemon/embedded_daemon.cpp
src/daemon/daemon_controller.cpp
src/daemon/lifecycle_adapters.cpp
src/daemon/xmrig_manager.cpp
src/util/bootstrap.cpp
src/util/lite_server_probe.cpp
src/util/pool_registry_core.cpp
src/util/pool_stats_service.cpp
src/util/xmrig_updater.cpp
src/util/xmrig_updater_core.cpp
src/util/daemon_updater.cpp
src/util/daemon_updater_core.cpp
src/util/secure_vault.cpp
src/ui/effects/framebuffer.cpp
src/ui/effects/blur_shader.cpp
src/ui/effects/noise_texture.cpp
src/ui/effects/acrylic.cpp
src/ui/effects/imgui_acrylic.cpp
src/ui/effects/theme_effects.cpp
src/ui/effects/low_spec.cpp
src/ui/schema/color_var_resolver.cpp
src/ui/schema/element_styles.cpp
src/ui/schema/ui_schema.cpp
src/ui/schema/skin_manager.cpp
src/resources/embedded_resources.cpp
)
# Note: The old -O0 workaround for embedded_resources.cpp is no longer needed.
# With INCBIN (.incbin assembler directive), the compiler never parses the
# binary data — the assembler streams it directly into the object file,
# using near-zero compile-time RAM instead of 12 GB+.
# Platform-specific sources
if(WIN32)
list(APPEND APP_SOURCES
src/platform/windows_backdrop.cpp
src/platform/dx11_context.cpp
)
endif()
set(APP_HEADERS
src/app.h
src/services/network_refresh_service.h
src/services/refresh_scheduler.h
src/services/wallet_security_controller.h
src/services/wallet_security_workflow.h
src/services/wallet_security_workflow_executor.h
src/wallet/wallet_capabilities.h
src/wallet/wallet_backend.h
src/wallet/lite_owned_string.h
src/wallet/lite_rollout_policy.h
src/wallet/lite_client_bridge.h
src/wallet/lite_connection_service.h
src/wallet/lite_result_parsers.h
src/wallet/lite_sync_service.h
src/wallet/lite_wallet_gateway.h
src/wallet/lite_wallet_state_mapper.h
src/wallet/lite_wallet_lifecycle_ui_adapter.h
src/wallet/lite_wallet_server_selection_adapter.h
src/wallet/lite_wallet_server_lifecycle_readiness.h
src/wallet/lite_wallet_lifecycle_service.h
src/chat/chat_protocol.h
src/config/version.h
src/data/wallet_state.h
src/data/transaction_history_cache.h
src/ui/theme.h
src/ui/theme_loader.h
src/ui/explorer/explorer_block_cache.h
src/ui/notifications.h
src/ui/windows/main_window.h
src/ui/windows/balance_tab.h
src/ui/windows/balance_address_list.h
src/ui/windows/balance_recent_tx.h
src/ui/windows/balance_tab_helpers.h
src/ui/windows/send_tab.h
src/ui/windows/receive_tab.h
src/ui/windows/transactions_tab.h
src/ui/windows/mining_tab.h
src/ui/windows/mining_benchmark.h
src/ui/windows/mining_pool_panel.h
src/ui/windows/mining_tab_helpers.h
src/ui/windows/peers_tab.h
src/ui/windows/explorer_tab.h
src/ui/windows/market_tab.h
src/ui/windows/console_channel.h
src/ui/windows/console_command_reference.h
src/ui/windows/console_input_model.h
src/ui/windows/console_model.h
src/ui/windows/console_output_model.h
src/ui/windows/console_tab.h
src/ui/windows/console_tab_helpers.h
src/ui/windows/settings_window.h
src/ui/windows/about_dialog.h
src/ui/windows/key_export_dialog.h
src/ui/windows/transaction_details_dialog.h
src/ui/windows/qr_popup_dialog.h
src/ui/windows/validate_address_dialog.h
src/ui/windows/address_book_dialog.h
src/ui/windows/shield_dialog.h
src/ui/windows/request_payment_dialog.h
src/ui/windows/block_info_dialog.h
src/ui/windows/import_key_dialog.h
src/ui/windows/export_all_keys_dialog.h
src/ui/windows/export_transactions_dialog.h
src/ui/windows/backup_wallet_dialog.h
src/ui/widgets/qr_code.h
src/rpc/rpc_client.h
src/rpc/rpc_worker.h
src/rpc/connection.h
src/rpc/types.h
src/config/settings.h
src/data/address_book.h
src/data/exchange_info.h
src/util/logger.h
src/util/async_task_manager.h
src/util/amount_format.h
src/util/base64.h
src/util/single_instance.h
src/util/i18n.h
src/util/platform.h
src/util/payment_uri.h
src/util/secure_vault.h
src/daemon/embedded_daemon.h
src/daemon/daemon_controller.h
src/daemon/lifecycle_adapters.h
src/daemon/xmrig_manager.h
src/ui/effects/framebuffer.h
src/ui/effects/blur_shader.h
src/ui/effects/noise_texture.h
src/ui/effects/acrylic.h
)
# Platform-specific headers
if(WIN32)
list(APPEND APP_HEADERS
src/platform/windows_backdrop.h
src/platform/dx11_context.h
)
endif()
# -----------------------------------------------------------------------------
# Executable
# -----------------------------------------------------------------------------
# Windows application icon + VERSIONINFO (.rc -> .res -> linked into .exe)
if(WIN32)
set(OBSIDIAN_ICO_PATH "${CMAKE_SOURCE_DIR}/res/img/ObsidianDragon.ico")
# Generate manifest with version from project()
configure_file(
${CMAKE_SOURCE_DIR}/res/ObsidianDragon.manifest.in
${CMAKE_SOURCE_DIR}/res/ObsidianDragon.manifest
@ONLY
)
set(OBSIDIAN_MANIFEST_PATH "${CMAKE_SOURCE_DIR}/res/ObsidianDragon.manifest")
# Generate .rc with version from project()
configure_file(
${CMAKE_SOURCE_DIR}/res/ObsidianDragon.rc
${CMAKE_BINARY_DIR}/generated/ObsidianDragon.rc
@ONLY
)
set(WIN_RC_FILE ${CMAKE_BINARY_DIR}/generated/ObsidianDragon.rc)
endif()
# Generate version values from the single project(VERSION ...) declaration.
# Keep the build-specific app name in the build tree so full/lite configures do
# not rewrite a tracked source header.
configure_file(
${CMAKE_SOURCE_DIR}/src/config/version.h.in
${CMAKE_BINARY_DIR}/generated/dragonx_generated_version.h
@ONLY
)
# Generate INCBIN font embedding source with absolute paths to .ttf files
configure_file(
${CMAKE_SOURCE_DIR}/src/embedded/embedded_fonts.cpp.in
${CMAKE_BINARY_DIR}/generated/embedded_fonts.cpp
@ONLY
)
# INCBIN uses .incbin assembler directives that reference font files at
# assembly time — CMake doesn't track these implicit dependencies.
# Tell CMake that the generated source depends on the actual font binaries
# so a font file change triggers recompilation.
set_source_files_properties(
${CMAKE_BINARY_DIR}/generated/embedded_fonts.cpp
PROPERTIES OBJECT_DEPENDS
"${CMAKE_SOURCE_DIR}/res/fonts/Ubuntu-R.ttf;\
${CMAKE_SOURCE_DIR}/res/fonts/Ubuntu-Light.ttf;\
${CMAKE_SOURCE_DIR}/res/fonts/Ubuntu-Medium.ttf;\
${CMAKE_SOURCE_DIR}/res/fonts/MaterialIcons-Regular.ttf;\
${CMAKE_SOURCE_DIR}/res/fonts/MaterialDesignIcons-Pickaxe-Subset.ttf;\
${CMAKE_SOURCE_DIR}/res/fonts/NotoSansCJK-Subset.ttf"
)
add_executable(ObsidianDragon
${APP_SOURCES}
${CMAKE_BINARY_DIR}/generated/embedded_fonts.cpp
${IMGUI_SOURCES}
${QRCODE_SOURCES}
${GLAD_SOURCES}
${MINIZ_SOURCES}
${WIN_RC_FILE}
)
set_target_properties(ObsidianDragon PROPERTIES OUTPUT_NAME "${DRAGONX_BINARY_NAME}")
target_include_directories(ObsidianDragon PRIVATE
${CMAKE_SOURCE_DIR}/src
${CMAKE_SOURCE_DIR}/src/embedded
${CMAKE_SOURCE_DIR}/src/resources
${CMAKE_SOURCE_DIR}/libs
${CMAKE_BINARY_DIR}/generated
${IMGUI_DIR}
${IMGUI_DIR}/backends
${QRCODE_DIR}
${SODIUM_INCLUDE_DIR}
${GLAD_INCLUDE}
${CURL_INCLUDE_DIRS}
${MINIZ_DIR}
)
target_link_libraries(ObsidianDragon PRIVATE
SDL3::SDL3
nlohmann_json::nlohmann_json
tomlplusplus::tomlplusplus
sqlite3_amalgamation
${CURL_LIBRARIES}
${SODIUM_LIBRARY}
)
if(DRAGONX_LITE_BACKEND_READY)
target_link_libraries(ObsidianDragon PRIVATE dragonx_lite_backend ${DRAGONX_LITE_BACKEND_EXTRA_LIBS})
# Real-backend smoke tool (only built when a real lite backend is linked).
add_executable(lite_smoke
tools/lite_smoke.cpp
src/wallet/lite_client_bridge.cpp
src/wallet/lite_owned_string.cpp
src/wallet/lite_rollout_policy.cpp
src/wallet/lite_connection_service.cpp
src/wallet/lite_result_parsers.cpp
)
target_include_directories(lite_smoke PRIVATE
${CMAKE_SOURCE_DIR}/src
${CMAKE_BINARY_DIR}/generated
${SODIUM_INCLUDE_DIR}
)
target_compile_definitions(lite_smoke PRIVATE DRAGONX_ENABLE_LITE_BACKEND=1)
target_link_libraries(lite_smoke PRIVATE
dragonx_lite_backend ${DRAGONX_LITE_BACKEND_EXTRA_LIBS}
nlohmann_json::nlohmann_json
${SODIUM_LIBRARY}
)
if(UNIX)
target_link_libraries(lite_smoke PRIVATE ${CMAKE_DL_LIBS} pthread)
endif()
# Real-backend SEND smoke tool — drives the exact GUI send path (bridge.execute("send", ...)).
add_executable(lite_send_smoke
tools/lite_send_smoke.cpp
src/wallet/lite_client_bridge.cpp
src/wallet/lite_owned_string.cpp
src/wallet/lite_rollout_policy.cpp
src/wallet/lite_connection_service.cpp
src/wallet/lite_result_parsers.cpp
)
target_include_directories(lite_send_smoke PRIVATE
${CMAKE_SOURCE_DIR}/src
${CMAKE_BINARY_DIR}/generated
${SODIUM_INCLUDE_DIR}
)
target_compile_definitions(lite_send_smoke PRIVATE DRAGONX_ENABLE_LITE_BACKEND=1)
target_link_libraries(lite_send_smoke PRIVATE
dragonx_lite_backend ${DRAGONX_LITE_BACKEND_EXTRA_LIBS}
nlohmann_json::nlohmann_json
${SODIUM_LIBRARY}
)
if(UNIX)
target_link_libraries(lite_send_smoke PRIVATE ${CMAKE_DL_LIBS} pthread)
endif()
endif()
# Platform-specific settings
if(WIN32)
target_link_libraries(ObsidianDragon PRIVATE ws2_32 winmm imm32 version setupapi dwmapi crypt32 wldap32 psapi iphlpapi d3d11 dxgi d3dcompiler dcomp)
# Hide console window in release builds
if(CMAKE_BUILD_TYPE STREQUAL "Release")
set_target_properties(ObsidianDragon PROPERTIES WIN32_EXECUTABLE TRUE)
endif()
elseif(APPLE)
target_link_libraries(ObsidianDragon PRIVATE "-framework Cocoa" "-framework IOKit" "-framework CoreVideo" "-framework OpenGL")
# When cross-compiling with osxcross, link the compiler-rt builtins (provides ___isPlatformVersionAtLeast etc.)
if(CMAKE_CROSSCOMPILING AND DEFINED OSXCROSS_COMPILER_RT)
target_link_libraries(ObsidianDragon PRIVATE "${OSXCROSS_COMPILER_RT}")
endif()
elseif(UNIX)
find_package(Threads REQUIRED)
target_link_libraries(ObsidianDragon PRIVATE OpenGL::GL Threads::Threads ${CMAKE_DL_LIBS})
endif()
# Compiler warnings
if(MSVC)
target_compile_options(ObsidianDragon PRIVATE /W4)
else()
target_compile_options(ObsidianDragon PRIVATE -Wall -Wextra -Wpedantic)
endif()
# Compile definitions
target_compile_definitions(ObsidianDragon PRIVATE
DRAGONX_DEBUG
DRAGONX_LITE_BUILD=$<BOOL:${DRAGONX_BUILD_LITE}>
DRAGONX_ENABLE_EMBEDDED_DAEMON=$<BOOL:${DRAGONX_ENABLE_EMBEDDED_DAEMON}>
DRAGONX_ENABLE_LITE_BACKEND=$<BOOL:${DRAGONX_LITE_BACKEND_READY}>
DRAGONX_ENABLE_CHAT=$<BOOL:${DRAGONX_ENABLE_CHAT}>
)
if(WIN32)
target_compile_definitions(ObsidianDragon PRIVATE DRAGONX_USE_DX11)
else()
target_compile_definitions(ObsidianDragon PRIVATE DRAGONX_HAS_GLAD)
endif()
add_executable(HushChatFixtureCheck
tools/hushchat_fixture_check.cpp
src/chat/chat_protocol.cpp
)
target_include_directories(HushChatFixtureCheck PRIVATE
${CMAKE_SOURCE_DIR}/src
${SODIUM_INCLUDE_DIR}
)
target_link_libraries(HushChatFixtureCheck PRIVATE
nlohmann_json::nlohmann_json
${SODIUM_LIBRARY}
)
target_compile_definitions(HushChatFixtureCheck PRIVATE
DRAGONX_ENABLE_CHAT=0
)
# -----------------------------------------------------------------------------
# Copy resources
# -----------------------------------------------------------------------------
# Copy font file - try local first, then SilentDragonX
if(EXISTS ${CMAKE_SOURCE_DIR}/res/fonts/Ubuntu-R.ttf)
configure_file(
${CMAKE_SOURCE_DIR}/res/fonts/Ubuntu-R.ttf
${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/res/fonts/Ubuntu-R.ttf
COPYONLY
)
elseif(EXISTS ${CMAKE_SOURCE_DIR}/../SilentDragonX/res/Ubuntu-R.ttf)
configure_file(
${CMAKE_SOURCE_DIR}/../SilentDragonX/res/Ubuntu-R.ttf
${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/res/fonts/Ubuntu-R.ttf
COPYONLY
)
endif()
# Copy language files at BUILD time (not just cmake configure time)
# so edits to res/lang/*.json are picked up by 'make' without re-running cmake.
file(GLOB LANG_FILES ${CMAKE_SOURCE_DIR}/res/lang/*.json)
if(LANG_FILES)
find_program(XXD_EXECUTABLE NAMES xxd)
if(NOT XXD_EXECUTABLE)
message(WARNING "xxd not found; runtime language JSON files will be copied, but embedded build/generated/embedded/lang_*.h files will not be regenerated")
endif()
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/generated/embedded)
foreach(LANG_FILE ${LANG_FILES})
get_filename_component(LANG_FILENAME ${LANG_FILE} NAME)
add_custom_command(
OUTPUT ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/res/lang/${LANG_FILENAME}
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${LANG_FILE}
${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/res/lang/${LANG_FILENAME}
DEPENDS ${LANG_FILE}
COMMENT "Copying ${LANG_FILENAME}"
)
list(APPEND LANG_OUTPUTS ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/res/lang/${LANG_FILENAME})
# Also regenerate the embedded header so the binary always has fresh translations
if(XXD_EXECUTABLE)
get_filename_component(LANG_CODE ${LANG_FILENAME} NAME_WE)
set(LANG_HEADER ${CMAKE_BINARY_DIR}/generated/embedded/lang_${LANG_CODE}.h)
add_custom_command(
OUTPUT ${LANG_HEADER}
COMMAND ${XXD_EXECUTABLE} -i "res/lang/${LANG_FILENAME}" > "${LANG_HEADER}"
DEPENDS ${LANG_FILE}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
COMMENT "Embedding lang_${LANG_CODE}.h"
)
list(APPEND LANG_OUTPUTS ${LANG_HEADER})
endif()
endforeach()
add_custom_target(copy_langs ALL DEPENDS ${LANG_OUTPUTS})
add_dependencies(ObsidianDragon copy_langs)
message(STATUS " Language files: ${LANG_FILES}")
endif()
# Embed ui.toml into the binary so it's always available at runtime
include(${CMAKE_SOURCE_DIR}/cmake/EmbedResources.cmake)
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/generated)
embed_resource(
${CMAKE_SOURCE_DIR}/res/themes/ui.toml
${CMAKE_BINARY_DIR}/generated/ui_toml_embedded.h
ui_toml
)
embed_resource(
${CMAKE_SOURCE_DIR}/res/default_banlist.txt
${CMAKE_BINARY_DIR}/generated/default_banlist_embedded.h
default_banlist
)
# Note: xmrig is embedded via build.sh (embedded_data.h) for Windows builds,
# following the same pattern as daemon embedding.
# Expand and copy theme files at BUILD time — skin files get layout sections
# from ui.toml appended automatically so users can see/edit all properties.
# Source skin files stay minimal; the merged output goes to build/bin/res/themes/.
find_package(Python3 QUIET COMPONENTS Interpreter)
if(NOT Python3_FOUND)
find_program(Python3_EXECUTABLE NAMES python3 python)
endif()
file(GLOB THEME_FILES ${CMAKE_SOURCE_DIR}/res/themes/*.toml)
if(THEME_FILES AND Python3_EXECUTABLE)
add_custom_command(
OUTPUT ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/res/themes/.expanded
COMMAND ${Python3_EXECUTABLE} ${CMAKE_SOURCE_DIR}/scripts/expand_themes.py
${CMAKE_SOURCE_DIR}/res/themes
${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/res/themes
COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/res/themes/.expanded
DEPENDS ${THEME_FILES} ${CMAKE_SOURCE_DIR}/scripts/expand_themes.py
COMMENT "Expanding theme files (merging layout from ui.toml)"
)
add_custom_target(copy_themes ALL DEPENDS ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/res/themes/.expanded)
add_dependencies(ObsidianDragon copy_themes)
message(STATUS " Theme files: ${THEME_FILES} (build-time expansion via Python)")
elseif(THEME_FILES)
# Fallback: plain copy if Python is not available
message(WARNING "Python3 not found; copying theme files without expand_themes.py layout merge")
foreach(THEME_FILE ${THEME_FILES})
get_filename_component(THEME_FILENAME ${THEME_FILE} NAME)
add_custom_command(
OUTPUT ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/res/themes/${THEME_FILENAME}
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${THEME_FILE}
${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/res/themes/${THEME_FILENAME}
DEPENDS ${THEME_FILE}
COMMENT "Copying ${THEME_FILENAME}"
)
list(APPEND THEME_OUTPUTS ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/res/themes/${THEME_FILENAME})
endforeach()
add_custom_target(copy_themes ALL DEPENDS ${THEME_OUTPUTS})
add_dependencies(ObsidianDragon copy_themes)
message(STATUS " Theme files: ${THEME_FILES} (plain copy, Python not found)")
endif()
# Copy image files (including backgrounds/ subdirectories and logos/)
file(GLOB IMG_ROOT_FILES ${CMAKE_SOURCE_DIR}/res/img/*.png ${CMAKE_SOURCE_DIR}/res/img/*.jpg ${CMAKE_SOURCE_DIR}/res/img/*.ico)
file(GLOB IMG_BG_TEXTURE_FILES ${CMAKE_SOURCE_DIR}/res/img/backgrounds/texture/*.png ${CMAKE_SOURCE_DIR}/res/img/backgrounds/texture/*.jpg)
file(GLOB IMG_BG_GRADIENT_FILES ${CMAKE_SOURCE_DIR}/res/img/backgrounds/gradient/*.png ${CMAKE_SOURCE_DIR}/res/img/backgrounds/gradient/*.jpg)
file(GLOB IMG_LOGO_FILES ${CMAKE_SOURCE_DIR}/res/img/logos/*.png ${CMAKE_SOURCE_DIR}/res/img/logos/*.jpg)
foreach(IMG_FILE ${IMG_ROOT_FILES})
get_filename_component(IMG_FILENAME ${IMG_FILE} NAME)
configure_file(${IMG_FILE} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/res/img/${IMG_FILENAME} COPYONLY)
endforeach()
foreach(IMG_FILE ${IMG_BG_TEXTURE_FILES})
get_filename_component(IMG_FILENAME ${IMG_FILE} NAME)
configure_file(${IMG_FILE} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/res/img/backgrounds/texture/${IMG_FILENAME} COPYONLY)
endforeach()
foreach(IMG_FILE ${IMG_BG_GRADIENT_FILES})
get_filename_component(IMG_FILENAME ${IMG_FILE} NAME)
configure_file(${IMG_FILE} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/res/img/backgrounds/gradient/${IMG_FILENAME} COPYONLY)
endforeach()
foreach(IMG_FILE ${IMG_LOGO_FILES})
get_filename_component(IMG_FILENAME ${IMG_FILE} NAME)
configure_file(${IMG_FILE} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/res/img/logos/${IMG_FILENAME} COPYONLY)
endforeach()
message(STATUS " Image files: ${IMG_ROOT_FILES} ${IMG_BG_TEXTURE_FILES} ${IMG_BG_GRADIENT_FILES} ${IMG_LOGO_FILES}")
# -----------------------------------------------------------------------------
# Install
# -----------------------------------------------------------------------------
install(TARGETS ObsidianDragon
RUNTIME DESTINATION bin
)
install(DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/res
DESTINATION share/${DRAGONX_BINARY_NAME}
OPTIONAL
)
# -----------------------------------------------------------------------------
# Tests
# -----------------------------------------------------------------------------
if(BUILD_TESTING)
add_executable(ObsidianDragonTests
tests/test_phase4.cpp
src/services/network_refresh_service.cpp
src/services/refresh_scheduler.cpp
src/services/wallet_security_controller.cpp
src/services/wallet_security_workflow.cpp
src/services/wallet_security_workflow_executor.cpp
src/chat/chat_protocol.cpp
src/wallet/lite_owned_string.cpp
src/wallet/lite_rollout_policy.cpp
src/wallet/lite_client_bridge.cpp
src/wallet/lite_connection_service.cpp
src/wallet/lite_diagnostics.cpp
src/wallet/lite_wallet_controller.cpp
src/wallet/lite_result_parsers.cpp
src/wallet/lite_sync_service.cpp
src/wallet/lite_wallet_gateway.cpp
src/wallet/lite_wallet_state_mapper.cpp
src/wallet/lite_wallet_lifecycle_ui_adapter.cpp
src/wallet/lite_wallet_server_selection_adapter.cpp
src/wallet/lite_wallet_server_lifecycle_readiness.cpp
src/wallet/lite_wallet_lifecycle_service.cpp
src/ui/explorer/explorer_block_cache.cpp
src/ui/windows/balance_address_list.cpp
src/ui/windows/balance_recent_tx.cpp
src/ui/windows/console_input_model.cpp
src/ui/windows/console_model.cpp
src/ui/windows/console_output_model.cpp
src/ui/windows/console_tab_helpers.cpp
src/ui/windows/console_text_layout.cpp
src/ui/windows/mining_benchmark.cpp
src/ui/windows/mining_pool_panel.cpp
src/ui/windows/mining_tab_helpers.cpp
src/util/payment_uri.cpp
src/util/amount_format.cpp
src/util/address_validation.cpp
src/util/i18n.cpp
src/util/text_format.cpp
src/data/wallet_state.cpp
src/data/transaction_history_cache.cpp
src/daemon/lifecycle_adapters.cpp
src/rpc/connection.cpp
src/config/settings.cpp
src/resources/embedded_resources.cpp
src/util/secure_vault.cpp
src/util/platform.cpp
src/util/logger.cpp
src/util/lite_server_probe.cpp
src/util/pool_registry_core.cpp
src/util/xmrig_updater.cpp
src/util/xmrig_updater_core.cpp
src/util/daemon_updater.cpp
src/util/daemon_updater_core.cpp
${MINIZ_SOURCES}
)
target_include_directories(ObsidianDragonTests PRIVATE
${CMAKE_SOURCE_DIR}/src
${CMAKE_SOURCE_DIR}/src/resources
${CMAKE_SOURCE_DIR}/libs
${CMAKE_BINARY_DIR}/generated
${IMGUI_DIR}
${SODIUM_INCLUDE_DIR}
${CURL_INCLUDE_DIRS}
${MINIZ_DIR}
)
target_link_libraries(ObsidianDragonTests PRIVATE
nlohmann_json::nlohmann_json
sqlite3_amalgamation
${SODIUM_LIBRARY}
${CURL_LIBRARIES}
)
target_compile_definitions(ObsidianDragonTests PRIVATE
DRAGONX_ENABLE_CHAT=$<BOOL:${DRAGONX_ENABLE_CHAT}>
DRAGONX_LITE_BUILD=$<BOOL:${DRAGONX_BUILD_LITE}>
DRAGONX_ENABLE_EMBEDDED_DAEMON=$<BOOL:${DRAGONX_ENABLE_EMBEDDED_DAEMON}>
DRAGONX_ENABLE_LITE_BACKEND=$<BOOL:${DRAGONX_LITE_BACKEND_READY}>
DRAGONX_TEST_FIXTURE_DIR="${CMAKE_SOURCE_DIR}/tests/fixtures"
)
if(DRAGONX_LITE_BACKEND_READY)
target_link_libraries(ObsidianDragonTests PRIVATE dragonx_lite_backend ${DRAGONX_LITE_BACKEND_EXTRA_LIBS})
endif()
if(UNIX)
target_link_libraries(ObsidianDragonTests PRIVATE ${CMAKE_DL_LIBS})
endif()
add_test(NAME ObsidianDragonPhase4Tests COMMAND ObsidianDragonTests)
endif()
# -----------------------------------------------------------------------------
# Summary
# -----------------------------------------------------------------------------
message(STATUS "")
message(STATUS "DragonX ImGui Wallet Configuration:")
message(STATUS " Version: ${DRAGONX_APP_VERSION}${DRAGONX_APP_VERSION_SUFFIX} (${DRAGONX_APP_NAME})")
message(STATUS " Build type: ${CMAKE_BUILD_TYPE}")
message(STATUS " C++ Standard: ${CMAKE_CXX_STANDARD}")
message(STATUS " ImGui dir: ${IMGUI_DIR}")
message(STATUS " SDL3 found: ${SDL3_FOUND}")
message(STATUS " Sodium lib: ${SODIUM_LIBRARY}")
message(STATUS " Lite build: ${DRAGONX_BUILD_LITE}")
message(STATUS " Lite requested: ${DRAGONX_ENABLE_LITE_BACKEND}")
message(STATUS " Lite backend: ${DRAGONX_LITE_BACKEND_READY}")
message(STATUS " Lite lib: ${DRAGONX_LITE_BACKEND_LIBRARY}")
message(STATUS " Lite symbols: ${DRAGONX_LITE_BACKEND_SYMBOLS_FILE}")
message(STATUS " Lite manifest: ${DRAGONX_LITE_BACKEND_MANIFEST}")
message(STATUS " Lite signature: ${DRAGONX_LITE_BACKEND_REQUIRE_SIGNATURE}")
message(STATUS "")