Improve handling of Qt-related warnings and errors (#7431)

* Load .env from app dir

* Use `OutputDebugStringA` on Windows

* Improved string manipulation in logger and better use of OutputDebugStringA

* Use noquote for path

* Apply TLS setting after activation

* Restart after activation

* Move skip debug message to correct place

* Only print settings path on create, not shown

* Merge common and global libs

* Reduce code duplication around constants, version, etc

* Simplify sprintf code

* Use snprintf

* Add error test menu

* Coverage for dotenv

* Coverage for Logger

* Add missing include

* Add init when no Git SHA

* Update ChangeLog

* Add spellings

* Set debugging on for release build

* Remove unused variable

* Remove bad comment

* Fixed copyright date

* Remove import

* Move URL const back to GUI

* Remove dead code and duplication

* Fixed/ignored lint warnings
This commit is contained in:
Nick Bolton
2024-08-09 14:03:48 +01:00
committed by GitHub
parent ef186d398d
commit 321418fcf8
67 changed files with 372 additions and 319 deletions

View File

@ -74,6 +74,7 @@ Enhancements:
- #7428 Refactor settings dialog to simplify enable/disable logic
- #7429 Parse date numbers as long instead of int
- #7430 Improve setting enable logic and test coverage
- #7431 Improve handling of Qt-related warnings and errors
# 1.14.6

View File

@ -9,6 +9,7 @@
"Breen",
"codesign",
"codesigning",
"Compat",
"contribs",
"Daun",
"distros",
@ -19,6 +20,9 @@
"gdrive",
"Hadzhylov",
"Hetu",
"hotspots",
"ifdef",
"integtests",
"keychain",
"Keychains",
"Kutytska",
@ -37,6 +41,8 @@
"Pixmap",
"Poschta",
"Priddy",
"pyproject",
"qputenv",
"Regen",
"runas",
"Sbârnea",
@ -46,6 +52,7 @@
"synergyc",
"synergyd",
"synergys",
"Valgrind",
"Volker",
"winget"
],

View File

@ -28,7 +28,6 @@ target_link_libraries(
arch
base
client
common
io
mt
net

View File

@ -54,7 +54,6 @@ target_link_libraries(
arch
base
client
common
io
mt
net

View File

@ -29,14 +29,12 @@ target_link_libraries(
${target}
arch
base
common
io
ipc
mt
net
platform
synlib
global
license
${libs})

View File

@ -54,7 +54,6 @@ target_link_libraries(
arch
base
client
common
io
mt
net

View File

@ -30,7 +30,6 @@ target_link_libraries(
arch
base
client
common
io
ipc
mt

View File

@ -51,7 +51,6 @@ include_directories(${CMAKE_BINARY_DIR}/src/lib/gui/gui_autogen/include)
target_link_libraries(
${target}
gui
global
license
Qt6::Core
Qt6::Widgets

View File

@ -17,6 +17,7 @@
*/
#include "AboutDialog.h"
#include "common/copyright.h"
#include <QDateTime>
@ -24,7 +25,7 @@
#include "gui/OSXHelpers.h"
#endif
#include "gui/version.h"
#include "common/version.h"
AboutDialog::AboutDialog(MainWindow *parent)
: QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint),
@ -34,7 +35,8 @@ AboutDialog::AboutDialog(MainWindow *parent)
this->setFixedSize(this->size());
m_pLabelSynergyVersion->setText(synergy::gui::version());
QString version = QString::fromStdString(synergy::version());
m_pLabelSynergyVersion->setText(version);
QString buildDateString = QString::fromLocal8Bit(__DATE__).simplified();
QDate buildDate = QLocale("en_US").toDate(buildDateString, "MMM d yyyy");
@ -43,8 +45,8 @@ AboutDialog::AboutDialog(MainWindow *parent)
}
int AboutDialog::exec() {
m_pDevelopersLabel->setText(getImportantDevelopers());
m_pCopyrightLabel->setText(getCopyright());
m_pDevelopersLabel->setText(importantDevelopers());
m_pCopyrightLabel->setText(QString::fromStdString(synergy::copyright()));
updateLogo();
return QDialog::exec();
@ -61,7 +63,7 @@ void AboutDialog::updateLogo() const {
#endif
}
QString AboutDialog::getImportantDevelopers() const {
QString AboutDialog::importantDevelopers() const {
QStringList awesomePeople;
// Chris is the ultimate creator, and the one who started it all in 2001.
@ -104,14 +106,3 @@ QString AboutDialog::getImportantDevelopers() const {
return awesomePeople.join(", ") + ".";
}
QString AboutDialog::getCopyright() const {
QString buildDateString = QString::fromLocal8Bit(__DATE__).simplified();
QDate buildDate = QLocale("en_US").toDate(buildDateString, "MMM d yyyy");
QString copyright("Copyright © 2012-%%YEAR%% Symless Ltd.\n"
"Copyright © 2009-2012 Nick Bolton\n"
"Copyright © 2002-2009 Chris Schoeneman");
return copyright.replace(
QString("%%YEAR%%"), QString::number(buildDate.year()));
}

View File

@ -37,6 +37,5 @@ private:
VersionChecker m_versionChecker;
void updateLogo() const;
virtual QString getCopyright() const;
virtual QString getImportantDevelopers() const;
virtual QString importantDevelopers() const;
};

View File

@ -75,13 +75,13 @@ ActivationDialog::~ActivationDialog() { delete m_ui; }
void ActivationDialog::reject() {
// don't show the cancel confirmation dialog if they've already registered,
// since it's not revent to customers who are changing their serial key.
// since it's not relevant to customers who are changing their serial key.
if (m_licenseHandler.productEdition() != Edition::kUnregistered) {
QDialog::reject();
return;
}
// the accept button should be labeled "Exit" on the cancel dialoig.
// the accept button should be labeled "Exit" on the cancel dialog.
CancelActivationDialog cancelActivationDialog(this);
if (cancelActivationDialog.exec() == QDialog::Accepted) {
QApplication::exit();

View File

@ -31,6 +31,7 @@
#include "gui/license/LicenseHandler.h"
#include "gui/license/license_notices.h"
#include "gui/messages.h"
#include "gui/string_utils.h"
#include "gui/styles.h"
#include "gui/tls/TlsFingerprint.h"
#include "license/License.h"
@ -258,14 +259,6 @@ void MainWindow::connectSlots() {
m_pActionRestore, &QAction::triggered, //
[this]() { showAndActivate(); });
connect(
m_pActionStartCore, &QAction::triggered, this,
&MainWindow::onActionStartCoreTriggered);
connect(
m_pActionStopCore, &QAction::triggered, this,
&MainWindow::onActionStopCoreTriggered);
connect(m_pActionQuit, &QAction::triggered, qApp, &QCoreApplication::quit);
connect(
@ -310,6 +303,9 @@ void MainWindow::onCreated() {
updateScreenName();
applyConfig();
restoreWindow();
qDebug().noquote() << "active settings path:"
<< m_ConfigScopes.activeFilePath();
}
void MainWindow::onShown() {
@ -366,16 +362,6 @@ void MainWindow::onVersionCheckerUpdateFound(const QString &version) {
m_pLabelUpdate->setText(text);
}
void MainWindow::onActionStartCoreTriggered() {
m_ClientConnection.setShowMessage();
m_CoreProcess.start();
}
void MainWindow::onActionStopCoreTriggered() {
qDebug("stopping core process");
m_CoreProcess.stop();
}
void MainWindow::onAppConfigScreenNameChanged() { updateScreenName(); }
void MainWindow::onAppConfigInvertConnection() { applyConfig(); }
@ -396,6 +382,24 @@ void MainWindow::onCoreProcessError(CoreProcess::Error error) {
}
}
void MainWindow::on_m_pActionStartCore_triggered() {
m_ClientConnection.setShowMessage();
m_CoreProcess.start();
}
void MainWindow::on_m_pActionStopCore_triggered() {
qDebug("stopping core process");
m_CoreProcess.stop();
}
void MainWindow::on_m_pActionTestFatalError_triggered() const {
qFatal("test fatal error");
}
void MainWindow::on_m_pActionTestCriticalError_triggered() const {
qCritical("test critical error");
}
bool MainWindow::on_m_pActionSave_triggered() {
QString fileName =
QFileDialog::getSaveFileName(this, QString("Save configuration as..."));
@ -596,6 +600,22 @@ void MainWindow::createMenuBar() {
m_pMenuHelp->addAction(m_pActionAbout);
m_pMenuHelp->addAction(m_pActionHelp);
#ifndef NDEBUG
// always enable test menu in debug mode.
const auto enableTestMenu = true;
#else
// only enable test menu in release build if env var is true.
const auto enableTestMenu =
strToTrue(qEnvironmentVariable("SYNERGY_TEST_MENU"));
#endif
if (enableTestMenu) {
auto testMenu = new QMenu("Test", m_pMenuBar);
m_pMenuBar->addMenu(testMenu);
testMenu->addAction(m_pActionTestFatalError);
testMenu->addAction(m_pActionTestCriticalError);
}
setMenuBar(m_pMenuBar);
}
@ -1041,6 +1061,11 @@ int MainWindow::showActivationDialog() {
if (result == QDialog::Accepted) {
m_AppConfig.setActivationHasRun(true);
// customers who are activating a pro license are usually doing so because
// they want tls. so, if it's available, turn it on after activating.
m_AppConfig.setTlsEnabled(m_LicenseHandler.license().isTlsAvailable());
m_ConfigScopes.save();
}
@ -1051,6 +1076,13 @@ int MainWindow::showActivationDialog() {
m_PendingClientNames.clear();
}
// restart core process after activation in case switching on tls.
// this saves customer from having to figure out they need to click apply.
if (m_CoreProcess.isStarted()) {
m_CoreProcess.restart();
}
return result;
}

View File

@ -27,7 +27,7 @@
#include "ActivationDialog.h"
#include "ServerConfig.h"
#include "global/Ipc.h"
#include "common/ipc.h"
#include "gui/TrayIcon.h"
#include "gui/VersionChecker.h"
#include "gui/config/AppConfig.h"
@ -108,8 +108,6 @@ private slots:
void onLicenseHandlerInvalidLicense();
void onVersionCheckerUpdateFound(const QString &version);
void onTrayIconActivated(QSystemTrayIcon::ActivationReason reason);
void onActionStartCoreTriggered();
void onActionStopCoreTriggered();
void onWindowSaveTimerTimeout();
void onServerConnectionConfigureClient(const QString &clientName);
@ -129,6 +127,10 @@ private slots:
void on_m_pActionHelp_triggered() const;
void on_m_pActionSettings_triggered();
void on_m_pActionActivate_triggered();
void on_m_pActionStartCore_triggered();
void on_m_pActionStopCore_triggered();
void on_m_pActionTestFatalError_triggered() const;
void on_m_pActionTestCriticalError_triggered() const;
void on_m_pLineEditHostname_returnPressed();
void on_m_pLineEditClientIp_returnPressed();
void on_m_pLineEditHostname_textChanged(const QString &text);

View File

@ -599,14 +599,6 @@
<string>Ctrl+T</string>
</property>
</action>
<action name="actionShowStatus">
<property name="text">
<string>S&amp;how Status</string>
</property>
<property name="shortcut">
<string notr="true">Ctrl+H</string>
</property>
</action>
<action name="m_pActionMinimize">
<property name="text">
<string>&amp;Hide</string>
@ -659,6 +651,22 @@
<string>Activate</string>
</property>
</action>
<action name="m_pActionTestFatalError">
<property name="text">
<string>Test fatal error</string>
</property>
<property name="menuRole">
<enum>QAction::TextHeuristicRole</enum>
</property>
</action>
<action name="m_pActionTestCriticalError">
<property name="text">
<string>Test critical error</string>
</property>
<property name="menuRole">
<enum>QAction::TextHeuristicRole</enum>
</property>
</action>
</widget>
<customwidgets>
<customwidget>

View File

@ -48,8 +48,7 @@ SetupWizardBlocker::SetupWizardBlocker(BlockerType type) {
}
void SetupWizardBlocker::onlineSupport() {
QDesktopServices::openUrl(
QUrl("https://symless.com/synergy/help?source=gui"));
QDesktopServices::openUrl(QUrl(synergy::gui::kUrlHelp));
cancel();
}

View File

@ -20,13 +20,13 @@
#include "QSynergyApplication.h"
#include "SetupWizard.h"
#include "SetupWizardBlocker.h"
#include "common/constants.h"
#include "common/version.h"
#include "gui/Logger.h"
#include "gui/config/AppConfig.h"
#include "gui/config/ConfigScopes.h"
#include "gui/constants.h"
#include "gui/dotenv.h"
#include "gui/messages.h"
#include "gui/version.h"
#include <QApplication>
#include <QDebug>
@ -61,18 +61,23 @@ int main(int argc, char *argv[]) {
::setenv("QT_BEARER_POLL_TIMEOUT", "-1", 1);
#endif
QCoreApplication::setOrganizationName(kAppName);
QCoreApplication::setApplicationName(kAppName);
QCoreApplication::setOrganizationDomain(kAppDomain);
// HACK: set org name to app name for backwards compatibility.
QCoreApplication::setOrganizationName(kAppName);
// HACK: set org domain to url for backwards compatibility.
QCoreApplication::setOrganizationDomain(kUrlWebsite);
QSynergyApplication app(argc, argv);
qInstallMessageHandler(synergy::gui::messages::messageHandler);
QString version = QString::fromStdString(synergy::version());
qInfo("Synergy v%s", qPrintable(version));
dotenv(".env");
dotenv();
Logger::instance().loadEnvVars();
qInfo("Synergy v%s", synergy::gui::version().toUtf8().constData());
#if defined(Q_OS_MAC)
if (app.applicationDirPath().startsWith("/Volumes/")) {

View File

@ -17,7 +17,6 @@
add_subdirectory(arch)
add_subdirectory(base)
add_subdirectory(client)
add_subdirectory(common)
add_subdirectory(io)
add_subdirectory(ipc)
add_subdirectory(mt)
@ -25,6 +24,5 @@ add_subdirectory(net)
add_subdirectory(platform)
add_subdirectory(server)
add_subdirectory(synergy)
add_subdirectory(global)
add_subdirectory(license)
add_subdirectory(gui)

View File

@ -19,7 +19,7 @@
#include "arch/win32/ArchMiscWindows.h"
#include "arch/win32/ArchDaemonWindows.h"
#include "base/Log.h"
#include "common/Version.h"
#include "common/constants.h"
#include <Wtsapi32.h>
#pragma warning(disable : 4099)

View File

@ -24,5 +24,5 @@ endif()
add_library(base STATIC ${sources})
if(UNIX)
target_link_libraries(base common)
target_link_libraries(base)
endif()

View File

@ -19,7 +19,7 @@
#include "base/Log.h"
#include "arch/Arch.h"
#include "base/log_outputters.h"
#include "common/Version.h"
#include "common/constants.h"
#include <cstdarg>
#include <cstdint>
@ -223,7 +223,7 @@ void Log::insert(ILogOutputter *outputter, bool alwaysAtHead) {
m_outputters.push_front(outputter);
}
outputter->open(kAppVersion);
outputter->open(kAppName);
// Issue 41
// don't show log unless user requests it, as some users find this

View File

@ -1,24 +0,0 @@
# Synergy -- mouse and keyboard sharing utility
# Copyright (C) 2012-2024 Symless Ltd.
# Copyright (C) 2009-2012 Nick Bolton
#
# This package is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# found in the file LICENSE that should have accompanied this file.
#
# This package is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
file(GLOB headers "*.h")
file(GLOB sources "*.cpp")
if(ADD_HEADERS_TO_SOURCES)
list(APPEND sources ${headers})
endif()
add_library(common STATIC ${sources})

View File

@ -1,29 +0,0 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012-2024 Symless Ltd.
* Copyright (C) 2009-2012 Nick Bolton
* Copyright (C) 2002-2009 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file LICENSE that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "common/Version.h"
const char *kApplication = "Synergy";
const char *kCopyright = "Copyright (C) 2012-%s Symless Ltd.\n"
"Copyright (C) 2009-2012 Nick Bolton\n"
"Copyright (C) 2002-2009 Chris Schoeneman";
const char *kContact = "Email: engineering@symless.com";
const char *kWebsite = "https://symless.com/";
const char *kVersion = SYNERGY_VERSION;
const char *kAppVersion = "Synergy " SYNERGY_VERSION;

View File

@ -1,39 +0,0 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012-2016 Symless Ltd.
* Copyright (C) 2002 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file LICENSE that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "common/common.h"
// set version macro if not set yet
#if !defined(SYNERGY_VERSION)
#error Version was not set (should be passed to compiler).
#endif
// important strings
extern const char *kApplication;
extern const char *kCopyright;
extern const char *kContact;
extern const char *kWebsite;
// build version. follows linux kernel style: an even minor number implies
// a release version, odd implies development version.
extern const char *kVersion;
// application version
extern const char *kAppVersion;

View File

@ -1,6 +1,7 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012 Symless Ltd.
* Copyright (C) 2002 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -15,14 +16,17 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Ipc.h"
#pragma once
const char *const kIpcHost = "127.0.0.1";
const int kIpcPort = 24801;
#if !defined(SYNERGY_VERSION)
#error version was not passed to the compiler
#endif
const char *const kIpcMsgHello = "IHEL%1i";
const char *const kIpcMsgHelloBack = "IHEL";
const char *const kIpcMsgLogLine = "ILOG%s";
const char *const kIpcMsgCommand = "ICMD%s%1i";
const char *const kIpcMsgShutdown = "ISDN";
const char *const kIpcMsgSetting = "SSET%s%s";
const auto kAppName = "Synergy";
const auto kVersion = SYNERGY_VERSION;
#ifdef GIT_SHA_SHORT
const auto kVersionGitSha = GIT_SHA_SHORT;
#else
const auto kVersionGitSha = "";
#endif

View File

@ -15,24 +15,27 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "version.h"
#pragma once
const QString kVersion = SYNERGY_VERSION;
#include <cstring>
#include <string>
#include <vector>
#ifdef GIT_SHA_SHORT
const QString kVersionGitSha = GIT_SHA_SHORT;
#else
const QString kVersionGitSha;
#endif
namespace synergy {
namespace synergy::gui {
const auto kCopyrightFormat = //
"Copyright (C) 2012-%s Symless Ltd.\n"
"Copyright (C) 2009-2012 Nick Bolton\n"
"Copyright (C) 2002-2009 Chris Schoeneman";
QString version() {
QString result(kVersion);
if (!kVersionGitSha.isEmpty()) {
result.append(QString(" (%1)").arg(kVersionGitSha));
}
return result;
inline std::string copyright() {
const std::string date = __DATE__;
const auto year = date.substr(date.size() - 4);
const auto kBufferLength = 256;
std::vector<char> buffer(kBufferLength);
std::snprintf( // NOSONAR
buffer.data(), kBufferLength, kCopyrightFormat, year.c_str());
return std::string(buffer.data());
}
} // namespace synergy::gui
} // namespace synergy

View File

@ -30,31 +30,31 @@ enum class IpcMessageType : UInt8 {
enum class IpcClientType { Unknown, GUI, Node };
extern const char *const kIpcHost;
extern const int kIpcPort;
const auto kIpcHost = "127.0.0.1";
const auto kIpcPort = 24801;
// handshake: node/gui -> daemon
// $1 = type, the client identifies it's self as gui or node (synergyc/s).
extern const char *const kIpcMsgHello;
const auto kIpcMsgHello = "IHEL%1i";
// handshake: daemon -> node/gui
// the daemon responds to the handshake.
extern const char *const kIpcMsgHelloBack;
const auto kIpcMsgHelloBack = "IHEL";
// log line: daemon -> gui
// $1 = aggregate log lines collected from synergys/c or the daemon itself.
extern const char *const kIpcMsgLogLine;
const auto kIpcMsgLogLine = "ILOG%s";
// command: gui -> daemon
// $1 = command; the command for the daemon to launch, typically the full
// path to synergys/c. $2 = true when process must be elevated on ms windows.
extern const char *const kIpcMsgCommand;
const auto kIpcMsgCommand = "ICMD%s%1i";
// shutdown: daemon -> node
// the daemon tells synergys/c to shut down gracefully.
extern const char *const kIpcMsgShutdown;
const auto kIpcMsgShutdown = "ISDN";
// set setting: gui -> daemon
// $1 = setting name
// $2 = setting value
extern const char *const kIpcMsgSetting;
const auto kIpcMsgSetting = "SSET%s%s";

View File

@ -17,10 +17,21 @@
#pragma once
#include <QString>
#include "constants.h"
namespace synergy::gui {
#include <string>
QString version();
namespace synergy {
} // namespace synergy::gui
inline std::string version() {
std::string result = kVersion;
std::string gitSha = kVersionGitSha;
if (!gitSha.empty()) {
result.append(" (");
result.append(gitSha);
result.append(")");
}
return result;
}
} // namespace synergy

View File

@ -1,26 +0,0 @@
# Synergy -- mouse and keyboard sharing utility
# Copyright (C) 2012-2024 Symless Ltd.
# Copyright (C) 2009-2012 Nick Bolton
#
# This package is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# found in the file LICENSE that should have accompanied this file.
#
# This package is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
file(GLOB headers "*.h")
file(GLOB sources "*.cpp")
if(ADD_HEADERS_TO_SOURCES)
list(APPEND sources ${headers})
endif()
add_library(global STATIC ${sources})
target_link_libraries(global)

View File

@ -36,7 +36,6 @@ endif()
add_library(${target} STATIC ${sources} ${ui_files})
target_link_libraries(
${target}
global
license
Qt6::Core
Qt6::Widgets

View File

@ -24,24 +24,58 @@
#include <QMessageBox>
#include <QTime>
#if defined(Q_OS_WIN)
#include <Windows.h>
#endif
namespace synergy::gui {
const auto kForceDebugMessages = QStringList{
"Synergy", // TEST
"No functional TLS backend was found",
"No TLS backend is available",
"QSslSocket::connectToHostEncrypted: TLS initialization failed",
"Retrying to obtain clipboard.",
"Unable to obtain clipboard."};
Logger Logger::s_instance;
QString fileLine(const QMessageLogContext &context) {
if (!context.file) {
return "";
}
return QString("%1:%2").arg(context.file).arg(context.line);
}
QString printLine(
FILE *out, const QString &type, const QString &message,
const QString &fileLine = "") {
auto datetime = QDateTime::currentDateTime().toString("yyyy-MM-ddTHH:mm:ss");
auto logLine = QString("[%1] %2: %3").arg(datetime).arg(type).arg(message);
QTextStream stream(&logLine);
if (!fileLine.isEmpty()) {
logLine += "\n\t" + fileLine;
stream << Qt::endl << "\t" + fileLine;
}
auto logLineUtf = logLine.toUtf8();
auto logLine_c = logLineUtf.constData();
fprintf(out, "%s\n", logLine_c);
QString logLineReturn = logLine;
QTextStream streamReturn(&logLineReturn);
streamReturn << Qt::endl;
auto logLineReturn_c = qPrintable(logLineReturn);
#if defined(Q_OS_WIN)
// Debug output is viewable using either VS Code, Visual Studio, DebugView, or
// DbgView++ (only one can be used at once). It's important to send output to
// the debug output API, because it's difficult to view stdout and stderr from
// a Windows GUI app.
OutputDebugStringA(logLineReturn_c);
#else
fprintf(out, "%s", logLineReturn_c);
fflush(out);
#endif
return logLine;
}
@ -64,11 +98,17 @@ void Logger::logVerbose(const QString &message) const {
}
void Logger::handleMessage(
QtMsgType type, const QMessageLogContext &context, const QString &message) {
const QtMsgType type, const QMessageLogContext &context,
const QString &message) {
auto mutatedType = type;
if (kForceDebugMessages.contains(message)) {
mutatedType = QtDebugMsg;
}
QString typeString;
auto out = stdout;
switch (type) {
switch (mutatedType) {
case QtDebugMsg:
typeString = "DEBUG";
if (!m_debug) {
@ -92,8 +132,7 @@ void Logger::handleMessage(
break;
}
const auto fileLine = QString("%1:%2").arg(context.file).arg(context.line);
const auto logLine = printLine(out, typeString, message, fileLine);
const auto logLine = printLine(out, typeString, message, fileLine(context));
emit newLine(logLine);
}

View File

@ -35,7 +35,7 @@ public:
void loadEnvVars();
void handleMessage(
QtMsgType type, const QMessageLogContext &context,
const QtMsgType type, const QMessageLogContext &context,
const QString &message);
void logVerbose(const QString &message) const;

View File

@ -17,7 +17,7 @@
#include "TrayIcon.h"
#include "constants.h"
#include "common/constants.h"
namespace synergy::gui {

View File

@ -96,8 +96,6 @@ ConfigScopes::ConfigScopes() {
m_pUserSettings = std::make_unique<QSettings>();
m_userSettingsProxy.set(*m_pUserSettings);
qDebug() << "user settings path:" << m_pUserSettings->fileName();
QSettings::setPath(
QSettings::Format::IniFormat, QSettings::Scope::SystemScope,
getSystemSettingPath());
@ -109,8 +107,6 @@ ConfigScopes::ConfigScopes() {
appName);
m_systemSettingsProxy.set(*m_pSystemSettings);
qDebug() << "system settings path:" << m_pSystemSettings->fileName();
#if defined(Q_OS_WIN)
loadWindowsLegacy(*m_pSystemSettings);
#endif
@ -164,6 +160,10 @@ const QSettingsProxy &ConfigScopes::activeSettings() const {
}
}
QString ConfigScopes::activeFilePath() const {
return activeSettings().fileName();
}
QVariant ConfigScopes::getFromScope(
const QString &name, const QVariant &defaultValue, Scope scope) const {
switch (scope) {

View File

@ -51,6 +51,7 @@ public:
Scope activeScope() const override;
QSettingsProxy &activeSettings() override;
const QSettingsProxy &activeSettings() const override;
QString activeFilePath() const;
signals:
void ready();

View File

@ -21,12 +21,6 @@
namespace synergy::gui {
const auto kAppName = "Synergy";
// TODO: change to `com.symless`. we'll need to gracefully import old settings,
// since qt uses this on some platforms when saving settings.
const auto kAppDomain = "https://symless.com";
const int kDebugLogLevel = 1;
#ifdef SYNERGY_PRODUCT_NAME
@ -54,8 +48,8 @@ const auto kLinkBuy = R"(<a href="%1" style="color: %2">Buy now</a>)";
const auto kLinkRenew = R"(<a href="%1" style="color: %2">Renew now</a>)";
const auto kLinkDownload = R"(<a href="%1" style="color: %2">Download now</a>)";
const auto kUrlSourceQuery = "source=gui";
const auto kUrlWebsite = "https://symless.com";
const auto kUrlSourceQuery = "source=gui";
const auto kUrlGitHub = "https://github.com/symless/synergy-core";
const auto kUrlGnomeTrayFix =
"https://extensions.gnome.org/extension/2890/tray-icons-reloaded/";

View File

@ -39,12 +39,14 @@ void ClientConnection::Deps::showError(
//
void ClientConnection::handleLogLine(const QString &logLine) {
if (!m_showMessage) {
qDebug("message already shown, skipping");
return;
}
if (logLine.contains("failed to connect to server")) {
if (!m_showMessage) {
qDebug("message already shown, skipping");
return;
}
m_showMessage = false;
// ignore the message if it's about the server refusing by name as

View File

@ -17,7 +17,9 @@
#include "dotenv.h"
#include <QCoreApplication>
#include <QDebug>
#include <QDir>
#include <QFile>
#include <QProcess>
#include <QProcessEnvironment>
@ -28,6 +30,11 @@ namespace synergy::gui {
QPair<QString, QString> getPair(const QString &line);
bool open(QFile &file, const QString &filePath) {
file.setFileName(filePath);
return file.open(QIODevice::ReadOnly | QIODevice::Text);
}
/**
* @brief A _very_ basic Qt .env file parser.
*
@ -38,14 +45,29 @@ QPair<QString, QString> getPair(const QString &line);
* If this function is not sufficient, replace it with a library such as:
* https://github.com/adeharo9/cpp-dotenv
*/
void dotenv(const QString &filePath) {
QFile file(filePath);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
qDebug("no .env file in current dir");
return;
void dotenv(const QString &filename) {
QString filePath = filename;
QFile file;
if (!open(file, filePath)) {
QFileInfo fileInfo(filePath);
qInfo(
"no %s file in dir: %s", qPrintable(filename),
qPrintable(fileInfo.absolutePath()));
// if nothing in current dir, then try app dir. this makes it a bit easier
// for engineers in the field to have an easily predictable location for the
// .env file.
QDir dir(QCoreApplication::applicationDirPath());
filePath = dir.filePath(filename);
if (!open(file, filePath)) {
qInfo(
"no %s file in app dir: %s", qPrintable(filename),
qPrintable(dir.absolutePath()));
return;
}
}
qDebug("loading env vars from: %s", filePath.toUtf8().constData());
qInfo("loading env vars from: %s", qPrintable(filePath));
QTextStream in(&file);
while (!in.atEnd()) {

View File

@ -21,10 +21,13 @@
namespace synergy::gui {
const QString kDefaultEnvFilename = ".env";
/**
* @param filePath Path to the .env file.
* @param envVars Somewhere to save strings for the lifetime of the app.
* @brief Loads environment variables from a .env file.
*
* First checks current dir for .env, then looks in the app dir.
*/
void dotenv(const QString &filePath);
void dotenv(const QString &filename = kDefaultEnvFilename);
} // namespace synergy::gui

View File

@ -19,7 +19,7 @@
#include "Logger.h"
#include "byte_utils.h"
#include "global/Ipc.h"
#include "common/ipc.h"
#include <QByteArray>
#include <QMutex>

View File

@ -19,7 +19,7 @@
#include "IpcReader.h"
#include "byte_utils.h"
#include "global/Ipc.h"
#include "common/ipc.h"
#include <QDataStream>
#include <QHostAddress>

View File

@ -18,6 +18,7 @@
#include "messages.h"
#include "Logger.h"
#include "common/version.h"
#include "constants.h"
#include "styles.h"
@ -64,7 +65,9 @@ void showErrorDialog(
.arg(kUrlBugReport, kColorSecondary);
}
text += QString("<pre>%3\n\n%4</pre>").arg(message, contextString);
const QString version = QString::fromStdString(synergy::version());
text +=
QString("<pre>v%1\n%2\n%3</pre>").arg(version, message, contextString);
if (type == QtFatalMsg) {
// create a blocking message box for fatal errors, as we want to wait

View File

@ -37,6 +37,7 @@ public:
virtual void remove(const QString &key);
virtual bool isWritable() const;
virtual bool contains(const QString &key) const;
virtual QString fileName() const { return m_pSettings->fileName(); }
void set(QSettings &settings) { m_pSettings = &settings; }
QSettings &get() const { return *m_pSettings; }

View File

@ -28,7 +28,6 @@ if(UNIX)
ipc
arch
base
common
mt
io
net

View File

@ -17,7 +17,7 @@
#include "ipc/IpcClient.h"
#include "base/TMethodEventJob.h"
#include "global/Ipc.h"
#include "common/ipc.h"
#include "ipc/IpcMessage.h"
#include "ipc/IpcServerProxy.h"

View File

@ -19,7 +19,7 @@
#include "base/Log.h"
#include "base/TMethodEventJob.h"
#include "global/Ipc.h"
#include "common/ipc.h"
#include "io/IStream.h"
#include "ipc/IpcMessage.h"
#include "ipc/IpcSettingMessage.h"

View File

@ -21,7 +21,7 @@
#include "arch/IArchMultithread.h"
#include "base/Event.h"
#include "base/EventTypes.h"
#include "global/Ipc.h"
#include "common/ipc.h"
namespace synergy {
class IStream;

View File

@ -24,7 +24,7 @@
#include "base/EventQueue.h"
#include "base/TMethodEventJob.h"
#include "base/TMethodJob.h"
#include "global/Ipc.h"
#include "common/ipc.h"
#include "ipc/IpcClientProxy.h"
#include "ipc/IpcMessage.h"
#include "ipc/IpcServer.h"

View File

@ -21,7 +21,7 @@
#include "arch/Arch.h"
#include "arch/IArchMultithread.h"
#include "base/ILogOutputter.h"
#include "global/Ipc.h"
#include "common/ipc.h"
#include <deque>

View File

@ -17,7 +17,7 @@
*/
#include "ipc/IpcMessage.h"
#include "global/Ipc.h"
#include "common/ipc.h"
IpcMessage::IpcMessage(IpcMessageType type) : m_type(type) {}

View File

@ -19,7 +19,7 @@
#include "base/Event.h"
#include "base/String.h"
#include "global/Ipc.h"
#include "common/ipc.h"
class IpcMessage : public EventData {
public:

View File

@ -22,7 +22,7 @@
#include "base/IEventQueue.h"
#include "base/Log.h"
#include "base/TMethodEventJob.h"
#include "global/Ipc.h"
#include "common/ipc.h"
#include "io/IStream.h"
#include "ipc/IpcClientProxy.h"
#include "ipc/IpcMessage.h"

View File

@ -20,7 +20,7 @@
#include "arch/Arch.h"
#include "base/EventTypes.h"
#include "global/Ipc.h"
#include "common/ipc.h"
#include "net/NetworkAddress.h"
#include "net/TCPListenSocket.h"

View File

@ -20,7 +20,7 @@
#include "base/Log.h"
#include "base/TMethodEventJob.h"
#include "global/Ipc.h"
#include "common/ipc.h"
#include "io/IStream.h"
#include "ipc/IpcMessage.h"
#include "synergy/ProtocolUtil.h"

View File

@ -23,7 +23,7 @@
#include "base/Log.h"
#include "base/TMethodJob.h"
#include "base/log_outputters.h"
#include "global/Ipc.h"
#include "common/ipc.h"
#include "ipc/IpcLogOutputter.h"
#include "ipc/IpcMessage.h"
#include "ipc/IpcServer.h"

View File

@ -23,7 +23,7 @@ endif()
add_library(server STATIC ${sources})
target_link_libraries(server global license)
target_link_libraries(server license)
if(UNIX)
target_link_libraries(server synlib)

View File

@ -26,8 +26,10 @@
#include "base/TMethodEventJob.h"
#include "base/XBase.h"
#include "base/log_outputters.h"
#include "common/Version.h"
#include "global/Ipc.h"
#include "common/constants.h"
#include "common/copyright.h"
#include "common/ipc.h"
#include "common/version.h"
#include "ipc/IpcMessage.h"
#include "ipc/IpcServerProxy.h"
#include "synergy/ArgsBase.h"
@ -44,6 +46,7 @@
#include <iostream>
#include <sstream>
#include <stdio.h>
#include <vector>
#if WINAPI_CARBON
#include <ApplicationServices/ApplicationServices.h>
@ -82,27 +85,17 @@ App::~App() {
}
void App::version() {
const std::string date = __DATE__;
std::string year = date.substr(date.size() - 4);
const auto version = synergy::version();
const auto copyright = synergy::copyright();
const size_t kBufferSize = 500;
const size_t kCopyrightSize = 200;
char copyrightBuffer[kCopyrightSize];
snprintf(copyrightBuffer, kCopyrightSize, kCopyright, year.c_str());
const auto kBufferLength = 1024;
std::vector<char> buffer(kBufferLength);
std::snprintf( // NOSONAR
buffer.data(), kBufferLength, "%s v%s, protocol v%d.%d\n%s", //
argsBase().m_pname, version.c_str(), kProtocolMajorVersion,
kProtocolMinorVersion, copyright.c_str());
std::stringstream version;
version << kVersion;
#ifdef GIT_SHA_SHORT
version << " (" << GIT_SHA_SHORT << ")";
#endif
char buffer[kBufferSize];
snprintf(
buffer, kBufferSize, "%s %s, protocol version %d.%d\n%s",
argsBase().m_pname, version.str().c_str(), kProtocolMajorVersion,
kProtocolMinorVersion, copyrightBuffer);
std::cout << buffer << std::endl;
std::cout << std::string(buffer.data()) << std::endl;
}
int App::run(int argc, char **argv) {

View File

@ -29,7 +29,7 @@
#include "base/TMethodJob.h"
#include "base/log_outputters.h"
#include "client/Client.h"
#include "common/Version.h"
#include "common/constants.h"
#include "mt/Thread.h"
#include "net/InverseSockets/InverseSocketFactory.h"
#include "net/NetworkAddress.h"

View File

@ -21,7 +21,7 @@
#include "base/IEventQueue.h"
#include "base/String.h"
#include "client/Client.h"
#include "common/Version.h"
#include "common/constants.h"
#include "mt/Lock.h"
//
@ -94,23 +94,23 @@ void ClientTaskBarReceiver::unlock() const {
std::string ClientTaskBarReceiver::getToolTip() const {
switch (m_state) {
case kNotRunning:
return synergy::string::sprintf("%s: Not running", kAppVersion);
return synergy::string::sprintf("%s: Not running", kAppName);
case kNotWorking:
return synergy::string::sprintf(
"%s: %s", kAppVersion, m_errorMessage.c_str());
"%s: %s", kAppName, m_errorMessage.c_str());
case kNotConnected:
return synergy::string::sprintf(
"%s: Not connected: %s", kAppVersion, m_errorMessage.c_str());
"%s: Not connected: %s", kAppName, m_errorMessage.c_str());
case kConnecting:
return synergy::string::sprintf(
"%s: Connecting to %s...", kAppVersion, m_server.c_str());
"%s: Connecting to %s...", kAppName, m_server.c_str());
case kConnected:
return synergy::string::sprintf(
"%s: Connected to %s", kAppVersion, m_server.c_str());
"%s: Connected to %s", kAppName, m_server.c_str());
default:
return "";

View File

@ -25,7 +25,7 @@
#include "base/Log.h"
#include "base/TMethodEventJob.h"
#include "base/log_outputters.h"
#include "global/Ipc.h"
#include "common/ipc.h"
#include "ipc/IpcClientProxy.h"
#include "ipc/IpcLogOutputter.h"
#include "ipc/IpcMessage.h"

View File

@ -27,7 +27,7 @@
#include "base/TMethodEventJob.h"
#include "base/TMethodJob.h"
#include "base/log_outputters.h"
#include "common/Version.h"
#include "common/constants.h"
#include "net/InverseSockets/InverseSocketFactory.h"
#include "net/SocketMultiplexer.h"
#include "net/TCPSocketFactory.h"

View File

@ -20,7 +20,7 @@
#include "arch/Arch.h"
#include "base/IEventQueue.h"
#include "base/String.h"
#include "common/Version.h"
#include "common/constants.h"
#include "mt/Lock.h"
#include "server/Server.h"
@ -97,17 +97,17 @@ void ServerTaskBarReceiver::unlock() const {
std::string ServerTaskBarReceiver::getToolTip() const {
switch (m_state) {
case kNotRunning:
return synergy::string::sprintf("%s: Not running", kAppVersion);
return synergy::string::sprintf("%s: Not running", kAppName);
case kNotWorking:
return synergy::string::sprintf(
"%s: %s", kAppVersion, m_errorMessage.c_str());
"%s: %s", kAppName, m_errorMessage.c_str());
case kNotConnected:
return synergy::string::sprintf("%s: Waiting for clients", kAppVersion);
return synergy::string::sprintf("%s: Waiting for clients", kAppName);
case kConnected:
return synergy::string::sprintf("%s: Connected", kAppVersion);
return synergy::string::sprintf("%s: Connected", kAppName);
default:
return "";

View File

@ -28,8 +28,6 @@
#include "platform/MSWindowsSession.h"
#endif
#define JSON_URL "https://symless.com/account/json/"
enum { kErrorOk, kErrorArgs, kErrorException, kErrorUnknown };
UInt32 ToolApp::run(int argc, char **argv) {

View File

@ -25,7 +25,7 @@
#include "base/IEventQueue.h"
#include "base/Log.h"
#include "base/log_outputters.h"
#include "common/Version.h"
#include "common/constants.h"
#include "platform/MSWindowsScreen.h"
#include "synergy/App.h"
#include "synergy/ArgsBase.h"

View File

@ -148,12 +148,10 @@ macro(config_test_deps)
set(test_libs
gtest
gmock
global
arch
base
client
server
common
io
net
platform

View File

@ -22,8 +22,16 @@
#include <gtest/gtest.h>
TEST(dotenv_tests, dotenv_fileDoesNotExist_doesNotLoadEnvVar) {
const QString envFile = "tmp/test/.env";
synergy::gui::dotenv(envFile);
const QString actualValue = qEnvironmentVariable("TEST_ENV_VAR");
EXPECT_TRUE(actualValue.isEmpty());
}
TEST(dotenv_tests, dotenv_envFileWithEntry_loadsEnvVar) {
// Arrange
const QString envFile = "tmp/test/.env";
QFile file(envFile);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
@ -42,7 +50,7 @@ TEST(dotenv_tests, dotenv_envFileWithEntry_loadsEnvVar) {
synergy::gui::dotenv(envFile);
const QString actualValue = qgetenv(key.toUtf8().constData());
const QString actualValue = qEnvironmentVariable(qPrintable(key));
EXPECT_EQ("test value", actualValue.toStdString());
QFile::remove(envFile);

View File

@ -27,7 +27,7 @@
#include "base/String.h"
#include "base/TMethodEventJob.h"
#include "base/TMethodJob.h"
#include "global/Ipc.h"
#include "common/ipc.h"
#include "ipc/IpcClient.h"
#include "ipc/IpcClientProxy.h"
#include "ipc/IpcMessage.h"

View File

@ -0,0 +1,58 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2024 Symless Ltd.
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file LICENSE that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "gui/Logger.h"
#include "gmock/gmock.h"
#include <gtest/gtest.h>
using namespace testing;
using namespace synergy::gui;
TEST(LoggerTests, handleMessage_withDebugEnvVarOn_emitsNewLine) {
Logger logger;
std::string newLineEmitted;
QObject::connect(
&logger, &Logger::newLine, //
[&newLineEmitted](const QString &line) {
newLineEmitted = line.toStdString();
});
qputenv("SYNERGY_GUI_DEBUG", "true");
logger.loadEnvVars();
logger.handleMessage(QtDebugMsg, QMessageLogContext(), "test");
EXPECT_THAT(newLineEmitted, HasSubstr("test"));
qputenv("SYNERGY_GUI_DEBUG", "");
}
TEST(LoggerTests, handleMessage_withDebugEnvVarOff_doesNotEmitNewLine) {
Logger logger;
bool newLineEmitted = false;
QObject::connect(
&logger, &Logger::newLine, //
[&newLineEmitted](const QString &line) { newLineEmitted = true; });
qputenv("SYNERGY_GUI_DEBUG", "false");
logger.loadEnvVars();
logger.handleMessage(QtDebugMsg, QMessageLogContext(), "test");
EXPECT_FALSE(newLineEmitted);
qputenv("SYNERGY_GUI_DEBUG", "");
}