summaryrefslogtreecommitdiffstats
path: root/src/detection/wm
diff options
context:
space:
mode:
Diffstat (limited to 'src/detection/wm')
-rw-r--r--src/detection/wm/wm.h7
-rw-r--r--src/detection/wm/wm_apple.m94
-rw-r--r--src/detection/wm/wm_linux.c335
-rw-r--r--src/detection/wm/wm_nosupport.c9
-rw-r--r--src/detection/wm/wm_windows.c204
5 files changed, 649 insertions, 0 deletions
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 <ctype.h>
+#include <libproc.h>
+#import <Foundation/Foundation.h>
+
+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 <stdalign.h>
+#include <windows.h>
+#include <ntstatus.h>
+#include <shlobj.h>
+#include <softpub.h>
+
+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";
+}