fixed update icon & green indicator - windows

This commit is contained in:
dranik
2026-05-29 18:30:23 +03:00
parent 6fad9f56e4
commit 2c37305cf0
4 changed files with 177 additions and 47 deletions
+13
View File
@@ -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()
+111 -41
View File
@@ -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");
}
} }
+3
View File
@@ -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
} }