mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-06-24 02:00:24 +07:00
fixed update icon & green indicator - windows
This commit is contained in:
@@ -267,6 +267,7 @@ if(WIN32 OR (APPLE AND NOT IOS AND NOT MACOS_NE) OR (LINUX AND NOT ANDROID))
|
|||||||
set(HEADERS ${HEADERS}
|
set(HEADERS ${HEADERS}
|
||||||
${CLIENT_ROOT_DIR}/core/utils/ipcClient.h
|
${CLIENT_ROOT_DIR}/core/utils/ipcClient.h
|
||||||
${CLIENT_ROOT_DIR}/ui/utils/systemTrayNotificationHandler.h
|
${CLIENT_ROOT_DIR}/ui/utils/systemTrayNotificationHandler.h
|
||||||
|
${CLIENT_ROOT_DIR}/ui/utils/platformTheme.h
|
||||||
${CLIENT_ROOT_DIR}/core/protocols/openVpnProtocol.h
|
${CLIENT_ROOT_DIR}/core/protocols/openVpnProtocol.h
|
||||||
${CLIENT_ROOT_DIR}/core/protocols/wireGuardProtocol.h
|
${CLIENT_ROOT_DIR}/core/protocols/wireGuardProtocol.h
|
||||||
${CLIENT_ROOT_DIR}/core/protocols/xrayProtocol.h
|
${CLIENT_ROOT_DIR}/core/protocols/xrayProtocol.h
|
||||||
@@ -278,20 +279,32 @@ if(WIN32 OR (APPLE AND NOT IOS AND NOT MACOS_NE) OR (LINUX AND NOT ANDROID))
|
|||||||
${CLIENT_ROOT_DIR}/core/utils/ipcClient.cpp
|
${CLIENT_ROOT_DIR}/core/utils/ipcClient.cpp
|
||||||
${CLIENT_ROOT_DIR}/mozilla/localsocketcontroller.cpp
|
${CLIENT_ROOT_DIR}/mozilla/localsocketcontroller.cpp
|
||||||
${CLIENT_ROOT_DIR}/ui/utils/systemTrayNotificationHandler.cpp
|
${CLIENT_ROOT_DIR}/ui/utils/systemTrayNotificationHandler.cpp
|
||||||
|
${CLIENT_ROOT_DIR}/ui/utils/platformTheme.cpp
|
||||||
${CLIENT_ROOT_DIR}/core/protocols/openVpnProtocol.cpp
|
${CLIENT_ROOT_DIR}/core/protocols/openVpnProtocol.cpp
|
||||||
${CLIENT_ROOT_DIR}/core/protocols/wireGuardProtocol.cpp
|
${CLIENT_ROOT_DIR}/core/protocols/wireGuardProtocol.cpp
|
||||||
${CLIENT_ROOT_DIR}/core/protocols/xrayProtocol.cpp
|
${CLIENT_ROOT_DIR}/core/protocols/xrayProtocol.cpp
|
||||||
${CLIENT_ROOT_DIR}/core/protocols/awgProtocol.cpp
|
${CLIENT_ROOT_DIR}/core/protocols/awgProtocol.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
|
set(HEADERS ${HEADERS}
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/windows/windowsutils.h
|
||||||
|
)
|
||||||
|
set(SOURCES ${SOURCES}
|
||||||
|
${CLIENT_ROOT_DIR}/platforms/windows/windowsutils.cpp
|
||||||
|
)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(APPLE AND MACOS_NE)
|
if(APPLE AND MACOS_NE)
|
||||||
# Include only the tray notification handler in NE builds
|
# Include only the tray notification handler in NE builds
|
||||||
set(HEADERS ${HEADERS}
|
set(HEADERS ${HEADERS}
|
||||||
${CLIENT_ROOT_DIR}/ui/utils/systemTrayNotificationHandler.h
|
${CLIENT_ROOT_DIR}/ui/utils/systemTrayNotificationHandler.h
|
||||||
|
${CLIENT_ROOT_DIR}/ui/utils/platformTheme.h
|
||||||
)
|
)
|
||||||
|
|
||||||
set(SOURCES ${SOURCES}
|
set(SOURCES ${SOURCES}
|
||||||
${CLIENT_ROOT_DIR}/ui/utils/systemTrayNotificationHandler.cpp
|
${CLIENT_ROOT_DIR}/ui/utils/systemTrayNotificationHandler.cpp
|
||||||
|
${CLIENT_ROOT_DIR}/ui/utils/platformTheme.cpp
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -12,65 +12,135 @@
|
|||||||
|
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
|
|
||||||
namespace {
|
namespace
|
||||||
Logger logger("WindowsUtils");
|
{
|
||||||
} // namespace
|
Logger logger("WindowsUtils");
|
||||||
|
|
||||||
constexpr const int WINDOWS_11_BUILD =
|
constexpr const wchar_t kThemeWatcherClassName[] = L"AmneziaVpnThemeWatcher";
|
||||||
22000; // Build Number of the first release win 11 iso
|
|
||||||
|
|
||||||
QString WindowsUtils::getErrorMessage(quint32 code) {
|
struct ThemeObserverState
|
||||||
LPSTR messageBuffer = nullptr;
|
{
|
||||||
size_t size = FormatMessageA(
|
std::function<void()> callback;
|
||||||
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
|
HWND hwnd = nullptr;
|
||||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
};
|
||||||
nullptr, code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
|
||||||
(LPSTR)&messageBuffer, 0, nullptr);
|
|
||||||
|
|
||||||
std::string message(messageBuffer, size);
|
ThemeObserverState g_themeObserver;
|
||||||
QString result(message.c_str());
|
|
||||||
LocalFree(messageBuffer);
|
bool registryUsesDarkTheme(const QSettings &settings, const QString &lightThemeKey)
|
||||||
return result;
|
{
|
||||||
|
if (settings.contains(lightThemeKey)) {
|
||||||
|
return settings.value(lightThemeKey).toInt() != 1;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LRESULT CALLBACK themeWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||||
|
{
|
||||||
|
Q_UNUSED(wParam);
|
||||||
|
|
||||||
|
if (msg == WM_SETTINGCHANGE && lParam != 0) {
|
||||||
|
const wchar_t *section = reinterpret_cast<const wchar_t *>(lParam);
|
||||||
|
if (wcscmp(section, L"ImmersiveColorSet") == 0 || wcscmp(section, L"WindowsThemeElement") == 0) {
|
||||||
|
if (g_themeObserver.callback) {
|
||||||
|
g_themeObserver.callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return DefWindowProcW(hwnd, msg, wParam, lParam);
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
constexpr const int WINDOWS_11_BUILD = 22000; // Build Number of the first release win 11 iso
|
||||||
|
|
||||||
|
QString WindowsUtils::getErrorMessage(quint32 code)
|
||||||
|
{
|
||||||
|
LPSTR messageBuffer = nullptr;
|
||||||
|
size_t size =
|
||||||
|
FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||||
|
nullptr, code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, nullptr);
|
||||||
|
|
||||||
|
std::string message(messageBuffer, size);
|
||||||
|
QString result(message.c_str());
|
||||||
|
LocalFree(messageBuffer);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString WindowsUtils::getErrorMessage() {
|
QString WindowsUtils::getErrorMessage()
|
||||||
return getErrorMessage(GetLastError());
|
{
|
||||||
|
return getErrorMessage(GetLastError());
|
||||||
}
|
}
|
||||||
|
|
||||||
// A simple function to log windows error messages.
|
// A simple function to log windows error messages.
|
||||||
void WindowsUtils::windowsLog(const QString& msg) {
|
void WindowsUtils::windowsLog(const QString &msg)
|
||||||
QString errmsg = getErrorMessage();
|
{
|
||||||
logger.error() << msg << "-" << errmsg;
|
QString errmsg = getErrorMessage();
|
||||||
|
logger.error() << msg << "-" << errmsg;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Static
|
// Static
|
||||||
QString WindowsUtils::windowsVersion() {
|
QString WindowsUtils::windowsVersion()
|
||||||
QSettings regCurrentVersion(
|
{
|
||||||
"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
|
QSettings regCurrentVersion("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
|
||||||
QSettings::NativeFormat);
|
QSettings::NativeFormat);
|
||||||
|
|
||||||
int buildNr = regCurrentVersion.value("CurrentBuild").toInt();
|
int buildNr = regCurrentVersion.value("CurrentBuild").toInt();
|
||||||
if (buildNr >= WINDOWS_11_BUILD) {
|
if (buildNr >= WINDOWS_11_BUILD) {
|
||||||
return "11";
|
return "11";
|
||||||
}
|
}
|
||||||
return QSysInfo::productVersion();
|
return QSysInfo::productVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
void WindowsUtils::forceCrash() {
|
void WindowsUtils::forceCrash()
|
||||||
RaiseException(0x0000DEAD, EXCEPTION_NONCONTINUABLE, 0, NULL);
|
{
|
||||||
|
RaiseException(0x0000DEAD, EXCEPTION_NONCONTINUABLE, 0, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
bool WindowsUtils::isDarkTheme() {
|
bool WindowsUtils::isDarkTheme()
|
||||||
QSettings settings(
|
{
|
||||||
QStringLiteral("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"),
|
QSettings settings(
|
||||||
QSettings::NativeFormat);
|
QStringLiteral("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"),
|
||||||
|
QSettings::NativeFormat);
|
||||||
|
|
||||||
if (settings.contains(QStringLiteral("AppsUseLightTheme"))) {
|
if (settings.contains(QStringLiteral("SystemUsesLightTheme"))) {
|
||||||
return settings.value(QStringLiteral("AppsUseLightTheme")).toInt() != 1;
|
return registryUsesDarkTheme(settings, QStringLiteral("SystemUsesLightTheme"));
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.warning() << "AppsUseLightTheme registry key is unavailable; assuming dark theme";
|
if (settings.contains(QStringLiteral("AppsUseLightTheme"))) {
|
||||||
return true;
|
return registryUsesDarkTheme(settings, QStringLiteral("AppsUseLightTheme"));
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.warning() << "SystemUsesLightTheme registry key is unavailable; assuming dark theme";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowsUtils::installThemeChangeObserver(std::function<void()> callback)
|
||||||
|
{
|
||||||
|
g_themeObserver.callback = std::move(callback);
|
||||||
|
|
||||||
|
if (g_themeObserver.hwnd) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
HINSTANCE instance = GetModuleHandleW(nullptr);
|
||||||
|
WNDCLASSW wc = {};
|
||||||
|
wc.lpfnWndProc = themeWndProc;
|
||||||
|
wc.hInstance = instance;
|
||||||
|
wc.lpszClassName = kThemeWatcherClassName;
|
||||||
|
|
||||||
|
WNDCLASSW existing = {};
|
||||||
|
if (!GetClassInfoW(instance, kThemeWatcherClassName, &existing)) {
|
||||||
|
if (!RegisterClassW(&wc)) {
|
||||||
|
WindowsUtils::windowsLog("Failed to register theme watcher window class");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g_themeObserver.hwnd = CreateWindowExW(0, kThemeWatcherClassName, L"AmneziaVpnThemeWatcher", 0, 0, 0, 0, 0,
|
||||||
|
HWND_MESSAGE, nullptr, instance, nullptr);
|
||||||
|
if (!g_themeObserver.hwnd) {
|
||||||
|
WindowsUtils::windowsLog("Failed to create theme watcher window");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
class WindowsUtils final {
|
class WindowsUtils final {
|
||||||
public:
|
public:
|
||||||
static QString getErrorMessage();
|
static QString getErrorMessage();
|
||||||
@@ -20,6 +22,7 @@ class WindowsUtils final {
|
|||||||
static void forceCrash();
|
static void forceCrash();
|
||||||
|
|
||||||
static bool isDarkTheme();
|
static bool isDarkTheme();
|
||||||
|
static void installThemeChangeObserver(std::function<void()> callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // WINDOWSUTILS_H
|
#endif // WINDOWSUTILS_H
|
||||||
|
|||||||
@@ -23,6 +23,10 @@
|
|||||||
# include "platforms/macos/macosstatusicon.h"
|
# include "platforms/macos/macosstatusicon.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(Q_OS_WIN)
|
||||||
|
# include "platforms/windows/windowsutils.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@@ -71,6 +75,28 @@ QPixmap renderTrayTemplate(const QString &resourcePath, qreal opacity)
|
|||||||
return pixmap;
|
return pixmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QPixmap colorizeTrayTemplate(const QPixmap &mask, const QColor &foreground)
|
||||||
|
{
|
||||||
|
QPixmap result(kTrayIconSize, kTrayIconSize);
|
||||||
|
result.fill(Qt::transparent);
|
||||||
|
|
||||||
|
QPainter painter(&result);
|
||||||
|
painter.fillRect(result.rect(), foreground);
|
||||||
|
painter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
|
||||||
|
painter.drawPixmap(0, 0, mask);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void drawStatusIndicator(QPainter &painter, const QColor &color)
|
||||||
|
{
|
||||||
|
const qreal dotSize = kTrayIconSize * 0.35;
|
||||||
|
const qreal dotOrigin = (kTrayIconSize - dotSize) * 0.8;
|
||||||
|
|
||||||
|
painter.setPen(Qt::NoPen);
|
||||||
|
painter.setBrush(color);
|
||||||
|
painter.drawEllipse(QRectF(dotOrigin, dotOrigin, dotSize, dotSize));
|
||||||
|
}
|
||||||
|
|
||||||
QByteArray renderTrayTemplatePng(qreal opacity)
|
QByteArray renderTrayTemplatePng(qreal opacity)
|
||||||
{
|
{
|
||||||
const QPixmap pixmap = renderTrayTemplate(QString::fromLatin1(kTrayTemplateIconPath), opacity);
|
const QPixmap pixmap = renderTrayTemplate(QString::fromLatin1(kTrayTemplateIconPath), opacity);
|
||||||
@@ -82,13 +108,19 @@ QByteArray renderTrayTemplatePng(qreal opacity)
|
|||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
QIcon buildTrayIcon(qreal opacity)
|
QIcon buildTrayIcon(qreal opacity, bool darkTheme, const QColor &indicatorColor)
|
||||||
{
|
{
|
||||||
const QPixmap pixmap = renderTrayTemplate(QString::fromLatin1(kTrayTemplateIconPath), opacity);
|
const QPixmap mask = renderTrayTemplate(QString::fromLatin1(kTrayTemplateIconPath), opacity);
|
||||||
|
const QColor foreground = darkTheme ? Qt::white : Qt::black;
|
||||||
|
QPixmap pixmap = colorizeTrayTemplate(mask, foreground);
|
||||||
|
|
||||||
|
if (indicatorColor.isValid()) {
|
||||||
|
QPainter painter(&pixmap);
|
||||||
|
drawStatusIndicator(painter, indicatorColor);
|
||||||
|
}
|
||||||
|
|
||||||
QIcon icon;
|
QIcon icon;
|
||||||
icon.addPixmap(pixmap);
|
icon.addPixmap(pixmap);
|
||||||
icon.setIsMask(true);
|
|
||||||
return icon;
|
return icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,7 +138,6 @@ SystemTrayNotificationHandler::SystemTrayNotificationHandler(QObject* parent) :
|
|||||||
m_macStatusIcon->setToolTip(APPLICATION_NAME);
|
m_macStatusIcon->setToolTip(APPLICATION_NAME);
|
||||||
// Template NSStatusItem icons follow the menu bar appearance automatically.
|
// Template NSStatusItem icons follow the menu bar appearance automatically.
|
||||||
#else
|
#else
|
||||||
m_systemTrayIcon.show();
|
|
||||||
connect(&m_systemTrayIcon, &QSystemTrayIcon::activated, this, &SystemTrayNotificationHandler::onTrayActivated);
|
connect(&m_systemTrayIcon, &QSystemTrayIcon::activated, this, &SystemTrayNotificationHandler::onTrayActivated);
|
||||||
m_systemTrayIcon.setContextMenu(&m_menu);
|
m_systemTrayIcon.setContextMenu(&m_menu);
|
||||||
#endif
|
#endif
|
||||||
@@ -138,10 +169,20 @@ SystemTrayNotificationHandler::SystemTrayNotificationHandler(QObject* parent) :
|
|||||||
qApp->installEventFilter(new TrayThemeChangeFilter([this]() {
|
qApp->installEventFilter(new TrayThemeChangeFilter([this]() {
|
||||||
refreshTheme();
|
refreshTheme();
|
||||||
}, this));
|
}, this));
|
||||||
|
|
||||||
|
#if defined(Q_OS_WIN)
|
||||||
|
WindowsUtils::installThemeChangeObserver([this]() {
|
||||||
|
refreshTheme();
|
||||||
|
});
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
refreshTheme();
|
refreshTheme();
|
||||||
setTrayState(Vpn::ConnectionState::Disconnected);
|
setTrayState(Vpn::ConnectionState::Disconnected);
|
||||||
|
|
||||||
|
#if !defined(Q_OS_MAC) || defined(MACOS_NE)
|
||||||
|
m_systemTrayIcon.show();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
SystemTrayNotificationHandler::~SystemTrayNotificationHandler() {
|
SystemTrayNotificationHandler::~SystemTrayNotificationHandler() {
|
||||||
@@ -220,7 +261,7 @@ void SystemTrayNotificationHandler::updateTrayIcon()
|
|||||||
m_macStatusIcon->setIconFromData(renderTrayTemplatePng(opacity));
|
m_macStatusIcon->setIconFromData(renderTrayTemplatePng(opacity));
|
||||||
m_macStatusIcon->setIndicatorColor(trayIndicatorColorForState(m_trayState));
|
m_macStatusIcon->setIndicatorColor(trayIndicatorColorForState(m_trayState));
|
||||||
#else
|
#else
|
||||||
m_systemTrayIcon.setIcon(buildTrayIcon(opacity));
|
m_systemTrayIcon.setIcon(buildTrayIcon(opacity, m_isDarkTheme, trayIndicatorColorForState(m_trayState)));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -292,7 +333,10 @@ void SystemTrayNotificationHandler::notify(NotificationHandler::Message type,
|
|||||||
Q_ASSERT(m_macStatusIcon);
|
Q_ASSERT(m_macStatusIcon);
|
||||||
m_macStatusIcon->showMessage(title, message);
|
m_macStatusIcon->showMessage(title, message);
|
||||||
#else
|
#else
|
||||||
m_systemTrayIcon.showMessage(title, message, buildTrayIcon(kConnectedTrayOpacity), timerMsec);
|
m_systemTrayIcon.showMessage(
|
||||||
|
title, message,
|
||||||
|
buildTrayIcon(kConnectedTrayOpacity, m_isDarkTheme, trayIndicatorColorForState(Vpn::ConnectionState::Connected)),
|
||||||
|
timerMsec);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user