@ -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 |