From 76424950e373d3b04ac3dd13019151bfba3e8423 Mon Sep 17 00:00:00 2001 From: sumuel Date: Mon, 17 Aug 2026 20:44:55 +0000 Subject: Add the files --- src/detection/wm/wm.h | 7 + src/detection/wm/wm_apple.m | 94 +++++++++++ src/detection/wm/wm_linux.c | 335 ++++++++++++++++++++++++++++++++++++++++ src/detection/wm/wm_nosupport.c | 9 ++ src/detection/wm/wm_windows.c | 204 ++++++++++++++++++++++++ 5 files changed, 649 insertions(+) create mode 100644 src/detection/wm/wm.h create mode 100644 src/detection/wm/wm_apple.m create mode 100644 src/detection/wm/wm_linux.c create mode 100644 src/detection/wm/wm_nosupport.c create mode 100644 src/detection/wm/wm_windows.c (limited to 'src/detection/wm') diff --git a/src/detection/wm/wm.h b/src/detection/wm/wm.h new file mode 100644 index 0000000..115a8cd --- /dev/null +++ b/src/detection/wm/wm.h @@ -0,0 +1,7 @@ +#pragma once + +#include "fastfetch.h" +#include "modules/wm/wm.h" + +const char* ffDetectWMPlugin(FFstrbuf* pluginName); +const char* ffDetectWMVersion(const FFstrbuf* wmName, FFstrbuf* result, FFWMOptions* options); diff --git a/src/detection/wm/wm_apple.m b/src/detection/wm/wm_apple.m new file mode 100644 index 0000000..89273e9 --- /dev/null +++ b/src/detection/wm/wm_apple.m @@ -0,0 +1,94 @@ +#include "wm.h" + +#include "common/sysctl.h" +#include "common/mallocHelper.h" +#include "common/strutil.h" +#include "common/apple/version.h" + +#include +#include +#import + +const char* ffDetectWMPlugin(FFstrbuf* pluginName) { + int request[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL }; + u_int requestLength = ARRAY_SIZE(request); + + size_t length = 0; + FF_AUTO_FREE struct kinfo_proc* processes = ffSysctlGetData(request, requestLength, &length); + if (processes == NULL) { + return "sysctl(CTL_KERN, KERN_PROC, KERN_PROC_ALL) failed"; + } + assert(length % sizeof(struct kinfo_proc) == 0); + + for (size_t i = 0; i < length / sizeof(struct kinfo_proc); i++) { + const struct kinfo_proc* proc = &processes[i]; + if (proc->kp_eproc.e_ppid != 1) { + continue; + } + + const char* comm = proc->kp_proc.p_comm; + + if ( + !ffStrEqualsIgnCase(comm, "rectangle") && // 28.6k + !ffStrEqualsIgnCase(comm, "yabai") && // 28.4k + !ffStrEqualsIgnCase(comm, "aerospace") && // 19.6k + !ffStrEqualsIgnCase(comm, "amethyst") && // 16k + !ffStrEqualsIgnCase(comm, "glazewm") && // 11.6k + +#if 0 + // Unmaintained + !ffStrEqualsIgnCase(comm, "spectacle") && // 13.6k + !ffStrEqualsIgnCase(comm, "chunkwm") && // repo deleted; was https://github.com/koekeishiya/chunkwm + !ffStrEqualsIgnCase(comm, "kwm") && // repo deleted; was https://github.com/koekeishiya/kwm +#endif + true) + continue; + + if (instance.config.general.detectVersion) { + char buf[PROC_PIDPATHINFO_MAXSIZE]; + int length = proc_pidpath(proc->kp_proc.p_pid, buf, ARRAY_SIZE(buf) - strlen("Info.plist")); + if (length > 0) { + buf[length] = '\0'; + FF_STRBUF_AUTO_DESTROY pluginVersion = ffStrbufCreate(); + if (ffGetAppNameAndVersion(buf, pluginName, &pluginVersion)) { + if (pluginName->length == 0) { + ffStrbufSetS(pluginName, comm); + } + if (pluginVersion.length > 0) { + ffStrbufAppendC(pluginName, ' '); + ffStrbufAppend(pluginName, &pluginVersion); + } + break; + } + } + } + + ffStrbufAppendS(pluginName, comm); + pluginName->chars[0] = (char) toupper(pluginName->chars[0]); + break; + } + + return NULL; +} + +const char* ffDetectWMVersion(const FFstrbuf* wmName, FFstrbuf* result, FF_A_UNUSED FFWMOptions* options) { + if (!wmName) { + return "No WM detected"; + } + + if (ffStrbufEqualS(wmName, "WindowServer")) { + NSError* error; + NSDictionary* dict = [NSDictionary dictionaryWithContentsOfURL:[NSURL fileURLWithPath:@"/System/Library/PrivateFrameworks/SkyLight.framework/Resources/version.plist" isDirectory:NO] + error:&error]; + if (!dict) { + dict = [NSDictionary dictionaryWithContentsOfURL:[NSURL fileURLWithPath:@"/System/Library/Frameworks/ApplicationServices.framework/Frameworks/CoreGraphics.framework/Resources/version.plist" isDirectory:NO] + error:&error]; + } + + if (dict) { + ffStrbufSetS(result, ((NSString*) dict[@"CFBundleShortVersionString"]).UTF8String); + } + } + + return NULL; +} diff --git a/src/detection/wm/wm_linux.c b/src/detection/wm/wm_linux.c new file mode 100644 index 0000000..a62a4af --- /dev/null +++ b/src/detection/wm/wm_linux.c @@ -0,0 +1,335 @@ +#include "wm.h" + +#include "common/processing.h" +#include "common/io.h" +#include "common/binary.h" +#include "common/path.h" +#include "common/strutil.h" +#include "common/debug.h" + +const char* ffDetectWMPlugin(FF_A_UNUSED FFstrbuf* pluginName) { + return "Not supported on this platform"; +} + +static bool extractCommonWmVersion(const char* line, FF_A_UNUSED uint32_t len, void* userdata) { + int count = 0; + sscanf(line, "%*d.%*d.%*d%n", &count); + if (count == 0) { + return true; + } + + ffStrbufSetNS((FFstrbuf*) userdata, len, line); + return false; +} + +#if !__ANDROID__ +static bool extractHyprlandVersion(const char* line, uint32_t len, void* userdata) { + if (line[0] != 'v') { + return true; + } + ++line; + --len; + int count = 0; + sscanf(line, "%*d.%*d.%*d%n", &count); + if (count == 0) { + return true; + } + + ffStrbufSetNS((FFstrbuf*) userdata, len, line); + return false; +} + +static const char* getHyprland(FFstrbuf* result) { + FF_DEBUG("Detecting Hyprland version"); + FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate(); + + FF_DEBUG("Checking for " FASTFETCH_TARGET_DIR_USR "/include/hyprland/src/version.h" + " file"); + if (ffReadFileBuffer(FASTFETCH_TARGET_DIR_USR "/include/hyprland/src/version.h", result)) { + FF_DEBUG("Found version.h file, extracting version"); + if (ffStrbufSubstrAfterFirstS(result, "\n#define GIT_TAG ")) { + ffStrbufSubstrAfterFirstC(result, '"'); + ffStrbufSubstrBeforeFirstC(result, '"'); + ffStrbufTrimLeft(result, 'v'); + FF_DEBUG("Extracted version from version.h: %s", result->chars); + return NULL; + } + FF_DEBUG("Failed to extract version from version.h"); + ffStrbufClear(result); + } else { + FF_DEBUG("version.h file not found, trying Hyprland executable"); + } + + const char* error = ffFindExecutableInPath("Hyprland", &buffer); + if (error) { + FF_DEBUG("Error finding Hyprland executable: %s", error); + return "Failed to find Hyprland executable path"; + } + FF_DEBUG("Found Hyprland executable at: %s", buffer.chars); + + ffBinaryExtractStrings(buffer.chars, extractHyprlandVersion, result, (uint32_t) strlen("v0.0.0")); + if (result->length > 0) { + FF_DEBUG("Extracted version from binary strings: %s", result->chars); + return NULL; + } + FF_DEBUG("Failed to extract version from binary strings, trying --version option"); + + if (ffProcessAppendStdOut(result, (char* const[]) { buffer.chars, "--version", NULL }) == NULL) { + // Hyprland 0.48.1 built from branch at commit 29e2e59... + // Date: ... + // Tag: v0.48.1, commits: 5937 + // ... + + FF_DEBUG("Raw version output: %s", result->chars); + // Use tag if available + if (ffStrbufSubstrAfterFirstS(result, "\nTag: v")) { + ffStrbufSubstrBeforeFirstC(result, ','); + FF_DEBUG("Extracted version from Tag: %s", result->chars); + } else { + ffStrbufSubstrAfterFirstC(result, ' '); + ffStrbufSubstrBeforeFirstC(result, ' '); + FF_DEBUG("Extracted version from output: %s", result->chars); + } + return NULL; + } + FF_DEBUG("Failed to run Hyprland --version command"); + + return "Failed to run command `Hyprland --version`"; +} + +static bool extractSwayVersion(const char* line, FF_A_UNUSED uint32_t len, void* userdata) { + FFstrbuf* result = (FFstrbuf*) userdata; + if (!ffStrStartsWith(line, "sway")) { + return true; + } + if (ffStrStartsWith(line + 4, " version ")) { + ffStrbufSetNS(result, len - (uint32_t) strlen("sway version "), line + strlen("sway version ")); + ffStrbufTrimRightSpace(result); + return false; + } else { + char swayfxVer[32], swayVer[32]; + if (sscanf(line + 4, "fx version %31[^ ] (based on sway %31[^)])", swayfxVer, swayVer) == 2) { + ffStrbufSetF(result, "%s [swayfx %s]", swayVer, swayfxVer); + return false; + } + } + + return true; +} + +static const char* getSway(FFstrbuf* result) { + FF_STRBUF_AUTO_DESTROY path = ffStrbufCreate(); + const char* error = ffFindExecutableInPath("sway", &path); + if (error) { + return "Failed to find sway executable path"; + } + + ffBinaryExtractStrings(path.chars, extractSwayVersion, result, (uint32_t) strlen("sway version 0.0.0")); + if (result->length > 0) { + return NULL; + } + + FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate(); + if (ffProcessAppendStdOut(&buffer, (char* const[]) { path.chars, "--version", NULL }) == NULL) { // sway version 1.10 + return extractSwayVersion(buffer.chars, buffer.length, result) ? "Failed to parse sway version output" : NULL; + } + + return "Failed to run command `sway --version`"; +} + +static const char* getLabwc(FFstrbuf* result) { + FF_STRBUF_AUTO_DESTROY path = ffStrbufCreate(); + const char* error = ffFindExecutableInPath("labwc", &path); + if (error) { + return "Failed to find labwc executable path"; + } + + ffBinaryExtractStrings(path.chars, extractCommonWmVersion, result, (uint32_t) strlen("0.0.0")); + if (result->length > 0) { + return NULL; + } + + if (ffProcessAppendStdOut(result, (char* const[]) { path.chars, "--version", NULL }) == NULL) { // labwc 0.9.0 (+xwayland +nls +rsvg +libsfdo) + ffStrbufSubstrAfterFirstC(result, ' '); + ffStrbufSubstrBeforeFirstC(result, ' '); + return NULL; + } + + return "Failed to run command `labwc --version`"; +} + +static const char* getNiri(FFstrbuf* result) { + if (ffProcessAppendStdOut(result, (char* const[]) { "niri", "--version", NULL }) == NULL) { // niri 25.11 (commit b35bcae) + ffStrbufSubstrAfterFirstC(result, ' '); + ffStrbufSubstrBeforeLastC(result, '('); + ffStrbufTrimRightSpace(result); + return NULL; + } + + return "Failed to run command `niri --version`"; +} + + #ifdef __linux__ +static const char* getWslg(FFstrbuf* result) { + if (!ffAppendFileBuffer("/mnt/wslg/versions.txt", result)) { + return "Failed to read /mnt/wslg/versions.txt"; + } + + if (!ffStrbufStartsWithS(result, "WSLg ")) { + return "Failed to find WSLg version"; + } + + ffStrbufSubstrBeforeFirstC(result, '\n'); + ffStrbufSubstrBeforeFirstC(result, '+'); + ffStrbufSubstrAfterFirstC(result, ':'); + ffStrbufTrimLeft(result, ' '); + return NULL; +} + #endif + +#endif // !__ANDROID__ + +static bool extractI3Version(const char* line, FF_A_UNUSED uint32_t len, void* userdata) { + int count = 0; + sscanf(line, "%*d.%*d%n", &count); + if (count == 0) { + return true; + } + + ffStrbufSetNS((FFstrbuf*) userdata, len, line); + return false; +} + +static const char* getI3(FFstrbuf* result) { + FF_STRBUF_AUTO_DESTROY path = ffStrbufCreate(); + const char* error = ffFindExecutableInPath("i3", &path); + if (error) { + return "Failed to find i3 executable path"; + } + + ffBinaryExtractStrings(path.chars, extractI3Version, result, (uint32_t) strlen("0.0")); + if (result->length > 0) { + return NULL; + } + + if (ffProcessAppendStdOut(result, (char* const[]) { path.chars, "--version", NULL }) == NULL) { // i3 version 1.10 C 2009... + ffStrbufSubstrAfterFirstS(result, "version "); + ffStrbufSubstrBeforeFirstC(result, ' '); + return NULL; + } + + return "Failed to run command `i3 --version`"; +} + +static const char* getCtwm(FFstrbuf* result) { + FF_STRBUF_AUTO_DESTROY path = ffStrbufCreate(); + const char* error = ffFindExecutableInPath("ctwm", &path); + if (error) { + return "Failed to find ctwm executable path"; + } + + ffBinaryExtractStrings(path.chars, extractCommonWmVersion, result, (uint32_t) strlen("0.0.0")); + if (result->length > 0) { + return NULL; + } + + if (ffProcessAppendStdOut(result, (char* const[]) { path.chars, "--version", NULL }) == NULL) { // ctwm version 4.0.1\n... + ffStrbufSubstrBeforeFirstC(result, '\n'); + ffStrbufSubstrAfterLastC(result, ' '); + return NULL; + } + + return "Failed to run command `ctwm --version`"; +} + +static const char* getFvwm(FFstrbuf* result) { + FF_STRBUF_AUTO_DESTROY path = ffStrbufCreate(); + const char* error = ffFindExecutableInPath("fvwm", &path); + if (error) { + return "Failed to find fvwm executable path"; + } + + ffBinaryExtractStrings(path.chars, extractCommonWmVersion, result, (uint32_t) strlen("0.0.0")); + if (result->length > 0) { + return NULL; + } + + if (ffProcessAppendStdOut(result, (char* const[]) { path.chars, "-version", NULL }) == NULL) { // [FVWM][main]: fvwm Version 2.2.5\n... + ffStrbufSubstrBeforeFirstC(result, '\n'); + ffStrbufSubstrAfterLastC(result, ' '); + return NULL; + } + + return "Failed to run command `fvwm -version`"; +} + +static const char* getOpenbox(FFstrbuf* result) { + FF_STRBUF_AUTO_DESTROY path = ffStrbufCreate(); + const char* error = ffFindExecutableInPath("openbox", &path); + if (error) { + return "Failed to find openbox executable path"; + } + + ffBinaryExtractStrings(path.chars, extractCommonWmVersion, result, (uint32_t) strlen("0.0.0")); + if (result->length > 0) { + return NULL; + } + + if (ffProcessAppendStdOut(result, (char* const[]) { path.chars, "--version", NULL }) == NULL) { // Openbox 3.6.1\n... + ffStrbufSubstrBeforeFirstC(result, '\n'); + ffStrbufSubstrAfterLastC(result, ' '); + return NULL; + } + + return "Failed to run command `openbox --version`"; +} + +const char* ffDetectWMVersion(const FFstrbuf* wmName, FFstrbuf* result, FF_A_UNUSED FFWMOptions* options) { + if (!wmName) { + return "No WM detected"; + } + +#if !__ANDROID__ + // Wayland compositors + if (ffStrbufIgnCaseEqualS(wmName, "Hyprland")) { + return getHyprland(result); + } + + if (ffStrbufEqualS(wmName, "sway")) { + return getSway(result); + } + + if (ffStrbufEqualS(wmName, "labwc")) { + return getLabwc(result); + } + + if (ffStrbufEqualS(wmName, "niri")) { + return getNiri(result); + } + + #if __linux__ + if (ffStrbufEqualS(wmName, "WSLg")) { + return getWslg(result); + } + #endif +#endif + + // X11 WMs + if (ffStrbufEqualS(wmName, "i3")) { + return getI3(result); + } + + if (ffStrbufEqualS(wmName, "ctwm")) { + return getCtwm(result); + } + + if (ffStrbufEqualS(wmName, "fvwm")) { + return getFvwm(result); + } + + if (ffStrbufEqualS(wmName, "Openbox")) { + return getOpenbox(result); + } + + return "Unsupported WM"; +} diff --git a/src/detection/wm/wm_nosupport.c b/src/detection/wm/wm_nosupport.c new file mode 100644 index 0000000..65c5348 --- /dev/null +++ b/src/detection/wm/wm_nosupport.c @@ -0,0 +1,9 @@ +#include "wm.h" + +const char* ffDetectWMPlugin(FF_A_UNUSED FFstrbuf* pluginName) { + return "Not supported on this platform"; +} + +const char* ffDetectWMVersion(FF_A_UNUSED const FFstrbuf* wmName, FF_A_UNUSED FFstrbuf* result, FF_A_UNUSED FFWMOptions* options) { + return "Not supported on this platform"; +} diff --git a/src/detection/wm/wm_windows.c b/src/detection/wm/wm_windows.c new file mode 100644 index 0000000..ee36399 --- /dev/null +++ b/src/detection/wm/wm_windows.c @@ -0,0 +1,204 @@ +#include "wm.h" +#include "common/mallocHelper.h" +#include "common/io.h" +#include "common/library.h" +#include "common/processing.h" +#include "common/windows/nt.h" +#include "common/windows/unicode.h" +#include "common/windows/version.h" + +#include +#include +#include +#include +#include + +typedef enum { + FF_PROCESS_TYPE_NONE, + FF_PROCESS_TYPE_SIGNED = 1 << 0, + FF_PROCESS_TYPE_WINDOWS_STORE = 1 << 1, + FF_PROCESS_TYPE_GUI = 1 << 2, + FF_PROCESS_TYPE_CUI = 1 << 3, +} FFProcessType; + +static bool verifySignature(const wchar_t* filePath) { + FF_LIBRARY_LOAD(wintrust, true, "wintrust" FF_LIBRARY_EXTENSION, -1) + FF_LIBRARY_LOAD_SYMBOL(wintrust, WinVerifyTrustEx, true) + + WINTRUST_FILE_INFO fileInfo = { + .cbStruct = sizeof(fileInfo), + .pcwszFilePath = filePath, + }; + + GUID actionID = WINTRUST_ACTION_GENERIC_VERIFY_V2; + + WINTRUST_DATA trustData = { + .cbStruct = sizeof(trustData), + .dwUIChoice = WTD_UI_NONE, + .fdwRevocationChecks = WTD_REVOKE_NONE, + .dwUnionChoice = WTD_CHOICE_FILE, + .pFile = &fileInfo, + .dwStateAction = WTD_STATEACTION_VERIFY, + .dwProvFlags = WTD_SAFER_FLAG, + }; + + LONG status = ffWinVerifyTrustEx(NULL, &actionID, &trustData); + trustData.dwStateAction = WTD_STATEACTION_CLOSE; + ffWinVerifyTrustEx(NULL, &actionID, &trustData); + + return status == ERROR_SUCCESS; +} + +static bool isProcessTrusted(DWORD processId, FFProcessType processType, UNICODE_STRING* buffer, size_t bufSize) { + FF_AUTO_CLOSE_FD HANDLE hProcess = NULL; + if (!NT_SUCCESS(NtOpenProcess(&hProcess, PROCESS_QUERY_LIMITED_INFORMATION, &(OBJECT_ATTRIBUTES) { + .Length = sizeof(OBJECT_ATTRIBUTES), + }, + &(CLIENT_ID) { .UniqueProcess = (HANDLE) (uintptr_t) processId }))) { + return false; + } + + ULONG size; + if (!NT_SUCCESS(NtQueryInformationProcess(hProcess, ProcessImageFileNameWin32, buffer, (ULONG) bufSize, &size)) || + buffer->Length == 0) { + return false; + } + assert(buffer->MaximumLength >= buffer->Length + 2); // NULL terminated + + if (processType & FF_PROCESS_TYPE_WINDOWS_STORE) { + static wchar_t windowsAppsPath[MAX_PATH]; + static uint32_t windowsAppsPathLen; + if (windowsAppsPathLen == 0) { + PWSTR pPath = NULL; + if (SUCCEEDED(SHGetKnownFolderPath(&FOLDERID_ProgramFiles, KF_FLAG_DEFAULT, NULL, &pPath))) { + windowsAppsPathLen = (uint32_t) wcslen(pPath); + memcpy(windowsAppsPath, pPath, windowsAppsPathLen * sizeof(wchar_t)); + memcpy(windowsAppsPath + windowsAppsPathLen, L"\\WindowsApps\\", sizeof(L"\\WindowsApps\\")); + windowsAppsPathLen += strlen("\\WindowsApps\\"); + } else { + windowsAppsPathLen = -1u; + } + CoTaskMemFree(pPath); + } + if (windowsAppsPathLen != -1u && + (buffer->Length <= windowsAppsPathLen * sizeof(wchar_t) || // Path is too short to be in WindowsApps + _wcsnicmp(buffer->Buffer, windowsAppsPath, windowsAppsPathLen) != 0) // Path does not start with WindowsApps + ) { + return false; + } + } + + if (processType & FF_PROCESS_TYPE_SIGNED) { + if (!verifySignature(buffer->Buffer)) { + return false; + } + } + + if (processType & (FF_PROCESS_TYPE_GUI | FF_PROCESS_TYPE_CUI)) { + SECTION_IMAGE_INFORMATION info = {}; + if (!NT_SUCCESS(NtQueryInformationProcess(hProcess, ProcessImageInformation, &info, sizeof(info), &size)) || + size != sizeof(info)) { + return false; + } + + if ((processType & FF_PROCESS_TYPE_GUI) && info.SubSystemType != IMAGE_SUBSYSTEM_WINDOWS_GUI) { + return false; + } + if ((processType & FF_PROCESS_TYPE_CUI) && info.SubSystemType != IMAGE_SUBSYSTEM_WINDOWS_CUI) { + return false; + } + } + + return true; +} + +#define ffStrEqualNWS(str, compareTo) (_wcsnicmp(str, L##compareTo, sizeof(compareTo) - 1) == 0) + +const char* ffDetectWMPlugin(FFstrbuf* pluginName) { + alignas(UNICODE_STRING) uint8_t buffer[4096]; + UNICODE_STRING* filePath = (UNICODE_STRING*) buffer; + SYSTEM_PROCESS_INFORMATION* FF_AUTO_FREE pstart = NULL; + + // Multiple attempts in case processes change while + // we are in the middle of querying them. + ULONG size = 0; + for (int attempts = 0;; ++attempts) { + if (size) { + pstart = (SYSTEM_PROCESS_INFORMATION*) realloc(pstart, size); + assert(pstart); + } + NTSTATUS status = NtQuerySystemInformation(SystemProcessInformation, pstart, size, &size); + if (NT_SUCCESS(status)) { + break; + } else if (status == STATUS_INFO_LENGTH_MISMATCH && attempts < 4) { + size += sizeof(SYSTEM_PROCESS_INFORMATION) * 5; + } else { + return "NtQuerySystemInformation(SystemProcessInformation) failed"; + } + } + + for (SYSTEM_PROCESS_INFORMATION* ptr = pstart;; ptr = (SYSTEM_PROCESS_INFORMATION*) ((uint8_t*) ptr + ptr->NextEntryOffset)) { + assert(ptr->ImageName.Length == 0 || ptr->ImageName.MaximumLength >= ptr->ImageName.Length + 2); // NULL terminated + if (ptr->ImageName.Length == strlen("FancyWM-GUI.exe") * sizeof(wchar_t) && + ffStrEqualNWS(ptr->ImageName.Buffer, "FancyWM-GUI.exe") && + isProcessTrusted((DWORD) (uintptr_t) ptr->UniqueProcessId, FF_PROCESS_TYPE_WINDOWS_STORE | FF_PROCESS_TYPE_GUI, filePath, sizeof(buffer))) { + if (instance.config.general.detectVersion && ffGetFileVersion(filePath->Buffer, NULL, pluginName)) { + ffStrbufPrependS(pluginName, "FancyWM "); + } else { + ffStrbufSetStatic(pluginName, "FancyWM"); + } + break; + } else if (ptr->ImageName.Length == strlen("glazewm-watcher.exe") * sizeof(wchar_t) && + ffStrEqualNWS(ptr->ImageName.Buffer, "glazewm-watcher.exe") && + isProcessTrusted((DWORD) (uintptr_t) ptr->UniqueProcessId, FF_PROCESS_TYPE_SIGNED | FF_PROCESS_TYPE_GUI, filePath, sizeof(buffer))) { + if (instance.config.general.detectVersion && ffGetFileVersion(filePath->Buffer, NULL, pluginName)) { + ffStrbufPrependS(pluginName, "GlazeWM "); + } else { + ffStrbufSetStatic(pluginName, "GlazeWM"); + } + break; + } else if (ptr->ImageName.Length == strlen("komorebi.exe") * sizeof(wchar_t) && + ffStrEqualNWS(ptr->ImageName.Buffer, "komorebi.exe") && + isProcessTrusted((DWORD) (uintptr_t) ptr->UniqueProcessId, FF_PROCESS_TYPE_CUI, filePath, sizeof(buffer))) { + if (instance.config.general.detectVersion) { + FF_STRBUF_AUTO_DESTROY path = ffStrbufCreateNWS(filePath->Length / sizeof(wchar_t), filePath->Buffer); + if (ffProcessAppendStdOut(pluginName, (char* const[]) { + path.chars, + "--version", + NULL, + }) == NULL) { + ffStrbufSubstrBeforeFirstC(pluginName, '\n'); + } + } + if (pluginName->length == 0) { + ffStrbufSetStatic(pluginName, "Komorebi"); + } + break; + } + + if (ptr->NextEntryOffset == 0) { + break; + } + } + + return NULL; +} + +const char* ffDetectWMVersion(const FFstrbuf* wmName, FFstrbuf* result, FF_A_UNUSED FFWMOptions* options) { + if (!wmName) { + return "No WM detected"; + } + + if (ffStrbufEqualS(wmName, "dwm.exe")) { + PWSTR pPath = NULL; + if (SUCCEEDED(SHGetKnownFolderPath(&FOLDERID_System, KF_FLAG_DEFAULT, NULL, &pPath))) { + wchar_t fullPath[MAX_PATH]; + wcscpy(fullPath, pPath); + wcscat(fullPath, L"\\dwm.exe"); + ffGetFileVersion(fullPath, NULL, result); + } + CoTaskMemFree(pPath); + return NULL; + } + return "Not supported on this platform"; +} -- cgit v1.2.3