diff --git a/.env.example b/.env.example index ef5b56cbb..ffce9a2df 100644 --- a/.env.example +++ b/.env.example @@ -7,28 +7,28 @@ # # Serial key value to pre-fill the activation dialog -# SYNERGY_TEST_SERIAL_KEY="DEADBEEF" +# DESKFLOW_TEST_SERIAL_KEY="DEADBEEF" # Shows the test menu in the GUI (on by default in debug mode) -# SYNERGY_TEST_MENU=true +# DESKFLOW_TEST_MENU=true # Version checker URL to use (useful for testing) -# SYNERGY_VERSION_URL="http://localhost:8787?version=v1" +# DESKFLOW_VERSION_URL="https://api.deskflow.org/version?fake=1.100.0" # Enable debug logging in the GUI (on by default in debug mode) -# SYNERGY_GUI_DEBUG=true +# DESKFLOW_GUI_DEBUG=true # Enable verbose logging in the GUI (always off by default) -# SYNERGY_GUI_VERBOSE=true +# DESKFLOW_GUI_VERBOSE=true # Reset all settings and delete all data on startup -# SYNERGY_RESET_ALL=true +# DESKFLOW_RESET_ALL=true # Enable the activation dialog (used for testing) -# SYNERGY_ENABLE_ACTIVATION=true +# DESKFLOW_ENABLE_ACTIVATION=true # Show licensed product menu items, etc (used for testing) -# SYNERGY_LICENSED_PRODUCT=true +# DESKFLOW_LICENSED_PRODUCT=true # # Build diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 495ee0cb6..0ebcf63bd 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -67,9 +67,9 @@ { "label": "kill", "type": "shell", - "command": "killall synergy; killall synergyc; killall synergys || true", + "command": "killall deskflow; killall deskflowc; killall deskflows || true", "windows": { - "command": "taskkill /F /IM synergy.exe /IM synergyc.exe /IM synergys.exe; $true" + "command": "taskkill /F /IM deskflow.exe /IM deskflowc.exe /IM deskflows.exe; $true" }, "presentation": { "reveal": "silent" @@ -83,11 +83,11 @@ { "label": "gui", "type": "process", - "command": "${workspaceFolder}/build/bin/synergy", + "command": "${workspaceFolder}/build/bin/deskflow", "dependsOn": ["build", "kill"], "problemMatcher": [], "windows": { - "command": "${workspaceFolder}/build/bin/synergy.exe" + "command": "${workspaceFolder}/build/bin/deskflow.exe" } }, { diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 61dad7234..18f863e50 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -570,7 +570,15 @@ void MainWindow::open() { showAndActivate(); } - m_VersionChecker.checkLatest(); + if (!m_AppConfig.enableUpdateCheck().has_value()) { + m_AppConfig.setEnableUpdateCheck(messages::showUpdateCheckOption(this)); + } + + if (m_AppConfig.enableUpdateCheck().value()) { + m_VersionChecker.checkLatest(); + } else { + qDebug("update check disabled"); + } if (m_AppConfig.startedBefore()) { m_CoreProcess.start(); diff --git a/src/lib/gui/VersionChecker.cpp b/src/lib/gui/VersionChecker.cpp index e2cf52ee8..189ca3aea 100644 --- a/src/lib/gui/VersionChecker.cpp +++ b/src/lib/gui/VersionChecker.cpp @@ -17,6 +17,8 @@ #include "VersionChecker.h" +#include "env_vars.h" + #include #include #include @@ -27,17 +29,21 @@ const char *const kVersion = DESKFLOW_VERSION; -VersionChecker::VersionChecker(std::shared_ptr network) +using namespace deskflow::gui; + +VersionChecker::VersionChecker( + std::shared_ptr network) : m_network( - network ? network : std::make_shared(this)) { + network ? network : std::make_shared()) { + m_network->init(); connect( - m_network.get(), SIGNAL(finished(QNetworkReply *)), this, - SLOT(replyFinished(QNetworkReply *))); + m_network.get(), &QNetworkAccessManagerProxy::finished, this, + &VersionChecker::replyFinished); } void VersionChecker::checkLatest() const { - const QString url = - qEnvironmentVariable("DESKFLOW_VERSION_URL", DESKFLOW_VERSION_URL); + const QString url = env_vars::versionUrl(); + qDebug("checking for updates at: %s", qPrintable(url)); auto request = QNetworkRequest(url); auto userAgent = QString("Deskflow %1 on %2") .arg(kVersion) @@ -53,7 +59,10 @@ void VersionChecker::replyFinished(QNetworkReply *reply) { auto newestVersion = QString(reply->readAll()); if (!newestVersion.isEmpty() && compareVersions(DESKFLOW_VERSION, newestVersion) > 0) { + qDebug("update found: %s", qPrintable(newestVersion)); emit updateFound(newestVersion); + } else { + qDebug("no updates found"); } } diff --git a/src/lib/gui/VersionChecker.h b/src/lib/gui/VersionChecker.h index 8199d7039..f99e32dc5 100644 --- a/src/lib/gui/VersionChecker.h +++ b/src/lib/gui/VersionChecker.h @@ -17,6 +17,8 @@ #pragma once +#include "proxy/QNetworkAccessManagerProxy.h" + #include #include #include @@ -26,13 +28,16 @@ class QNetworkReply; class VersionCheckerTests; class VersionChecker : public QObject { + using QNetworkAccessManagerProxy = + deskflow::gui::proxy::QNetworkAccessManagerProxy; + Q_OBJECT friend class VersionCheckerTests; public: explicit VersionChecker( - std::shared_ptr network = nullptr); + std::shared_ptr network = nullptr); void checkLatest() const; public slots: void replyFinished(QNetworkReply *reply); @@ -50,5 +55,5 @@ private: */ static int getStageVersion(QString stage); - std::shared_ptr m_network; + std::shared_ptr m_network; }; diff --git a/src/lib/gui/config/AppConfig.cpp b/src/lib/gui/config/AppConfig.cpp index 67fc99572..e22ef1718 100644 --- a/src/lib/gui/config/AppConfig.cpp +++ b/src/lib/gui/config/AppConfig.cpp @@ -94,6 +94,7 @@ const char *const AppConfig::m_SettingsName[] = { "mainWindowPosition", "showDevThanks", "showCloseReminder", + "enableUpdateCheck", }; AppConfig::AppConfig( @@ -175,6 +176,8 @@ void AppConfig::recallFromCurrentScope() { getFromCurrentScope(kShowDevThanks, m_ShowDevThanks).toBool(); m_ShowCloseReminder = getFromCurrentScope(kShowCloseReminder, m_ShowCloseReminder).toBool(); + m_EnableUpdateCheck = getFromCurrentScope( + kEnableUpdateCheck, [](const QVariant &v) { return v.toBool(); }); } void AppConfig::recallScreenName() { @@ -231,11 +234,11 @@ void AppConfig::commit() { setInCurrentScope(kInvertConnection, m_InvertConnection); setInCurrentScope(kEnableService, m_EnableService); setInCurrentScope(kCloseToTray, m_CloseToTray); - setInCurrentScope(kShowDevThanks, m_ShowDevThanks); - setInCurrentScope(kShowCloseReminder, m_ShowCloseReminder); - setInCurrentScope(kMainWindowSize, m_MainWindowSize); setInCurrentScope(kMainWindowPosition, m_MainWindowPosition); + setInCurrentScope(kShowDevThanks, m_ShowDevThanks); + setInCurrentScope(kShowCloseReminder, m_ShowCloseReminder); + setInCurrentScope(kEnableUpdateCheck, m_EnableUpdateCheck); } if (m_TlsChanged) { @@ -535,6 +538,10 @@ bool AppConfig::showDevThanks() const { return m_ShowDevThanks; } bool AppConfig::showCloseReminder() const { return m_ShowCloseReminder; } +std::optional AppConfig::enableUpdateCheck() const { + return m_EnableUpdateCheck; +} + /////////////////////////////////////////////////////////////////////////////// // End getters /////////////////////////////////////////////////////////////////////////////// @@ -661,6 +668,10 @@ void AppConfig::setShowCloseReminder(bool value) { m_ShowCloseReminder = value; } +void AppConfig::setEnableUpdateCheck(bool value) { + m_EnableUpdateCheck = value; +} + /////////////////////////////////////////////////////////////////////////////// // End setters /////////////////////////////////////////////////////////////////////////////// diff --git a/src/lib/gui/config/AppConfig.h b/src/lib/gui/config/AppConfig.h index f693be5ff..b99f38bb6 100644 --- a/src/lib/gui/config/AppConfig.h +++ b/src/lib/gui/config/AppConfig.h @@ -106,6 +106,7 @@ private: kMainWindowPosition = 40, kShowDevThanks = 41, kShowCloseReminder = 42, + kEnableUpdateCheck = 43, }; public: @@ -180,6 +181,7 @@ public: std::optional mainWindowPosition() const; bool showDevThanks() const; bool showCloseReminder() const; + std::optional enableUpdateCheck() const; // // Setters (overrides) @@ -224,6 +226,7 @@ public: void setMainWindowPosition(const QPoint &position); void setShowDevThanks(bool show); void setShowCloseReminder(bool show); + void setEnableUpdateCheck(bool value); /// @brief Sets the user preference to load from SystemScope. /// @param [in] value @@ -332,6 +335,7 @@ private: bool m_ShowDevThanks = !deskflow::gui::license::isLicensedProduct(); bool m_LoadFromSystemScope = false; bool m_ShowCloseReminder = true; + std::optional m_EnableUpdateCheck; /** * @brief Flag is set when any TLS is setting is changed, and is reset diff --git a/src/lib/gui/env_vars.h b/src/lib/gui/env_vars.h new file mode 100644 index 000000000..f91f03d8e --- /dev/null +++ b/src/lib/gui/env_vars.h @@ -0,0 +1,29 @@ +/* + * Deskflow -- 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 . + */ + +#pragma once + +#include +#include + +namespace deskflow::gui::env_vars { + +inline QString versionUrl() { + return qEnvironmentVariable("DESKFLOW_VERSION_URL", DESKFLOW_VERSION_URL); +} + +} // namespace deskflow::gui::env_vars diff --git a/src/lib/gui/messages.cpp b/src/lib/gui/messages.cpp index 97f05790b..2070ecb36 100644 --- a/src/lib/gui/messages.cpp +++ b/src/lib/gui/messages.cpp @@ -18,8 +18,10 @@ #include "messages.h" #include "Logger.h" +#include "common/constants.h" #include "common/version.h" #include "constants.h" +#include "env_vars.h" #include "gui/license/license_utils.h" #include "styles.h" @@ -314,4 +316,19 @@ void showWaylandLibraryError(QWidget *parent) { .arg(kUrlBugReport, kColorSecondary)); } +bool showUpdateCheckOption(QWidget *parent) { + QMessageBox message(parent); + message.addButton(QObject::tr("Close"), QMessageBox::RejectRole); + const auto checkButton = message.addButton( + QObject::tr("Check for updates"), QMessageBox::AcceptRole); + message.setText( + QString("

Would you like to check for updates when %1 starts?

" + "

Checking for updates requires an Internet connection.

" + "

URL:

%2

") + .arg(kAppName, env_vars::versionUrl())); + + message.exec(); + return message.clickedButton() == checkButton; +} + } // namespace deskflow::gui::messages diff --git a/src/lib/gui/messages.h b/src/lib/gui/messages.h index 4fa8d0ae1..ee42c9108 100644 --- a/src/lib/gui/messages.h +++ b/src/lib/gui/messages.h @@ -56,4 +56,6 @@ void showWaylandExperimental(QWidget *parent); void showWaylandLibraryError(QWidget *parent); +bool showUpdateCheckOption(QWidget *parent); + } // namespace deskflow::gui::messages diff --git a/src/lib/gui/proxy/QNetworkAccessManagerProxy.cpp b/src/lib/gui/proxy/QNetworkAccessManagerProxy.cpp new file mode 100644 index 000000000..d49086ac3 --- /dev/null +++ b/src/lib/gui/proxy/QNetworkAccessManagerProxy.cpp @@ -0,0 +1,37 @@ +/* + * Deskflow -- 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 . + */ + +#include "QNetworkAccessManagerProxy.h" + +#include +#include + +namespace deskflow::gui::proxy { + +void QNetworkAccessManagerProxy::init() { + m_network = std::make_shared(); + + connect( + m_network.get(), &QNetworkAccessManager::finished, this, + [this](QNetworkReply *reply) { emit finished(reply); }); +} + +void QNetworkAccessManagerProxy::get(const QNetworkRequest &request) const { + m_network->get(request); +} + +} // namespace deskflow::gui::proxy diff --git a/src/lib/gui/proxy/QNetworkAccessManagerProxy.h b/src/lib/gui/proxy/QNetworkAccessManagerProxy.h new file mode 100644 index 000000000..78df4c938 --- /dev/null +++ b/src/lib/gui/proxy/QNetworkAccessManagerProxy.h @@ -0,0 +1,39 @@ +/* + * Deskflow -- 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 . + */ + +#pragma once + +#include +#include + +namespace deskflow::gui::proxy { + +class QNetworkAccessManagerProxy : public QObject { + Q_OBJECT + +public: + virtual void init(); + virtual void get(const QNetworkRequest &request) const; + +signals: + void finished(QNetworkReply *reply); + +private: + std::shared_ptr m_network; +}; + +} // namespace deskflow::gui::proxy diff --git a/src/test/unittests/gui/VersionCheckerTests.cpp b/src/test/unittests/gui/VersionCheckerTests.cpp index bd850a5ba..e3505e12f 100644 --- a/src/test/unittests/gui/VersionCheckerTests.cpp +++ b/src/test/unittests/gui/VersionCheckerTests.cpp @@ -16,9 +16,14 @@ */ #include "gui/VersionChecker.h" +#include "shared/gui/TestQtCoreApp.h" +#include #include +using namespace deskflow::gui::proxy; +using namespace testing; + class VersionCheckerTests : public ::testing::Test { protected: int compareVersions(const QString &left, const QString &right) { @@ -26,6 +31,22 @@ protected: } }; +class MockNetworkAccessManager : public QNetworkAccessManagerProxy { +public: + MOCK_METHOD(void, init, (), (override)); + MOCK_METHOD(void, get, (const QNetworkRequest &request), (const, override)); +}; + +TEST_F(VersionCheckerTests, checkLatest_callsNetworkGet) { + TestQtCoreApp app; + const auto network = std::make_shared>(); + const VersionChecker checker(network); + + EXPECT_CALL(*network, get(testing::_)).Times(1); + + checker.checkLatest(); +} + TEST_F(VersionCheckerTests, compareVersions_major_isValid) { EXPECT_EQ(compareVersions("1.0.0", "2.0.0"), 1); EXPECT_EQ(compareVersions("2.0.0", "1.0.0"), -1);