File indexing completed on 2024-04-21 04:56:47

0001 /*
0002  * SPDX-FileCopyrightText: 2019 Weixuan XIAO <veyx.shaw@gmail.com>
0003  *
0004  * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005  */
0006 
0007 #include <QDebug>
0008 #include <QFile>
0009 #include <QIcon>
0010 #include <QSettings>
0011 #include <QStandardPaths>
0012 
0013 #include <iostream>
0014 
0015 #include <Windows.h>
0016 #include <tlhelp32.h>
0017 
0018 #include <winrt/Windows.Foundation.Collections.h>
0019 #include <winrt/Windows.UI.ViewManagement.h>
0020 
0021 #include "indicator_debug.h"
0022 #include "indicatorhelper.h"
0023 
0024 winrt::Windows::UI::ViewManagement::UISettings uiSettings;
0025 
0026 IndicatorHelper::IndicatorHelper(const QUrl &indicatorUrl)
0027     : m_indicatorUrl(indicatorUrl)
0028 {
0029     uiSettings = winrt::Windows::UI::ViewManagement::UISettings();
0030 }
0031 
0032 IndicatorHelper::~IndicatorHelper()
0033 {
0034     this->terminateProcess(processes::dbus_daemon, m_indicatorUrl);
0035     this->terminateProcess(processes::kdeconnect_app, m_indicatorUrl);
0036     this->terminateProcess(processes::kdeconnect_handler, m_indicatorUrl);
0037     this->terminateProcess(processes::kdeconnect_settings, m_indicatorUrl);
0038     this->terminateProcess(processes::kdeconnect_sms, m_indicatorUrl);
0039     this->terminateProcess(processes::kdeconnect_daemon, m_indicatorUrl);
0040 }
0041 
0042 void IndicatorHelper::preInit()
0043 {
0044 }
0045 
0046 void IndicatorHelper::postInit()
0047 {
0048 }
0049 
0050 void IndicatorHelper::iconPathHook()
0051 {
0052     // FIXME: This doesn't seem to be enough for QIcon::fromTheme to find the icons, so we still have to use the full path when setting the icon
0053     const QString iconPath = QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("icons"), QStandardPaths::LocateDirectory);
0054     if (!iconPath.isNull()) {
0055         QStringList themeSearchPaths = QIcon::themeSearchPaths();
0056         themeSearchPaths << iconPath;
0057         QIcon::setThemeSearchPaths(themeSearchPaths);
0058     }
0059 }
0060 
0061 int IndicatorHelper::daemonHook(QProcess &kdeconnectd)
0062 {
0063     kdeconnectd.start(processes::kdeconnect_daemon, QStringList());
0064     return 0;
0065 }
0066 
0067 void onThemeChanged(QSystemTrayIcon &systray)
0068 {
0069     // Since this is a system tray icon,  we care about the system theme and not the app theme
0070     QSettings registry(QStringLiteral("HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"), QSettings::Registry64Format);
0071     bool isLightTheme = registry.value(QStringLiteral("SystemUsesLightTheme")).toBool();
0072     if (isLightTheme) {
0073         systray.setIcon(
0074             QIcon(QStandardPaths::locate(QStandardPaths::AppLocalDataLocation, QStringLiteral("icons/hicolor/scalable/apps/kdeconnectindicator.svg"))));
0075     } else {
0076         systray.setIcon(
0077             QIcon(QStandardPaths::locate(QStandardPaths::AppLocalDataLocation, QStringLiteral("icons/hicolor/scalable/apps/kdeconnectindicatordark.svg"))));
0078     }
0079 }
0080 
0081 void IndicatorHelper::systrayIconHook(QSystemTrayIcon &systray)
0082 {
0083     // Set a callback so we can detect changes to light/dark themes and manually call the callback once the first time
0084     uiSettings.ColorValuesChanged([&systray](auto &&unused1, auto &&unused2) {
0085         onThemeChanged(systray);
0086     });
0087     onThemeChanged(systray);
0088 }
0089 
0090 bool IndicatorHelper::terminateProcess(const QString &processName, const QUrl &indicatorUrl) const
0091 {
0092     HANDLE hProcessSnap;
0093     HANDLE hProcess;
0094 
0095     hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
0096     if (hProcessSnap == INVALID_HANDLE_VALUE) {
0097         qCWarning(KDECONNECT_INDICATOR) << "Failed to get snapshot of processes.";
0098         return FALSE;
0099     }
0100 
0101     PROCESSENTRY32 pe32;
0102     pe32.dwSize = sizeof(PROCESSENTRY32);
0103 
0104     if (!Process32First(hProcessSnap, &pe32)) {
0105         qCWarning(KDECONNECT_INDICATOR) << "Failed to get handle for the first process.";
0106         CloseHandle(hProcessSnap);
0107         return FALSE;
0108     }
0109 
0110     do {
0111         if (QString::fromWCharArray((wchar_t *)pe32.szExeFile) == processName) {
0112             hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe32.th32ProcessID);
0113 
0114             if (hProcess == NULL) {
0115                 qCWarning(KDECONNECT_INDICATOR) << "Failed to get handle for the process:" << processName;
0116                 return FALSE;
0117             } else {
0118                 const DWORD processPathSize = 4096;
0119                 CHAR processPathString[processPathSize];
0120 
0121                 BOOL gotProcessPath = QueryFullProcessImageNameA(hProcess, 0, (LPSTR)processPathString, (PDWORD)&processPathSize);
0122 
0123                 if (gotProcessPath) {
0124                     const QUrl processUrl = QUrl::fromLocalFile(QString::fromStdString(processPathString)); // to replace \\ with /
0125                     if (indicatorUrl.isParentOf(processUrl)) {
0126                         BOOL terminateSuccess = TerminateProcess(hProcess, 0);
0127                         if (!terminateSuccess) {
0128                             qCWarning(KDECONNECT_INDICATOR) << "Failed to terminate process:" << processName;
0129                             return FALSE;
0130                         }
0131                     }
0132                 }
0133             }
0134         }
0135     } while (Process32Next(hProcessSnap, &pe32));
0136 
0137     CloseHandle(hProcessSnap);
0138     return TRUE;
0139 }