@ -0,0 +1,2 @@
|
|||||||
|
cmake-build-debug/
|
||||||
|
.idea/
|
@ -0,0 +1,221 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.13)
|
||||||
|
project(moneroservice)
|
||||||
|
|
||||||
|
message(STATUS "Initiating compile using CMake ${CMAKE_VERSION}")
|
||||||
|
|
||||||
|
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||||
|
set(VERSION_MAJOR "0")
|
||||||
|
set(VERSION_MINOR "1")
|
||||||
|
set(VERSION_REVISION "0")
|
||||||
|
set(VERSION "beta-3")
|
||||||
|
|
||||||
|
list(INSERT CMAKE_MODULE_PATH 0 "${CMAKE_SOURCE_DIR}/cmake")
|
||||||
|
include(CheckCCompilerFlag)
|
||||||
|
include(CheckCXXCompilerFlag)
|
||||||
|
include(CheckLinkerFlag)
|
||||||
|
include(FetchContent)
|
||||||
|
include(FindCcache)
|
||||||
|
include(CheckIncludeFile)
|
||||||
|
include(CheckSymbolExists)
|
||||||
|
|
||||||
|
if(DEBUG)
|
||||||
|
set(CMAKE_VERBOSE_MAKEFILE ON)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(ARCH "x86-64")
|
||||||
|
set(BUILD_64 ON)
|
||||||
|
set(USE_SINGLE_BUILDDIR ON)
|
||||||
|
|
||||||
|
check_include_file(sys/prctl.h HAVE_SYS_PRCTL_H)
|
||||||
|
check_symbol_exists(prctl "sys/prctl.h" HAVE_PRCTL)
|
||||||
|
|
||||||
|
function (add_c_flag_if_supported flag var)
|
||||||
|
string(REPLACE "-" "_" supported ${flag}_c)
|
||||||
|
check_c_compiler_flag(${flag} ${supported})
|
||||||
|
if(${${supported}})
|
||||||
|
set(${var} "${${var}} ${flag}" PARENT_SCOPE)
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function (add_cxx_flag_if_supported flag var)
|
||||||
|
string(REPLACE "-" "_" supported ${flag}_cxx)
|
||||||
|
check_cxx_compiler_flag(${flag} ${supported})
|
||||||
|
if(${${supported}})
|
||||||
|
set(${var} "${${var}} ${flag}" PARENT_SCOPE)
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function (add_linker_flag_if_supported flag var)
|
||||||
|
string(REPLACE "-" "_" supported ${flag}_ld)
|
||||||
|
string(REPLACE "," "_" supported ${flag}_ld)
|
||||||
|
check_linker_flag(${flag} ${supported})
|
||||||
|
if(${${supported}})
|
||||||
|
set(${var} "${${var}} ${flag}" PARENT_SCOPE)
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
|
||||||
|
include(CMakePackageConfigHelpers)
|
||||||
|
|
||||||
|
if(UNIX AND NOT APPLE)
|
||||||
|
if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||||
|
# https://github.com/monero-project/monero-gui/issues/3142#issuecomment-705940446
|
||||||
|
set(CMAKE_SKIP_RPATH ON)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_package(X11 REQUIRED)
|
||||||
|
message(STATUS "X11_FOUND = ${X11_FOUND}")
|
||||||
|
message(STATUS "X11_INCLUDE_DIR = ${X11_INCLUDE_DIR}")
|
||||||
|
message(STATUS "X11_LIBRARIES = ${X11_LIBRARIES}")
|
||||||
|
include_directories(${X11_INCLUDE_DIR})
|
||||||
|
link_directories(${X11_LIBRARIES})
|
||||||
|
if(STATIC)
|
||||||
|
find_library(XCB_LIBRARY xcb)
|
||||||
|
message(STATUS "Found xcb library: ${XCB_LIBRARY}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
if(MINGW)
|
||||||
|
string(REGEX MATCH "^[^/]:/[^/]*" msys2_install_path "${CMAKE_C_COMPILER}")
|
||||||
|
message(STATUS "MSYS location: ${msys2_install_path}")
|
||||||
|
set(CMAKE_INCLUDE_PATH "${msys2_install_path}/mingw${ARCH_WIDTH}/include")
|
||||||
|
# This is necessary because otherwise CMake will make Boost libraries -lfoo
|
||||||
|
# rather than a full path. Unfortunately, this makes the shared libraries get
|
||||||
|
# linked due to a bug in CMake which misses putting -static flags around the
|
||||||
|
# -lfoo arguments.
|
||||||
|
set(DEFLIB ${msys2_install_path}/mingw${ARCH_WIDTH}/lib)
|
||||||
|
list(REMOVE_ITEM CMAKE_C_IMPLICIT_LINK_DIRECTORIES ${DEFLIB})
|
||||||
|
list(REMOVE_ITEM CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES ${DEFLIB})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
if(MINGW)
|
||||||
|
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wa,-mbig-obj")
|
||||||
|
set(EXTRA_LIBRARIES mswsock;ws2_32;iphlpapi;crypt32;bcrypt)
|
||||||
|
if(DEPENDS)
|
||||||
|
set(ICU_LIBRARIES iconv)
|
||||||
|
else()
|
||||||
|
set(ICU_LIBRARIES icuio icuin icuuc icudt icutu iconv)
|
||||||
|
endif()
|
||||||
|
elseif(APPLE)
|
||||||
|
set(EXTRA_LIBRARIES "-framework AppKit")
|
||||||
|
elseif(OPENBSD)
|
||||||
|
set(EXTRA_LIBRARIES "")
|
||||||
|
elseif(FREEBSD)
|
||||||
|
set(EXTRA_LIBRARIES execinfo)
|
||||||
|
elseif(DRAGONFLY)
|
||||||
|
find_library(COMPAT compat)
|
||||||
|
set(EXTRA_LIBRARIES execinfo ${COMPAT})
|
||||||
|
elseif(CMAKE_SYSTEM_NAME MATCHES "(SunOS|Solaris)")
|
||||||
|
set(EXTRA_LIBRARIES socket nsl resolv)
|
||||||
|
elseif(NOT MSVC AND NOT DEPENDS)
|
||||||
|
find_library(RT rt)
|
||||||
|
set(EXTRA_LIBRARIES ${RT})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
list(APPEND EXTRA_LIBRARIES ${CMAKE_DL_LIBS})
|
||||||
|
|
||||||
|
if(APPLE)
|
||||||
|
include_directories(SYSTEM /usr/include/malloc)
|
||||||
|
if(POLICY CMP0042)
|
||||||
|
cmake_policy(SET CMP0042 NEW)
|
||||||
|
endif()
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=x86-64 -fvisibility=default -std=c++11")
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=default -DGTEST_HAS_TR1_TUPLE=0")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (APPLE AND NOT IOS)
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=x86-64 -fvisibility=default -std=c++11")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(APPLE)
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=default -DGTEST_HAS_TR1_TUPLE=0")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# warnings
|
||||||
|
# @TODO: enable these 2 for migration to Qt 6
|
||||||
|
#add_c_flag_if_supported(-Werror C_SECURITY_FLAGS)
|
||||||
|
#add_cxx_flag_if_supported(-Werror CXX_SECURITY_FLAGS)
|
||||||
|
add_c_flag_if_supported(-Wformat C_SECURITY_FLAGS)
|
||||||
|
add_cxx_flag_if_supported(-Wformat CXX_SECURITY_FLAGS)
|
||||||
|
add_c_flag_if_supported(-Wformat-security C_SECURITY_FLAGS)
|
||||||
|
add_cxx_flag_if_supported(-Wformat-security CXX_SECURITY_FLAGS)
|
||||||
|
|
||||||
|
# -fstack-protector
|
||||||
|
if (NOT OPENBSD AND NOT (WIN32 AND (CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_C_COMPILER_VERSION VERSION_LESS 9.1)))
|
||||||
|
add_c_flag_if_supported(-fstack-protector C_SECURITY_FLAGS)
|
||||||
|
add_cxx_flag_if_supported(-fstack-protector CXX_SECURITY_FLAGS)
|
||||||
|
add_c_flag_if_supported(-fstack-protector-strong C_SECURITY_FLAGS)
|
||||||
|
add_cxx_flag_if_supported(-fstack-protector-strong CXX_SECURITY_FLAGS)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# New in GCC 8.2
|
||||||
|
if (NOT OPENBSD AND NOT (WIN32 AND (CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_C_COMPILER_VERSION VERSION_LESS 9.1)))
|
||||||
|
add_c_flag_if_supported(-fcf-protection=full C_SECURITY_FLAGS)
|
||||||
|
add_cxx_flag_if_supported(-fcf-protection=full CXX_SECURITY_FLAGS)
|
||||||
|
endif()
|
||||||
|
if (NOT WIN32 AND NOT OPENBSD)
|
||||||
|
add_c_flag_if_supported(-fstack-clash-protection C_SECURITY_FLAGS)
|
||||||
|
add_cxx_flag_if_supported(-fstack-clash-protection CXX_SECURITY_FLAGS)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Removed in GCC 9.1 (or before ?), but still accepted, so spams the output
|
||||||
|
if (NOT (CMAKE_C_COMPILER_ID STREQUAL "GNU" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 9.1))
|
||||||
|
add_c_flag_if_supported(-mmitigate-rop C_SECURITY_FLAGS)
|
||||||
|
add_cxx_flag_if_supported(-mmitigate-rop CXX_SECURITY_FLAGS)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# linker
|
||||||
|
if (APPLE)
|
||||||
|
add_linker_flag_if_supported(-Wl,-bind_at_load LD_SECURITY_FLAGS)
|
||||||
|
add_linker_flag_if_supported(-Wl,-dead_strip LD_SECURITY_FLAGS)
|
||||||
|
add_linker_flag_if_supported(-Wl,-dead_strip_dylibs LD_SECURITY_FLAGS)
|
||||||
|
endif()
|
||||||
|
if (NOT APPLE AND NOT (WIN32 AND CMAKE_C_COMPILER_ID STREQUAL "GNU"))
|
||||||
|
# Windows binaries die on startup with PIE when compiled with GCC
|
||||||
|
add_linker_flag_if_supported(-pie LD_SECURITY_FLAGS)
|
||||||
|
endif()
|
||||||
|
add_linker_flag_if_supported(-Wl,-z,relro LD_SECURITY_FLAGS)
|
||||||
|
add_linker_flag_if_supported(-Wl,-z,now LD_SECURITY_FLAGS)
|
||||||
|
add_linker_flag_if_supported(-Wl,-z,noexecstack noexecstack_SUPPORTED)
|
||||||
|
if (noexecstack_SUPPORTED)
|
||||||
|
set(LD_SECURITY_FLAGS "${LD_SECURITY_FLAGS} -Wl,-z,noexecstack")
|
||||||
|
endif()
|
||||||
|
add_linker_flag_if_supported(-Wl,-z,noexecheap noexecheap_SUPPORTED)
|
||||||
|
if (noexecheap_SUPPORTED)
|
||||||
|
set(LD_SECURITY_FLAGS "${LD_SECURITY_FLAGS} -Wl,-z,noexecheap")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# some windows linker bits
|
||||||
|
if (WIN32)
|
||||||
|
add_linker_flag_if_supported(-Wl,--dynamicbase LD_SECURITY_FLAGS)
|
||||||
|
add_linker_flag_if_supported(-Wl,--nxcompat LD_SECURITY_FLAGS)
|
||||||
|
add_linker_flag_if_supported(-Wl,--high-entropy-va LD_SECURITY_FLAGS)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(STATIC)
|
||||||
|
add_linker_flag_if_supported(-static-libgcc STATIC_FLAGS)
|
||||||
|
add_linker_flag_if_supported(-static-libstdc++ STATIC_FLAGS)
|
||||||
|
if(MINGW)
|
||||||
|
add_linker_flag_if_supported(-static STATIC_FLAGS)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# With GCC 6.1.1 the compiled binary malfunctions due to aliasing. Until that
|
||||||
|
# is fixed in the code (Issue #847), force compiler to be conservative.
|
||||||
|
add_c_flag_if_supported(-fno-strict-aliasing C_SECURITY_FLAGS)
|
||||||
|
add_cxx_flag_if_supported(-fno-strict-aliasing CXX_SECURITY_FLAGS)
|
||||||
|
|
||||||
|
add_c_flag_if_supported(-fPIC C_SECURITY_FLAGS)
|
||||||
|
add_cxx_flag_if_supported(-fPIC CXX_SECURITY_FLAGS)
|
||||||
|
|
||||||
|
message(STATUS "Using C security hardening flags: ${C_SECURITY_FLAGS}")
|
||||||
|
message(STATUS "Using C++ security hardening flags: ${CXX_SECURITY_FLAGS}")
|
||||||
|
message(STATUS "Using linker security hardening flags: ${LD_SECURITY_FLAGS}")
|
||||||
|
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11 ${C_SECURITY_FLAGS}")
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 ${CXX_SECURITY_FLAGS}")
|
||||||
|
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${LD_SECURITY_FLAGS} ${STATIC_FLAGS}")
|
||||||
|
|
||||||
|
add_subdirectory(src)
|
@ -0,0 +1,14 @@
|
|||||||
|
#ifdef __CLASSIC_C__
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
int ac;
|
||||||
|
char* av[];
|
||||||
|
#else
|
||||||
|
int main(int ac, char* av[])
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
if (ac > 1000) {
|
||||||
|
return *av[0];
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
include(CheckCCompilerFlag)
|
||||||
|
|
||||||
|
macro(CHECK_LINKER_FLAG flag VARIABLE)
|
||||||
|
if(NOT DEFINED "${VARIABLE}")
|
||||||
|
if(NOT CMAKE_REQUIRED_QUIET)
|
||||||
|
message(STATUS "Looking for ${flag} linker flag")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(_cle_source ${CMAKE_SOURCE_DIR}/cmake/CheckLinkerFlag.c)
|
||||||
|
|
||||||
|
set(saved_CMAKE_C_FLAGS ${CMAKE_C_FLAGS})
|
||||||
|
set(CMAKE_C_FLAGS "${flag}")
|
||||||
|
try_compile(${VARIABLE}
|
||||||
|
${CMAKE_BINARY_DIR}
|
||||||
|
${_cle_source}
|
||||||
|
COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} ${flag}
|
||||||
|
CMAKE_FLAGS
|
||||||
|
OUTPUT_VARIABLE OUTPUT)
|
||||||
|
unset(_cle_source)
|
||||||
|
set(CMAKE_C_FLAGS ${saved_CMAKE_C_FLAGS})
|
||||||
|
unset(saved_CMAKE_C_FLAGS)
|
||||||
|
|
||||||
|
if ("${OUTPUT}" MATCHES "warning.*ignored")
|
||||||
|
set(${VARIABLE} 0)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(${VARIABLE})
|
||||||
|
if(NOT CMAKE_REQUIRED_QUIET)
|
||||||
|
message(STATUS "Looking for ${flag} linker flag - found")
|
||||||
|
endif()
|
||||||
|
set(${VARIABLE} 1 CACHE INTERNAL "Have linker flag ${flag}")
|
||||||
|
file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log
|
||||||
|
"Determining if the ${flag} linker flag is supported "
|
||||||
|
"passed with the following output:\n"
|
||||||
|
"${OUTPUT}\n\n")
|
||||||
|
else()
|
||||||
|
if(NOT CMAKE_REQUIRED_QUIET)
|
||||||
|
message(STATUS "Looking for ${flag} linker flag - not found")
|
||||||
|
endif()
|
||||||
|
set(${VARIABLE} "" CACHE INTERNAL "Have linker flag ${flag}")
|
||||||
|
file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log
|
||||||
|
"Determining if the ${flag} linker flag is supported "
|
||||||
|
"failed with the following output:\n"
|
||||||
|
"${OUTPUT}\n\n")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endmacro()
|
@ -0,0 +1,56 @@
|
|||||||
|
# Copyright (c) 2014-2020, The Monero Project
|
||||||
|
#
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# Redistribution and use in source and binary forms, with or without modification, are
|
||||||
|
# permitted provided that the following conditions are met:
|
||||||
|
#
|
||||||
|
# 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||||
|
# conditions and the following disclaimer.
|
||||||
|
#
|
||||||
|
# 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||||
|
# of conditions and the following disclaimer in the documentation and/or other
|
||||||
|
# materials provided with the distribution.
|
||||||
|
#
|
||||||
|
# 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||||
|
# used to endorse or promote products derived from this software without specific
|
||||||
|
# prior written permission.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||||
|
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||||
|
# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||||
|
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||||
|
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
# - Try to find readline include dirs and libraries
|
||||||
|
#
|
||||||
|
# Automatically finds ccache build accelerator, if it's found in PATH.
|
||||||
|
#
|
||||||
|
# Usage of this module as follows:
|
||||||
|
#
|
||||||
|
# project(monero)
|
||||||
|
# include(FindCcache) # Include AFTER the project() macro to be able to reach the CMAKE_CXX_COMPILER variable
|
||||||
|
#
|
||||||
|
# Properties modified by this module:
|
||||||
|
#
|
||||||
|
# GLOBAL PROPERTY RULE_LAUNCH_COMPILE set to ccache, when ccache found
|
||||||
|
# GLOBAL PROPERTY RULE_LAUNCH_LINK set to ccache, when ccache found
|
||||||
|
|
||||||
|
find_program(CCACHE_FOUND ccache)
|
||||||
|
if (CCACHE_FOUND)
|
||||||
|
set(TEMP_CPP_FILE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/test-program.cpp")
|
||||||
|
file(WRITE "${TEMP_CPP_FILE}" "int main() { return 0; }")
|
||||||
|
execute_process(COMMAND "${CCACHE_FOUND}" "${CMAKE_CXX_COMPILER}" "${TEMP_CPP_FILE}" RESULT_VARIABLE RET)
|
||||||
|
if (${RET} EQUAL 0)
|
||||||
|
message("found usable ccache: ${CCACHE_FOUND}")
|
||||||
|
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_FOUND}")
|
||||||
|
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK "${CCACHE_FOUND}")
|
||||||
|
else()
|
||||||
|
message("found ccache ${CCACHE_FOUND}, but is UNUSABLE! Return code: ${RET}")
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
message("ccache NOT found!")
|
||||||
|
endif()
|
@ -0,0 +1,68 @@
|
|||||||
|
set(CMAKE_AUTOMOC ON)
|
||||||
|
set(CMAKE_AUTORCC ON)
|
||||||
|
set(CMAKE_AUTOUIC ON)
|
||||||
|
|
||||||
|
# pthread
|
||||||
|
find_package(Threads REQUIRED)
|
||||||
|
find_package(Qt5 REQUIRED COMPONENTS Core Widgets Gui)
|
||||||
|
|
||||||
|
qt5_add_resources(RESOURCES assets.qrc)
|
||||||
|
|
||||||
|
# Compile source files (.h/.cpp)
|
||||||
|
file(GLOB SOURCE_FILES
|
||||||
|
"*.h"
|
||||||
|
"*.cpp"
|
||||||
|
"ui/BreezeStyleSheets/breeze.qrc"
|
||||||
|
"ui/qdarkstyle/style.qrc"
|
||||||
|
)
|
||||||
|
|
||||||
|
set(EXECUTABLE_FLAG)
|
||||||
|
|
||||||
|
add_executable(moneroservice ${EXECUTABLE_FLAG} main.cpp
|
||||||
|
${SOURCE_FILES}
|
||||||
|
${RESOURCES}
|
||||||
|
)
|
||||||
|
|
||||||
|
set_property(TARGET moneroservice PROPERTY RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
|
||||||
|
|
||||||
|
target_include_directories(moneroservice PUBLIC ${Qt5Gui_PRIVATE_INCLUDE_DIRS})
|
||||||
|
|
||||||
|
file(GLOB_RECURSE SRC_SOURCES *.cpp)
|
||||||
|
file(GLOB_RECURSE SRC_HEADERS *.h)
|
||||||
|
|
||||||
|
target_include_directories(moneroservice PUBLIC
|
||||||
|
${CMAKE_BINARY_DIR}/src/moneroservice_autogen/include
|
||||||
|
${CMAKE_SOURCE_DIR}/src
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
${X11_INCLUDE_DIR}
|
||||||
|
${Qt5Core_INCLUDE_DIRS}
|
||||||
|
${Qt5Widgets_INCLUDE_DIRS}
|
||||||
|
${Qt5Gui_INCLUDE_DIRS}
|
||||||
|
)
|
||||||
|
|
||||||
|
target_compile_definitions(moneroservice
|
||||||
|
PUBLIC
|
||||||
|
${Qt5Core_DEFINITIONS}
|
||||||
|
${Qt5Widgets_DEFINITIONS}
|
||||||
|
${Qt5Gui_DEFINITIONS}
|
||||||
|
)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}")
|
||||||
|
|
||||||
|
target_link_libraries(moneroservice
|
||||||
|
${CMAKE_DL_LIBS}
|
||||||
|
${EXTRA_LIBRARIES}
|
||||||
|
Qt5::Core
|
||||||
|
Qt5::Widgets
|
||||||
|
Qt5::Gui
|
||||||
|
${ICU_LIBRARIES}
|
||||||
|
Threads::Threads
|
||||||
|
)
|
||||||
|
|
||||||
|
if(X11_FOUND)
|
||||||
|
target_link_libraries(moneroservice ${X11_LIBRARIES})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
install(TARGETS moneroservice
|
||||||
|
DESTINATION ${CMAKE_INSTALL_PREFIX}
|
||||||
|
)
|
@ -0,0 +1,61 @@
|
|||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
// Copyright (c) 2021, The Monero Project.
|
||||||
|
|
||||||
|
#include "CommandLineEdit.h"
|
||||||
|
|
||||||
|
#include <QKeyEvent>
|
||||||
|
|
||||||
|
CommandLineEdit::CommandLineEdit(QWidget *parent) : QLineEdit(parent)
|
||||||
|
{
|
||||||
|
connect(this, &QLineEdit::returnPressed, this, &CommandLineEdit::updateHistory);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandLineEdit::keyPressEvent(QKeyEvent *event) {
|
||||||
|
if (event->key() == Qt::Key_Up) {
|
||||||
|
if (m_historyIndex == m_history.size()) {
|
||||||
|
m_currentLine = this->text();
|
||||||
|
}
|
||||||
|
if (this->decreaseIndex()) {
|
||||||
|
this->setText(this->getCommand(m_historyIndex));
|
||||||
|
}
|
||||||
|
} else if (event->key() == Qt::Key_Down) {
|
||||||
|
if (this->increaseIndex()) {
|
||||||
|
this->setText(this->getCommand(m_historyIndex));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QLineEdit::keyPressEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CommandLineEdit::decreaseIndex() {
|
||||||
|
if (m_historyIndex > 0 && m_history.size() >= m_historyIndex) {
|
||||||
|
m_historyIndex -= 1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CommandLineEdit::increaseIndex() {
|
||||||
|
if (m_historyIndex < m_history.size()) {
|
||||||
|
m_historyIndex += 1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString CommandLineEdit::getCommand(int index) {
|
||||||
|
if (index < m_history.size()) {
|
||||||
|
return m_history[index];
|
||||||
|
} else {
|
||||||
|
return m_currentLine;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandLineEdit::updateHistory() {
|
||||||
|
QString command = this->text();
|
||||||
|
if (m_history.isEmpty() || m_history.last() != command) {
|
||||||
|
m_history << command;
|
||||||
|
m_historyIndex = m_history.size();
|
||||||
|
}
|
||||||
|
this->setText("");
|
||||||
|
emit commandExecuted(command.split(" "));
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
// Copyright (c) 2021, The Monero Project.
|
||||||
|
|
||||||
|
#ifndef MONEROSERVICE_COMMANDLINEEDIT_H
|
||||||
|
#define MONEROSERVICE_COMMANDLINEEDIT_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QLineEdit>
|
||||||
|
|
||||||
|
class CommandLineEdit : public QLineEdit {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit CommandLineEdit(QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
void keyPressEvent(QKeyEvent *event) override;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void commandExecuted(const QStringList &command);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void updateHistory();
|
||||||
|
bool decreaseIndex();
|
||||||
|
bool increaseIndex();
|
||||||
|
QString getCommand(int index);
|
||||||
|
|
||||||
|
QString m_currentLine;
|
||||||
|
int m_historyIndex = 1;
|
||||||
|
QStringList m_history;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //MONEROSERVICE_COMMANDLINEEDIT_H
|
@ -0,0 +1,118 @@
|
|||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
// Copyright (c) 2020-2021, The Monero Project.
|
||||||
|
// Copyright (c) 2020 KeePassXC Team <team@keepassxc.org>
|
||||||
|
// Copyright (c) 2011 Felix Geyer <debfx@fobos.de>
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
#include <QDir>
|
||||||
|
#include <QCoreApplication>
|
||||||
|
|
||||||
|
#define QS QStringLiteral
|
||||||
|
|
||||||
|
struct ConfigDirective
|
||||||
|
{
|
||||||
|
QString name;
|
||||||
|
QVariant defaultValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const QHash<Config::ConfigKey, ConfigDirective> configStrings = {
|
||||||
|
{Config::network,{QS("network"), "mainnet"}},
|
||||||
|
{Config::pathToMonerod,{QS("pathToMonerod"), ""}},
|
||||||
|
{Config::pruneBlockchain,{QS("pruneBlockchain"), true}},
|
||||||
|
{Config::dataDir,{QS("dataDir"), ""}},
|
||||||
|
{Config::logLevel,{QS("logLevel"), 0}},
|
||||||
|
{Config::limitRateDown,{QS("limitRateDown"), 0}},
|
||||||
|
{Config::customStartupFlags,{QS("customStartupFlags"), ""}},
|
||||||
|
{Config::noZeroMQ,{QS("noZeroMQ"), true}},
|
||||||
|
{Config::RPCBindIP,{QS("RPCBindIP"), ""}},
|
||||||
|
{Config::RPCBindPort,{QS("RPCBindPort"), 0}},
|
||||||
|
{Config::P2PBindIP,{QS("P2PBindIP"), ""}},
|
||||||
|
{Config::P2PBindPort,{QS("P2PBindPort"), 0}},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
QPointer<Config> Config::m_instance(nullptr);
|
||||||
|
|
||||||
|
QVariant Config::get(ConfigKey key)
|
||||||
|
{
|
||||||
|
auto cfg = configStrings[key];
|
||||||
|
auto defaultValue = configStrings[key].defaultValue;
|
||||||
|
|
||||||
|
return m_settings->value(cfg.name, defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Config::getFileName()
|
||||||
|
{
|
||||||
|
return m_settings->fileName();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::set(ConfigKey key, const QVariant& value)
|
||||||
|
{
|
||||||
|
if (get(key) == value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto cfg = configStrings[key];
|
||||||
|
m_settings->setValue(cfg.name, value);
|
||||||
|
|
||||||
|
this->sync();
|
||||||
|
emit changed(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sync configuration with persistent storage.
|
||||||
|
*
|
||||||
|
* Usually, you don't need to call this method manually, but if you are writing
|
||||||
|
* configurations after an emitted \link QCoreApplication::aboutToQuit() signal,
|
||||||
|
* use it to guarantee your config values are persisted.
|
||||||
|
*/
|
||||||
|
void Config::sync()
|
||||||
|
{
|
||||||
|
m_settings->sync();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::resetToDefaults()
|
||||||
|
{
|
||||||
|
m_settings->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
Config::Config(const QString& fileName, QObject* parent)
|
||||||
|
: QObject(parent)
|
||||||
|
{
|
||||||
|
init(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
Config::Config(QObject* parent)
|
||||||
|
: QObject(parent)
|
||||||
|
{
|
||||||
|
QString configPath;
|
||||||
|
|
||||||
|
configPath = QDir::homePath();
|
||||||
|
configPath += "/.config/moneroservice/settings.json";
|
||||||
|
|
||||||
|
init(QDir::toNativeSeparators(configPath));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Config::~Config()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::init(const QString& configFileName)
|
||||||
|
{
|
||||||
|
// const QSettings::Format jsonFormat = QSettings::registerFormat("json", Utils::readJsonFile, Utils::writeJsonFile);
|
||||||
|
// QSettings::setDefaultFormat(jsonFormat);
|
||||||
|
m_settings.reset(new QSettings());
|
||||||
|
|
||||||
|
connect(qApp, &QCoreApplication::aboutToQuit, this, &Config::sync);
|
||||||
|
}
|
||||||
|
|
||||||
|
Config* Config::instance()
|
||||||
|
{
|
||||||
|
if (!m_instance) {
|
||||||
|
m_instance = new Config(qApp);
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_instance;
|
||||||
|
}
|
@ -0,0 +1,64 @@
|
|||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
// Copyright (c) 2020-2021, The Monero Project.
|
||||||
|
// Copyright (c) 2020 KeePassXC Team <team@keepassxc.org>
|
||||||
|
// Copyright (c) 2011 Felix Geyer <debfx@fobos.de>
|
||||||
|
|
||||||
|
#ifndef MONEROSERVICE_SETTINGS_H
|
||||||
|
#define MONEROSERVICE_SETTINGS_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QPointer>
|
||||||
|
|
||||||
|
class Config : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
Q_DISABLE_COPY(Config)
|
||||||
|
|
||||||
|
enum ConfigKey
|
||||||
|
{
|
||||||
|
network,
|
||||||
|
pathToMonerod,
|
||||||
|
pruneBlockchain,
|
||||||
|
dataDir,
|
||||||
|
logLevel,
|
||||||
|
limitRateDown,
|
||||||
|
customStartupFlags,
|
||||||
|
noZeroMQ,
|
||||||
|
RPCBindIP,
|
||||||
|
RPCBindPort,
|
||||||
|
P2PBindIP,
|
||||||
|
P2PBindPort
|
||||||
|
};
|
||||||
|
|
||||||
|
~Config() override;
|
||||||
|
QVariant get(ConfigKey key);
|
||||||
|
QString getFileName();
|
||||||
|
void set(ConfigKey key, const QVariant& value);
|
||||||
|
void sync();
|
||||||
|
void resetToDefaults();
|
||||||
|
|
||||||
|
static Config* instance();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void changed(ConfigKey key);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Config(const QString& fileName, QObject* parent = nullptr);
|
||||||
|
explicit Config(QObject* parent);
|
||||||
|
void init(const QString& configFileName);
|
||||||
|
|
||||||
|
static QPointer<Config> m_instance;
|
||||||
|
|
||||||
|
QScopedPointer<QSettings> m_settings;
|
||||||
|
QHash<QString, QVariant> m_defaults;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline Config* config()
|
||||||
|
{
|
||||||
|
return Config::instance();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //MONEROSERVICE_SETTINGS_H
|
@ -0,0 +1,86 @@
|
|||||||
|
#include "Configurator.h"
|
||||||
|
#include "ui_Configurator.h"
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
#include <QIcon>
|
||||||
|
|
||||||
|
Configurator::Configurator(QWidget *parent) :
|
||||||
|
QDialog(parent),
|
||||||
|
ui(new Ui::Configurator)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
// Basic settings
|
||||||
|
ui->lineMonerod->setText(config()->get(Config::pathToMonerod).toString());
|
||||||
|
ui->lineMonerod->setCursorPosition(0);
|
||||||
|
connect(ui->lineMonerod, &QLineEdit::textChanged, [this](const QString &text){
|
||||||
|
this->setConfig(Config::pathToMonerod, text);
|
||||||
|
});
|
||||||
|
|
||||||
|
ui->checkPruneBlockchain->setChecked(config()->get(Config::pruneBlockchain).toBool());
|
||||||
|
connect(ui->checkPruneBlockchain, &QCheckBox::toggled, [this](bool toggled){
|
||||||
|
this->setConfig(Config::pruneBlockchain, toggled);
|
||||||
|
});
|
||||||
|
|
||||||
|
ui->lineDataDir->setText(config()->get(Config::dataDir).toString());
|
||||||
|
connect(ui->lineDataDir, &QLineEdit::textChanged, [this](const QString &text){
|
||||||
|
this->setConfig(Config::dataDir, text);
|
||||||
|
});
|
||||||
|
|
||||||
|
ui->spinLimitSpeedDown->setValue(config()->get(Config::limitRateDown).toInt());
|
||||||
|
connect(ui->spinLimitSpeedDown, QOverload<int>::of(&QSpinBox::valueChanged), [this](int value){
|
||||||
|
this->setConfig(Config::limitRateDown, value);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Advanced settings
|
||||||
|
ui->comboNetwork->setCurrentIndex(config()->get(Config::network).toInt());
|
||||||
|
connect(ui->comboNetwork, QOverload<int>::of(&QComboBox::currentIndexChanged), [this](int value){
|
||||||
|
this->setConfig(Config::network, value);
|
||||||
|
});
|
||||||
|
|
||||||
|
ui->comboLogLevel->setCurrentIndex(config()->get(Config::logLevel).toInt());
|
||||||
|
connect(ui->comboLogLevel, QOverload<int>::of(&QComboBox::currentIndexChanged), [this](int value){
|
||||||
|
this->setConfig(Config::logLevel, value);
|
||||||
|
});
|
||||||
|
|
||||||
|
ui->checkNoZeroMQ->setChecked(config()->get(Config::noZeroMQ).toBool());
|
||||||
|
connect(ui->checkNoZeroMQ, &QCheckBox::toggled, [this](bool toggled){
|
||||||
|
this->setConfig(Config::noZeroMQ, toggled);
|
||||||
|
});
|
||||||
|
|
||||||
|
ui->lineRPCBindIP->setText(config()->get(Config::RPCBindIP).toString());
|
||||||
|
connect(ui->lineRPCBindIP, &QLineEdit::textChanged, [this](const QString &text){
|
||||||
|
this->setConfig(Config::RPCBindIP, text);
|
||||||
|
});
|
||||||
|
|
||||||
|
ui->lineRPCBindPort->setText(config()->get(Config::RPCBindPort).toString());
|
||||||
|
connect(ui->lineP2PBindPort, &QLineEdit::textChanged, [this](const QString &text){
|
||||||
|
this->setConfig(Config::P2PBindPort, text);
|
||||||
|
});
|
||||||
|
|
||||||
|
ui->lineP2PBindIP->setText(config()->get(Config::P2PBindIP).toString());
|
||||||
|
connect(ui->lineP2PBindIP, &QLineEdit::textChanged, [this](const QString &text){
|
||||||
|
this->setConfig(Config::P2PBindIP, text);
|
||||||
|
});
|
||||||
|
|
||||||
|
ui->lineP2PBindPort->setText(config()->get(Config::P2PBindPort).toString());
|
||||||
|
connect(ui->lineP2PBindPort, &QLineEdit::textChanged, [this](const QString &text){
|
||||||
|
this->setConfig(Config::P2PBindPort, text);
|
||||||
|
});
|
||||||
|
|
||||||
|
this->adjustSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Configurator::configUpdated() const {
|
||||||
|
return m_configUpdated;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Configurator::setConfig(Config::ConfigKey configKey, const QVariant &value) {
|
||||||
|
this->m_configUpdated = true;
|
||||||
|
config()->set(configKey, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
Configurator::~Configurator()
|
||||||
|
{
|
||||||
|
delete ui;
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
#ifndef MONEROSERVICE_CONFIGURATOR_H
|
||||||
|
#define MONEROSERVICE_CONFIGURATOR_H
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class Configurator;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Configurator : public QDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit Configurator(QWidget *parent = nullptr);
|
||||||
|
~Configurator() override;
|
||||||
|
bool configUpdated() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void setConfig(Config::ConfigKey configKey, const QVariant &value);
|
||||||
|
Ui::Configurator *ui;
|
||||||
|
|
||||||
|
bool m_configUpdated = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //MONEROSERVICE_CONFIGURATOR_H
|
@ -0,0 +1,345 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>Configurator</class>
|
||||||
|
<widget class="QDialog" name="Configurator">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>692</width>
|
||||||
|
<height>585</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>500</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Config</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QTabWidget" name="tabWidget">
|
||||||
|
<property name="currentIndex">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="tab">
|
||||||
|
<attribute name="title">
|
||||||
|
<string>Basic</string>
|
||||||
|
</attribute>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="groupBox">
|
||||||
|
<property name="title">
|
||||||
|
<string>Path to monerod:</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="lineMonerod"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btn_BrowseMonerodPath">
|
||||||
|
<property name="text">
|
||||||
|
<string>Browse</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="groupBox_6">
|
||||||
|
<property name="title">
|
||||||
|
<string>Blockchain</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="checkPruneBlockchain">
|
||||||
|
<property name="text">
|
||||||
|
<string>Prune blockchain</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>Data directory:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="lineDataDir">
|
||||||
|
<property name="placeholderText">
|
||||||
|
<string>Leave blank for default</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btn_BrowseDataDir">
|
||||||
|
<property name="text">
|
||||||
|
<string>Browse</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="groupBox_7">
|
||||||
|
<property name="title">
|
||||||
|
<string>Network</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Limit download speed:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QSpinBox" name="spinLimitSpeedDown">
|
||||||
|
<property name="maximum">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QWidget" name="tab_2">
|
||||||
|
<attribute name="title">
|
||||||
|
<string>Advanced</string>
|
||||||
|
</attribute>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||||
|
<item>
|
||||||
|
<layout class="QFormLayout" name="formLayout_2">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="label_4">
|
||||||
|
<property name="text">
|
||||||
|
<string>Network</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QComboBox" name="comboNetwork">
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Mainnet</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Stagenet</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Testnet</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label_5">
|
||||||
|
<property name="text">
|
||||||
|
<string>Log level</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QComboBox" name="comboLogLevel">
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Default</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Info</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Debug</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="label_6">
|
||||||
|
<property name="text">
|
||||||
|
<string>RPC port</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="0">
|
||||||
|
<widget class="QLabel" name="label_7">
|
||||||
|
<property name="text">
|
||||||
|
<string>P2P port</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="label_8">
|
||||||
|
<property name="text">
|
||||||
|
<string>RPC bind IP</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QLineEdit" name="lineRPCBindIP">
|
||||||
|
<property name="placeholderText">
|
||||||
|
<string>127.0.0.1</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0">
|
||||||
|
<widget class="QLabel" name="label_9">
|
||||||
|
<property name="text">
|
||||||
|
<string>P2P bind IP</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="1">
|
||||||
|
<widget class="QLineEdit" name="lineP2PBindIP">
|
||||||
|
<property name="placeholderText">
|
||||||
|
<string>127.0.0.1</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QLineEdit" name="lineRPCBindPort">
|
||||||
|
<property name="placeholderText">
|
||||||
|
<string>18081</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="1">
|
||||||
|
<widget class="QLineEdit" name="lineP2PBindPort">
|
||||||
|
<property name="placeholderText">
|
||||||
|
<string>18082</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="checkNoZeroMQ">
|
||||||
|
<property name="text">
|
||||||
|
<string>No ZeroMQ</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="groupBox_3">
|
||||||
|
<property name="title">
|
||||||
|
<string>Startup flags</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="lineStartupFlags"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>81</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Ok</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>accepted()</signal>
|
||||||
|
<receiver>Configurator</receiver>
|
||||||
|
<slot>accept()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>248</x>
|
||||||
|
<y>254</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>157</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>rejected()</signal>
|
||||||
|
<receiver>Configurator</receiver>
|
||||||
|
<slot>reject()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>316</x>
|
||||||
|
<y>260</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>286</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
@ -0,0 +1,240 @@
|
|||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
// Copyright (c) 2014-2021, The Monero Project.
|
||||||
|
|
||||||
|
#include "DaemonManager.h"
|
||||||
|
#include <QElapsedTimer>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QMutexLocker>
|
||||||
|
#include <QThread>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QUrl>
|
||||||
|
#include <QtConcurrent/QtConcurrent>
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QProcess>
|
||||||
|
#include <QStorageInfo>
|
||||||
|
#include <QVariantMap>
|
||||||
|
#include <QVariant>
|
||||||
|
#include <QMap>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
bool DaemonManager::start(const QStringList &flags, int port)
|
||||||
|
{
|
||||||
|
if (!QFileInfo(m_monerod).isFile())
|
||||||
|
{
|
||||||
|
emit daemonStartFailure("\"" + QDir::toNativeSeparators(m_monerod) + "\" " + tr("executable is missing"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_port = port;
|
||||||
|
|
||||||
|
// prepare command line arguments and pass to monerod
|
||||||
|
QStringList arguments = flags;
|
||||||
|
|
||||||
|
arguments << "--check-updates" << "disabled";
|
||||||
|
|
||||||
|
qDebug() << "starting monerod " + m_monerod;
|
||||||
|
qDebug() << "With command line arguments " << arguments;
|
||||||
|
|
||||||
|
QMutexLocker locker(&m_daemonMutex);
|
||||||
|
|
||||||
|
m_daemon.reset(new QProcess());
|
||||||
|
|
||||||
|
// Connect output slots
|
||||||
|
connect(m_daemon.get(), &QProcess::readyReadStandardOutput, this, &DaemonManager::printOutput);
|
||||||
|
connect(m_daemon.get(), &QProcess::readyReadStandardError, this, &DaemonManager::printError);
|
||||||
|
|
||||||
|
// add state changed listener
|
||||||
|
connect(m_daemon.get(), &QProcess::stateChanged, this, &DaemonManager::stateChanged);
|
||||||
|
|
||||||
|
// Start monerod
|
||||||
|
m_daemon->start(m_monerod, arguments);
|
||||||
|
|
||||||
|
// Start start watcher
|
||||||
|
m_scheduler.run([this] {
|
||||||
|
if (startWatcher()) {
|
||||||
|
emit daemonStarted();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DaemonManager::stop()
|
||||||
|
{
|
||||||
|
m_daemon->terminate();
|
||||||
|
m_scheduler.run([this]{
|
||||||
|
stopWatcher();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DaemonManager::isRunning() const {
|
||||||
|
return m_running;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DaemonManager::startWatcher()
|
||||||
|
{
|
||||||
|
// Check if daemon is started every 2 seconds
|
||||||
|
QElapsedTimer timer;
|
||||||
|
timer.start();
|
||||||
|
while(!m_app_exit && m_running) {
|
||||||
|
QThread::sleep(2);
|
||||||
|
if(!started()) {
|
||||||
|
qDebug() << "daemon not running. checking again in 2 seconds.";
|
||||||
|
} else {
|
||||||
|
qDebug() << "daemon is started. Waiting 5 seconds to let daemon catch up";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DaemonManager::stopWatcher()
|
||||||
|
{
|
||||||
|
// Check if daemon is running every 2 seconds. Kill if still running after 10 seconds
|
||||||
|
int counter = 0;
|
||||||
|
while(!m_app_exit && !m_running) {
|
||||||
|
QThread::sleep(2);
|
||||||
|
counter++;
|
||||||
|
if(!started()) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
qDebug() << "Daemon still running. " << counter;
|
||||||
|
if(counter >= 5) {
|
||||||
|
m_daemon->kill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DaemonManager::stateChanged(QProcess::ProcessState state)
|
||||||
|
{
|
||||||
|
qDebug() << "STATE CHANGED: " << state;
|
||||||
|
m_running = (state != QProcess::NotRunning);
|
||||||
|
if (state == QProcess::NotRunning) {
|
||||||
|
emit daemonStopped();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DaemonManager::printOutput()
|
||||||
|
{
|
||||||
|
QByteArray byteArray = [this]() {
|
||||||
|
QMutexLocker locker(&m_daemonMutex);
|
||||||
|
return m_daemon->readAllStandardOutput();
|
||||||
|
}();
|
||||||
|
QStringList strLines = QString(byteArray).split("\n");
|
||||||
|
|
||||||
|
for (const auto& line: strLines) {
|
||||||
|
emit daemonConsoleUpdated(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DaemonManager::printError()
|
||||||
|
{
|
||||||
|
QByteArray byteArray = [this]() {
|
||||||
|
QMutexLocker locker(&m_daemonMutex);
|
||||||
|
return m_daemon->readAllStandardError();
|
||||||
|
}();
|
||||||
|
QStringList strLines = QString(byteArray).split("\n");
|
||||||
|
|
||||||
|
for (const auto& line: strLines) {
|
||||||
|
emit daemonConsoleUpdated(line);
|
||||||
|
qDebug() << "Daemon ERROR: " + line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DaemonManager::started()
|
||||||
|
{
|
||||||
|
QString status;
|
||||||
|
sendCommand({"sync_info"}, status);
|
||||||
|
qDebug() << status;
|
||||||
|
m_started = status.contains("Height:");
|
||||||
|
return m_started;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DaemonManager::sendCommand(const QStringList &cmd, QString &message) const
|
||||||
|
{
|
||||||
|
QProcess p;
|
||||||
|
QStringList external_cmd(cmd);
|
||||||
|
|
||||||
|
external_cmd << "--rpc-bind-port" << QString::number(m_port);
|
||||||
|
|
||||||
|
qDebug() << "sending external cmd: " << external_cmd;
|
||||||
|
|
||||||
|
p.start(m_monerod, external_cmd);
|
||||||
|
|
||||||
|
bool started = p.waitForFinished(-1);
|
||||||
|
message = p.readAllStandardOutput();
|
||||||
|
emit daemonConsoleUpdated(message);
|
||||||
|
return started;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DaemonManager::sendCommandAsync(const QStringList &cmd, std::function<void(QVariantList)> callback) const
|
||||||
|
{
|
||||||
|
m_scheduler.run([this, cmd] {
|
||||||
|
QString message;
|
||||||
|
return QVariantList({sendCommand(cmd, message), message});
|
||||||
|
}, std::move(callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
void DaemonManager::exit()
|
||||||
|
{
|
||||||
|
qDebug("DaemonManager: exit()");
|
||||||
|
m_app_exit = true;
|
||||||
|
}
|
||||||
|
//
|
||||||
|
//QVariantMap DaemonManager::validateDataDir(const QString &dataDir) const
|
||||||
|
//{
|
||||||
|
// QVariantMap result;
|
||||||
|
// bool valid = true;
|
||||||
|
// bool readOnly = false;
|
||||||
|
// int storageAvailable = 0;
|
||||||
|
// bool lmdbExists = true;
|
||||||
|
//
|
||||||
|
// QStorageInfo storage(dataDir);
|
||||||
|
// if (storage.isValid() && storage.isReady()) {
|
||||||
|
// if (storage.isReadOnly()) {
|
||||||
|
// readOnly = true;
|
||||||
|
// valid = false;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Make sure there is 75GB storage available
|
||||||
|
// storageAvailable = storage.bytesAvailable()/1000/1000/1000;
|
||||||
|
// if (storageAvailable < 75) {
|
||||||
|
// valid = false;
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// valid = false;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// if (!QDir(dataDir+"/lmdb").exists()) {
|
||||||
|
// lmdbExists = false;
|
||||||
|
// valid = false;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// result.insert("valid", valid);
|
||||||
|
// result.insert("lmdbExists", lmdbExists);
|
||||||
|
// result.insert("readOnly", readOnly);
|
||||||
|
// result.insert("storageAvailable", storageAvailable);
|
||||||
|
//
|
||||||
|
// return result;
|
||||||
|
//}
|
||||||
|
|
||||||
|
void DaemonManager::setMonerodPath(const QString &path) {
|
||||||
|
m_monerod = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
DaemonManager::DaemonManager(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
, m_scheduler(this)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
DaemonManager::~DaemonManager()
|
||||||
|
{
|
||||||
|
m_scheduler.shutdownWaitForFinished();
|
||||||
|
}
|
@ -0,0 +1,64 @@
|
|||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
// Copyright (c) 2014-2021, The Monero Project.
|
||||||
|
|
||||||
|
#ifndef DAEMONMANAGER_H
|
||||||
|
#define DAEMONMANAGER_H
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <QMutex>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QUrl>
|
||||||
|
#include <QProcess>
|
||||||
|
#include <QVariantMap>
|
||||||
|
#include "FutureScheduler.h"
|
||||||
|
|
||||||
|
class DaemonManager : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit DaemonManager(QObject *parent = nullptr);
|
||||||
|
~DaemonManager() override;
|
||||||
|
|
||||||
|
bool start(const QStringList &flags, int port);
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
void sendCommandAsync(const QStringList &cmd, std::function<void(QVariantList)> callback) const;
|
||||||
|
void exit();
|
||||||
|
// QVariantMap validateDataDir(const QString &dataDir) const;
|
||||||
|
|
||||||
|
void setMonerodPath(const QString &path);
|
||||||
|
bool isRunning() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool started();
|
||||||
|
bool sendCommand(const QStringList &cmd, QString &message) const;
|
||||||
|
bool startWatcher();
|
||||||
|
bool stopWatcher();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void daemonStarted() const;
|
||||||
|
void daemonStopped() const;
|
||||||
|
void daemonStartFailure(const QString &error) const;
|
||||||
|
void daemonConsoleUpdated(QString message) const;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void printOutput();
|
||||||
|
void printError();
|
||||||
|
void stateChanged(QProcess::ProcessState state);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<QProcess> m_daemon;
|
||||||
|
QMutex m_daemonMutex;
|
||||||
|
QString m_monerod;
|
||||||
|
|
||||||
|
bool m_running = false;
|
||||||
|
bool m_started = false;
|
||||||
|
bool m_app_exit = false;
|
||||||
|
int m_port;
|
||||||
|
|
||||||
|
mutable FutureScheduler m_scheduler;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // DAEMONMANAGER_H
|
@ -0,0 +1,96 @@
|
|||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
// Copyright (c) 2014-2021, The Monero Project.
|
||||||
|
|
||||||
|
#include "FutureScheduler.h"
|
||||||
|
|
||||||
|
FutureScheduler::FutureScheduler(QObject *parent)
|
||||||
|
: QObject(parent), Alive(0), Stopping(false)
|
||||||
|
{
|
||||||
|
static std::once_flag once;
|
||||||
|
std::call_once(once, []() {
|
||||||
|
QThreadPool::globalInstance()->setMaxThreadCount(4);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
FutureScheduler::~FutureScheduler()
|
||||||
|
{
|
||||||
|
shutdownWaitForFinished();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FutureScheduler::shutdownWaitForFinished() noexcept
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&Mutex);
|
||||||
|
|
||||||
|
Stopping = true;
|
||||||
|
while (Alive > 0)
|
||||||
|
{
|
||||||
|
Condition.wait(&Mutex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QPair<bool, QFuture<void>> FutureScheduler::run(std::function<void()> function) noexcept
|
||||||
|
{
|
||||||
|
return execute<void>([this, function](QFutureWatcher<void> *) {
|
||||||
|
return QtConcurrent::run([this, function] {
|
||||||
|
try
|
||||||
|
{
|
||||||
|
function();
|
||||||
|
}
|
||||||
|
catch (const std::exception &exception)
|
||||||
|
{
|
||||||
|
qWarning() << "Exception thrown from async function: " << exception.what();
|
||||||
|
}
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QPair<bool, QFuture<QVariantList>> FutureScheduler::run(std::function<QVariantList()> function, const std::function<void(QVariantList)> callback)
|
||||||
|
{
|
||||||
|
return execute<QVariantList>([this, function, callback](QFutureWatcher<QVariantList> *watcher) {
|
||||||
|
connect(watcher, &QFutureWatcher<QVariantList>::finished, [watcher, callback] {
|
||||||
|
callback(watcher->future().result());
|
||||||
|
});
|
||||||
|
return QtConcurrent::run([this, function] {
|
||||||
|
QVariantList result;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
result = function();
|
||||||
|
}
|
||||||
|
catch (const std::exception &exception)
|
||||||
|
{
|
||||||
|
qWarning() << "Exception thrown from async function: " << exception.what();
|
||||||
|
}
|
||||||
|
done();
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FutureScheduler::stopping() const noexcept
|
||||||
|
{
|
||||||
|
return Stopping;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FutureScheduler::add() noexcept
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&Mutex);
|
||||||
|
|
||||||
|
if (Stopping)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
++Alive;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FutureScheduler::done() noexcept
|
||||||
|
{
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&Mutex);
|
||||||
|
--Alive;
|
||||||
|
}
|
||||||
|
|
||||||
|
Condition.wakeAll();
|
||||||
|
}
|
@ -0,0 +1,67 @@
|
|||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
// Copyright (c) 2014-2021, The Monero Project.
|
||||||
|
|
||||||
|
#ifndef FUTURE_SCHEDULER_H
|
||||||
|
#define FUTURE_SCHEDULER_H
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#include <QtConcurrent/QtConcurrent>
|
||||||
|
#include <QFuture>
|
||||||
|
#include <QMutex>
|
||||||
|
#include <QMutexLocker>
|
||||||
|
#include <QPair>
|
||||||
|
#include <QWaitCondition>
|
||||||
|
|
||||||
|
class FutureScheduler : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
FutureScheduler(QObject *parent);
|
||||||
|
~FutureScheduler();
|
||||||
|
|
||||||
|
void shutdownWaitForFinished() noexcept;
|
||||||
|
|
||||||
|
QPair<bool, QFuture<void>> run(std::function<void()> function) noexcept;
|
||||||
|
QPair<bool, QFuture<QVariantList>> run(std::function<QVariantList()> function, const std::function<void(QVariantList)> callback);
|
||||||
|
bool stopping() const noexcept;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool add() noexcept;
|
||||||
|
void done() noexcept;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
QPair<bool, QFuture<T>> execute(std::function<QFuture<T>(QFutureWatcher<T> *)> makeFuture) noexcept
|
||||||
|
{
|
||||||
|
if (add())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto *watcher = new QFutureWatcher<T>();
|
||||||
|
connect(watcher, &QFutureWatcher<T>::finished, [watcher] {
|
||||||
|
watcher->deleteLater();
|
||||||
|
});
|
||||||
|
watcher->setFuture(makeFuture(watcher));
|
||||||
|
return qMakePair(true, watcher->future());
|
||||||
|
}
|
||||||
|
catch (const std::exception &exception)
|
||||||
|
{
|
||||||
|
qCritical() << "Failed to schedule async function: " << exception.what();
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return qMakePair(false, QFuture<T>());
|
||||||
|
}
|
||||||
|
|
||||||
|
QFutureWatcher<void> schedule(std::function<void()> function);
|
||||||
|
|
||||||
|
private:
|
||||||
|
size_t Alive;
|
||||||
|
QWaitCondition Condition;
|
||||||
|
QMutex Mutex;
|
||||||
|
std::atomic<bool> Stopping;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // FUTURE_SCHEDULER_H
|
@ -0,0 +1,5 @@
|
|||||||
|
<!DOCTYPE RCC><RCC version="1.0">
|
||||||
|
<qresource prefix="/">
|
||||||
|
<file>assets/monero_grey.png</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
After Width: | Height: | Size: 3.9 KiB |
@ -0,0 +1,21 @@
|
|||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
// Copyright (c) 2021, The Monero Project.
|
||||||
|
|
||||||
|
#include "mainwindow.h"
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QResource>
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
Q_INIT_RESOURCE(assets);
|
||||||
|
|
||||||
|
QApplication a(argc, argv);
|
||||||
|
|
||||||
|
QApplication::setApplicationName("moneroservice");
|
||||||
|
QApplication::setOrganizationName("monero");
|
||||||
|
|
||||||
|
MainWindow w;
|
||||||
|
w.show();
|
||||||
|
|
||||||
|
return a.exec();
|
||||||
|
}
|
@ -0,0 +1,233 @@
|
|||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
// Copyright (c) 2021, The Monero Project.
|
||||||
|
|
||||||
|
#include "mainwindow.h"
|
||||||
|
#include "ui_mainwindow.h"
|
||||||
|
#include "Configurator.h"
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QIcon>
|
||||||
|
|
||||||
|
MainWindow::MainWindow(QWidget *parent) :
|
||||||
|
QMainWindow(parent),
|
||||||
|
ui(new Ui::MainWindow)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
this->setWindowIcon(QIcon(":/assets/monero_grey.png"));
|
||||||
|
|
||||||
|
// Setup tray
|
||||||
|
m_trayIcon = new QSystemTrayIcon(QIcon(":/assets/monero_grey.png"));
|
||||||
|
m_trayIcon->show();
|
||||||
|
m_showWindow = m_trayMenu.addAction("Show window");
|
||||||
|
m_trayIcon->setContextMenu(&m_trayMenu);
|
||||||
|
|
||||||
|
// Daemon manager
|
||||||
|
daemonManager = new DaemonManager(this);
|
||||||
|
|
||||||
|
QString monerodPath = config()->get(Config::pathToMonerod).toString();
|
||||||
|
if (monerodPath.isEmpty()) {
|
||||||
|
monerodPath = this->defaultMonerodPath();
|
||||||
|
}
|
||||||
|
config()->set(Config::pathToMonerod, monerodPath);
|
||||||
|
daemonManager->setMonerodPath(monerodPath);
|
||||||
|
connect(daemonManager, &DaemonManager::daemonConsoleUpdated, this, &MainWindow::writeLogLine);
|
||||||
|
|
||||||
|
connect(m_showWindow, &QAction::triggered, [this]{
|
||||||
|
this->show();
|
||||||
|
this->raise();
|
||||||
|
this->setFocus();
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(ui->btn_Configure, &QPushButton::clicked, this, &MainWindow::showConfigurator);
|
||||||
|
connect(ui->commandPrompt, &CommandLineEdit::commandExecuted, this, &MainWindow::sendCommand);
|
||||||
|
|
||||||
|
this->setStatus("not running");
|
||||||
|
connect(ui->btn_ToggleDaemon, &QPushButton::pressed, [this]{
|
||||||
|
if (daemonManager->isRunning()) {
|
||||||
|
this->setStatus("stopping");
|
||||||
|
daemonManager->stop();
|
||||||
|
} else {
|
||||||
|
QStringList flags = this->getStartupFlags();
|
||||||
|
this->setStatus("starting");
|
||||||
|
ui->btn_ToggleDaemon->setEnabled(false);
|
||||||
|
daemonManager->start(flags, this->getPort());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
connect(daemonManager, &DaemonManager::daemonStarted, [this]{
|
||||||
|
this->setStatus("running");
|
||||||
|
ui->btn_ToggleDaemon->setEnabled(true);
|
||||||
|
ui->btn_ToggleDaemon->setText("Stop daemon");
|
||||||
|
});
|
||||||
|
connect(daemonManager, &DaemonManager::daemonStopped, [this]{
|
||||||
|
this->setStatus("stopped");
|
||||||
|
ui->btn_ToggleDaemon->setText("Start daemon");
|
||||||
|
});
|
||||||
|
|
||||||
|
// qApp->setStyleSheet(this->loadStylesheet(":/qdarkstyle/style.qss"));
|
||||||
|
|
||||||
|
this->adjustSize();
|
||||||
|
// showMinimized();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::writeLogLine(const QString &line) {
|
||||||
|
auto l = line.trimmed();
|
||||||
|
if (!l.isEmpty()) {
|
||||||
|
ui->logs->append(l);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::changeEvent(QEvent* e)
|
||||||
|
{
|
||||||
|
switch (e->type())
|
||||||
|
{
|
||||||
|
case QEvent::WindowStateChange:
|
||||||
|
{
|
||||||
|
if (this->windowState() & Qt::WindowMinimized)
|
||||||
|
{
|
||||||
|
QTimer::singleShot(250, this, SLOT(hide()));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMainWindow::changeEvent(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::sendCommand(const QStringList &command) {
|
||||||
|
daemonManager->sendCommandAsync(command, [](const QVariantList& l){
|
||||||
|
bool success = l[0].toBool();
|
||||||
|
QString message = l[1].toString();
|
||||||
|
|
||||||
|
qDebug() << QString("Command executed: %1, with message: %2").arg(success ? "Success" : "Fail", message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::showConfigurator() {
|
||||||
|
auto *dialog = new Configurator(this);
|
||||||
|
dialog->exec();
|
||||||
|
if (dialog->configUpdated()) {
|
||||||
|
this->askRestartMonerod();
|
||||||
|
}
|
||||||
|
dialog->deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::askRestartMonerod() {
|
||||||
|
QMessageBox::information(this, "Config Updated", "The config has been altered, do you want to restart monerod now?");
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::setStatus(const QString &status) {
|
||||||
|
ui->labelStatus->setText(QString("Daemon status: %1").arg(status));
|
||||||
|
}
|
||||||
|
|
||||||
|
int MainWindow::getPort() {
|
||||||
|
int RPCBindPort = config()->get(Config::RPCBindPort).toInt();
|
||||||
|
if (RPCBindPort) {
|
||||||
|
return RPCBindPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (config()->get(Config::network).toInt()) {
|
||||||
|
case 0:
|
||||||
|
return 18081;
|
||||||
|
case 1:
|
||||||
|
return 38081;
|
||||||
|
case 2:
|
||||||
|
return 28081;
|
||||||
|
}
|
||||||
|
return 18081;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList MainWindow::getStartupFlags() {
|
||||||
|
QStringList startupFlags;
|
||||||
|
|
||||||
|
if (config()->get(Config::pruneBlockchain).toBool()) {
|
||||||
|
startupFlags << "--prune-blockchain";
|
||||||
|
}
|
||||||
|
|
||||||
|
QString dataDir = config()->get(Config::dataDir).toString();
|
||||||
|
if (!dataDir.isEmpty()) {
|
||||||
|
// Todo: check if dir exists
|
||||||
|
startupFlags << "--data-dir" << dataDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
int limitRateDown = config()->get(Config::limitRateDown).toInt();
|
||||||
|
if (limitRateDown > 0) {
|
||||||
|
startupFlags << "--limit-rate-down" << QString::number(limitRateDown);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (config()->get(Config::network).toInt()) {
|
||||||
|
case 1:
|
||||||
|
startupFlags << "--stagenet";
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
startupFlags << "--testnet";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config()->get(Config::noZeroMQ).toBool()) {
|
||||||
|
startupFlags << "--no-zmq";
|
||||||
|
}
|
||||||
|
|
||||||
|
QString RPCBindIP = config()->get(Config::RPCBindIP).toString();
|
||||||
|
if (!RPCBindIP.isEmpty()) {
|
||||||
|
startupFlags << "--rpc-bind-ip" << RPCBindIP;
|
||||||
|
}
|
||||||
|
int RPCBindPort = config()->get(Config::RPCBindPort).toInt();
|
||||||
|
if (RPCBindPort > 0) {
|
||||||
|
startupFlags << "--rpc-bind-port" << QString::number(RPCBindPort);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString P2PBindIP = config()->get(Config::P2PBindIP).toString();
|
||||||
|
if (!RPCBindIP.isEmpty()) {
|
||||||
|
startupFlags << "--p2p-bind-ip" << P2PBindIP;
|
||||||
|
}
|
||||||
|
int P2PBindPort = config()->get(Config::P2PBindPort).toInt();
|
||||||
|
if (RPCBindPort > 0) {
|
||||||
|
startupFlags << "--p2p-bind-port" << QString::number(P2PBindPort);
|
||||||
|
}
|
||||||
|
|
||||||
|
return startupFlags;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString MainWindow::defaultMonerodPath() {
|
||||||
|
QString monerod;
|
||||||
|
|
||||||
|
// Platform dependent path to monerod
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
monerod = QApplication::applicationDirPath() + "/monerod.exe";
|
||||||
|
#elif defined(Q_OS_UNIX)
|
||||||
|
monerod = QApplication::applicationDirPath() + "/monerod";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (monerod.length() == 0) {
|
||||||
|
qCritical() << "no daemon binary defined for current platform";
|
||||||
|
}
|
||||||
|
|
||||||
|
return monerod;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString MainWindow::loadStylesheet(const QString &resource) {
|
||||||
|
QFile f(resource);
|
||||||
|
if (!f.exists()) {
|
||||||
|
printf("Unable to set stylesheet, file not found\n");
|
||||||
|
f.close();
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
f.open(QFile::ReadOnly | QFile::Text);
|
||||||
|
QTextStream ts(&f);
|
||||||
|
QString data = ts.readAll();
|
||||||
|
f.close();
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
MainWindow::~MainWindow()
|
||||||
|
{
|
||||||
|
delete ui;
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
// Copyright (c) 2021, The Monero Project.
|
||||||
|
|
||||||
|
#ifndef MAINWINDOW_H
|
||||||
|
#define MAINWINDOW_H
|
||||||
|
|
||||||
|
#include <QMainWindow>
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QSystemTrayIcon>
|
||||||
|
|
||||||
|
#include "DaemonManager.h"
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class MainWindow;
|
||||||
|
}
|
||||||
|
|
||||||
|
class MainWindow : public QMainWindow
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit MainWindow(QWidget *parent = nullptr);
|
||||||
|
~MainWindow() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void writeLogLine(const QString &line);
|
||||||
|
void changeEvent(QEvent *e) override;
|
||||||
|
void showConfigurator();
|
||||||
|
void sendCommand(const QStringList &command);
|
||||||
|
void askRestartMonerod();
|
||||||
|
void setStatus(const QString &status);
|
||||||
|
QStringList getStartupFlags();
|
||||||
|
int getPort();
|
||||||
|
QString defaultMonerodPath();
|
||||||
|
QString loadStylesheet(const QString &resource);
|
||||||
|
|
||||||
|
Ui::MainWindow *ui;
|
||||||
|
|
||||||
|
QSystemTrayIcon *m_trayIcon;
|
||||||
|
QMenu m_trayMenu;
|
||||||
|
QAction *m_stopDaemon;
|
||||||
|
QAction *m_showWindow;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
DaemonManager *daemonManager;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // MAINWINDOW_H
|
@ -0,0 +1,109 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>MainWindow</class>
|
||||||
|
<widget class="QMainWindow" name="MainWindow">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>828</width>
|
||||||
|
<height>724</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>750</width>
|
||||||
|
<height>650</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Monero Daemon Service</string>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="centralWidget">
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QTextEdit" name="logs">
|
||||||
|
<property name="readOnly">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="placeholderText">
|
||||||
|
<string>Logs are empty</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="CommandLineEdit" name="commandPrompt">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<weight>75</weight>
|
||||||
|
<bold>true</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="placeholderText">
|
||||||
|
<string>Enter command</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="labelStatus">
|
||||||
|
<property name="text">
|
||||||
|
<string>status</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btn_ToggleDaemon">
|
||||||
|
<property name="text">
|
||||||
|
<string>Start daemon</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btn_Configure">
|
||||||
|
<property name="text">
|
||||||
|
<string>Configure</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
<layoutdefault spacing="6" margin="11"/>
|
||||||
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>CommandLineEdit</class>
|
||||||
|
<extends>QLineEdit</extends>
|
||||||
|
<header>CommandLineEdit.h</header>
|
||||||
|
</customwidget>
|
||||||
|
</customwidgets>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
@ -0,0 +1,89 @@
|
|||||||
|
<RCC>
|
||||||
|
<qresource>
|
||||||
|
<file>light/hmovetoolbar.svg</file>
|
||||||
|
<file>light/vmovetoolbar.svg</file>
|
||||||
|
<file>light/hsepartoolbar.svg</file>
|
||||||
|
<file>light/vsepartoolbars.svg</file>
|
||||||
|
<file>light/stylesheet-branch-end.svg</file>
|
||||||
|
<file>light/stylesheet-branch-end-closed.svg</file>
|
||||||
|
<file>light/stylesheet-branch-end-open.svg</file>
|
||||||
|
<file>light/stylesheet-vline.svg</file>
|
||||||
|
<file>light/stylesheet-branch-more.svg</file>
|
||||||
|
<file>light/branch_closed.svg</file>
|
||||||
|
<file>light/branch_closed-on.svg</file>
|
||||||
|
<file>light/branch_open.svg</file>
|
||||||
|
<file>light/branch_open-on.svg</file>
|
||||||
|
<file>light/down_arrow.svg</file>
|
||||||
|
<file>light/down_arrow_disabled.svg</file>
|
||||||
|
<file>light/down_arrow-hover.svg</file>
|
||||||
|
<file>light/left_arrow.svg</file>
|
||||||
|
<file>light/left_arrow_disabled.svg</file>
|
||||||
|
<file>light/right_arrow.svg</file>
|
||||||
|
<file>light/right_arrow_disabled.svg</file>
|
||||||
|
<file>light/up_arrow.svg</file>
|
||||||
|
<file>light/up_arrow_disabled.svg</file>
|
||||||
|
<file>light/up_arrow-hover.svg</file>
|
||||||
|
<file>light/sizegrip.svg</file>
|
||||||
|
<file>light/transparent.svg</file>
|
||||||
|
<file>light/close.svg</file>
|
||||||
|
<file>light/close-hover.svg</file>
|
||||||
|
<file>light/close-pressed.svg</file>
|
||||||
|
<file>light/undock.svg</file>
|
||||||
|
<file>light/undock-hover.svg</file>
|
||||||
|
<file>light/checkbox_checked-hover.svg</file>
|
||||||
|
<file>light/checkbox_checked.svg</file>
|
||||||
|
<file>light/checkbox_checked_disabled.svg</file>
|
||||||
|
<file>light/checkbox_indeterminate.svg</file>
|
||||||
|
<file>light/checkbox_indeterminate-hover.svg</file>
|
||||||
|
<file>light/checkbox_indeterminate_disabled.svg</file>
|
||||||
|
<file>light/checkbox_unchecked-hover.svg</file>
|
||||||
|
<file>light/checkbox_unchecked_disabled.svg</file>
|
||||||
|
<file>light/radio_checked-hover.svg</file>
|
||||||
|
<file>light/radio_checked.svg</file>
|
||||||
|
<file>light/radio_checked_disabled.svg</file>
|
||||||
|
<file>light/radio_unchecked-hover.svg</file>
|
||||||
|
<file>light/radio_unchecked_disabled.svg</file>
|
||||||
|
<file>dark/hmovetoolbar.svg</file>
|
||||||
|
<file>dark/vmovetoolbar.svg</file>
|
||||||
|
<file>dark/hsepartoolbar.svg</file>
|
||||||
|
<file>dark/vsepartoolbars.svg</file>
|
||||||
|
<file>dark/stylesheet-branch-end.svg</file>
|
||||||
|
<file>dark/stylesheet-branch-end-closed.svg</file>
|
||||||
|
<file>dark/stylesheet-branch-end-open.svg</file>
|
||||||
|
<file>dark/stylesheet-vline.svg</file>
|
||||||
|
<file>dark/stylesheet-branch-more.svg</file>
|
||||||
|
<file>dark/branch_closed.svg</file>
|
||||||
|
<file>dark/branch_closed-on.svg</file>
|
||||||
|
<file>dark/branch_open.svg</file>
|
||||||
|
<file>dark/branch_open-on.svg</file>
|
||||||
|
<file>dark/down_arrow.svg</file>
|
||||||
|
<file>dark/down_arrow_disabled.svg</file>
|
||||||
|
<file>dark/down_arrow-hover.svg</file>
|
||||||
|
<file>dark/left_arrow.svg</file>
|
||||||
|
<file>dark/left_arrow_disabled.svg</file>
|
||||||
|
<file>dark/right_arrow.svg</file>
|
||||||
|
<file>dark/right_arrow_disabled.svg</file>
|
||||||
|
<file>dark/up_arrow.svg</file>
|
||||||
|
<file>dark/up_arrow_disabled.svg</file>
|
||||||
|
<file>dark/up_arrow-hover.svg</file>
|
||||||
|
<file>dark/sizegrip.svg</file>
|
||||||
|
<file>dark/transparent.svg</file>
|
||||||
|
<file>dark/close.svg</file>
|
||||||
|
<file>dark/close-hover.svg</file>
|
||||||
|
<file>dark/close-pressed.svg</file>
|
||||||
|
<file>dark/undock.svg</file>
|
||||||
|
<file>dark/undock-hover.svg</file>
|
||||||
|
<file>dark/checkbox_checked.svg</file>
|
||||||
|
<file>dark/checkbox_checked_disabled.svg</file>
|
||||||
|
<file>dark/checkbox_indeterminate.svg</file>
|
||||||
|
<file>dark/checkbox_indeterminate_disabled.svg</file>
|
||||||
|
<file>dark/checkbox_unchecked.svg</file>
|
||||||
|
<file>dark/checkbox_unchecked_disabled.svg</file>
|
||||||
|
<file>dark/radio_checked.svg</file>
|
||||||
|
<file>dark/radio_checked_disabled.svg</file>
|
||||||
|
<file>dark/radio_unchecked.svg</file>
|
||||||
|
<file>dark/radio_unchecked_disabled.svg</file>
|
||||||
|
<file>light.qss</file>
|
||||||
|
<file>dark.qss</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
After Width: | Height: | Size: 94 B |
After Width: | Height: | Size: 78 B |
After Width: | Height: | Size: 74 B |
After Width: | Height: | Size: 82 B |
After Width: | Height: | Size: 330 B |
After Width: | Height: | Size: 330 B |
After Width: | Height: | Size: 492 B |
After Width: | Height: | Size: 492 B |
After Width: | Height: | Size: 260 B |
After Width: | Height: | Size: 260 B |
After Width: | Height: | Size: 530 B |
After Width: | Height: | Size: 530 B |
After Width: | Height: | Size: 530 B |
After Width: | Height: | Size: 554 B |
After Width: | Height: | Size: 551 B |
After Width: | Height: | Size: 554 B |
After Width: | Height: | Size: 253 B |
After Width: | Height: | Size: 188 B |
After Width: | Height: | Size: 554 B |
After Width: | Height: | Size: 557 B |
After Width: | Height: | Size: 427 B |
After Width: | Height: | Size: 427 B |
After Width: | Height: | Size: 341 B |
After Width: | Height: | Size: 341 B |
After Width: | Height: | Size: 460 B |
After Width: | Height: | Size: 463 B |
After Width: | Height: | Size: 244 B |
After Width: | Height: | Size: 455 B |
After Width: | Height: | Size: 173 B |
After Width: | Height: | Size: 173 B |
After Width: | Height: | Size: 173 B |
After Width: | Height: | Size: 173 B |
After Width: | Height: | Size: 90 B |
After Width: | Height: | Size: 30 B |
After Width: | Height: | Size: 343 B |
After Width: | Height: | Size: 144 B |
After Width: | Height: | Size: 546 B |
After Width: | Height: | Size: 543 B |
After Width: | Height: | Size: 546 B |
After Width: | Height: | Size: 665 B |
After Width: | Height: | Size: 574 B |
After Width: | Height: | Size: 94 B |
After Width: | Height: | Size: 78 B |
After Width: | Height: | Size: 74 B |
After Width: | Height: | Size: 82 B |
After Width: | Height: | Size: 330 B |
After Width: | Height: | Size: 330 B |
After Width: | Height: | Size: 330 B |
After Width: | Height: | Size: 492 B |
After Width: | Height: | Size: 492 B |
After Width: | Height: | Size: 492 B |
After Width: | Height: | Size: 260 B |
After Width: | Height: | Size: 260 B |
After Width: | Height: | Size: 530 B |
After Width: | Height: | Size: 530 B |
After Width: | Height: | Size: 530 B |
After Width: | Height: | Size: 554 B |
After Width: | Height: | Size: 554 B |
After Width: | Height: | Size: 554 B |
After Width: | Height: | Size: 254 B |
After Width: | Height: | Size: 188 B |
After Width: | Height: | Size: 557 B |
After Width: | Height: | Size: 557 B |
After Width: | Height: | Size: 427 B |
After Width: | Height: | Size: 427 B |
After Width: | Height: | Size: 427 B |
After Width: | Height: | Size: 341 B |
After Width: | Height: | Size: 341 B |
After Width: | Height: | Size: 554 B |
After Width: | Height: | Size: 463 B |
After Width: | Height: | Size: 244 B |
After Width: | Height: | Size: 455 B |
After Width: | Height: | Size: 173 B |
After Width: | Height: | Size: 173 B |