Files
fastfetch/src/detection/wm/wm_linux.c
T

329 lines
10 KiB
C
Raw Normal View History

#include "wm.h"
2026-01-06 09:48:38 +08:00
#include "common/processing.h"
#include "common/io.h"
#include "common/binary.h"
#include "common/path.h"
#include "common/stringUtils.h"
#include "common/debug.h"
2026-01-05 16:19:47 +08:00
#include "detection/displayserver/displayserver.h"
2026-04-09 16:20:59 +08:00
const char* ffDetectWMPlugin(FF_A_UNUSED FFstrbuf* pluginName) {
return "Not supported on this platform";
}
2026-04-09 16:20:59 +08:00
static bool extractCommonWmVersion(const char* line, FF_A_UNUSED uint32_t len, void* userdata) {
2025-09-01 09:59:24 +08:00
int count = 0;
sscanf(line, "%*d.%*d.%*d%n", &count);
2026-03-29 09:28:49 +08:00
if (count == 0) {
return true;
}
2025-09-01 09:59:24 +08:00
ffStrbufSetNS((FFstrbuf*) userdata, len, line);
return false;
}
#if !__ANDROID__
2026-03-29 09:28:49 +08:00
static bool extractHyprlandVersion(const char* line, uint32_t len, void* userdata) {
if (line[0] != 'v') {
return true;
}
++line;
--len;
2025-01-02 16:36:31 +08:00
int count = 0;
sscanf(line, "%*d.%*d.%*d%n", &count);
2026-03-29 09:28:49 +08:00
if (count == 0) {
return true;
}
2025-01-02 16:36:31 +08:00
ffStrbufSetNS((FFstrbuf*) userdata, len, line);
2025-01-02 16:36:31 +08:00
return false;
}
2026-03-29 09:28:49 +08:00
static const char* getHyprland(FFstrbuf* result) {
FF_DEBUG("Detecting Hyprland version");
FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate();
2025-01-02 16:36:31 +08:00
2026-03-29 09:28:49 +08:00
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");
2026-03-29 09:28:49 +08:00
if (ffStrbufSubstrAfterFirstS(result, "\n#define GIT_TAG ")) {
ffStrbufSubstrAfterFirstC(result, '"');
ffStrbufSubstrBeforeFirstC(result, '"');
2025-04-06 17:21:32 +08:00
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);
2026-03-29 09:28:49 +08:00
} 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");
2025-01-02 16:36:31 +08:00
2026-03-30 10:27:23 +08:00
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
2026-03-29 09:28:49 +08:00
if (ffStrbufSubstrAfterFirstS(result, "\nTag: v")) {
ffStrbufSubstrBeforeFirstC(result, ',');
FF_DEBUG("Extracted version from Tag: %s", result->chars);
2026-03-29 09:28:49 +08:00
} else {
ffStrbufSubstrAfterFirstC(result, ' ');
ffStrbufSubstrBeforeFirstC(result, ' ');
FF_DEBUG("Extracted version from output: %s", result->chars);
}
2025-01-02 16:36:31 +08:00
return NULL;
}
FF_DEBUG("Failed to run Hyprland --version command");
2025-01-02 16:36:31 +08:00
return "Failed to run command `Hyprland --version`";
}
2026-04-09 16:20:59 +08:00
static bool extractSwayVersion(const char* line, FF_A_UNUSED uint32_t len, void* userdata) {
2026-03-29 09:28:49 +08:00
if (!ffStrStartsWith(line, "sway version ")) {
return true;
}
2025-01-02 16:36:31 +08:00
FFstrbuf* result = (FFstrbuf*) userdata;
ffStrbufSetNS(result, len - (uint32_t) strlen("sway version "), line + strlen("sway version "));
ffStrbufTrimRightSpace(result);
2025-01-02 16:36:31 +08:00
return false;
}
2026-03-29 09:28:49 +08:00
static const char* getSway(FFstrbuf* result) {
2025-01-02 16:36:31 +08:00
FF_STRBUF_AUTO_DESTROY path = ffStrbufCreate();
const char* error = ffFindExecutableInPath("sway", &path);
2026-03-29 09:28:49 +08:00
if (error) {
return "Failed to find sway executable path";
}
2025-01-02 16:36:31 +08:00
ffBinaryExtractStrings(path.chars, extractSwayVersion, result, (uint32_t) strlen("v0.0.0"));
2026-03-29 09:28:49 +08:00
if (result->length > 0) {
return NULL;
}
2026-03-30 10:27:23 +08:00
if (ffProcessAppendStdOut(result, (char* const[]) { path.chars, "--version", NULL }) == NULL) { // sway version 1.10
2025-01-02 16:36:31 +08:00
ffStrbufSubstrAfterLastC(result, ' ');
ffStrbufTrimRightSpace(result);
return NULL;
}
return "Failed to run command `sway --version`";
}
2026-03-29 09:28:49 +08:00
static const char* getLabwc(FFstrbuf* result) {
2025-05-31 10:41:38 +08:00
FF_STRBUF_AUTO_DESTROY path = ffStrbufCreate();
2025-09-01 09:59:24 +08:00
const char* error = ffFindExecutableInPath("labwc", &path);
2026-03-29 09:28:49 +08:00
if (error) {
return "Failed to find labwc executable path";
}
2025-05-31 10:41:38 +08:00
2025-09-01 09:59:24 +08:00
ffBinaryExtractStrings(path.chars, extractCommonWmVersion, result, (uint32_t) strlen("0.0.0"));
2026-03-29 09:28:49 +08:00
if (result->length > 0) {
return NULL;
}
2026-03-30 10:27:23 +08:00
if (ffProcessAppendStdOut(result, (char* const[]) { path.chars, "--version", NULL }) == NULL) { // labwc 0.9.0 (+xwayland +nls +rsvg +libsfdo)
2025-09-01 09:59:24 +08:00
ffStrbufSubstrAfterFirstC(result, ' ');
2025-05-31 10:41:38 +08:00
ffStrbufSubstrBeforeFirstC(result, ' ');
return NULL;
}
2025-09-01 09:59:24 +08:00
return "Failed to run command `labwc --version`";
2025-05-31 10:41:38 +08:00
}
2026-03-29 09:28:49 +08:00
static const char* getNiri(FFstrbuf* result) {
2026-03-30 10:27:23 +08:00
if (ffProcessAppendStdOut(result, (char* const[]) { "niri", "--version", NULL }) == NULL) { // niri 25.11 (commit b35bcae)
2025-12-28 19:13:46 +08:00
ffStrbufSubstrAfterFirstC(result, ' ');
ffStrbufSubstrBeforeLastC(result, '(');
ffStrbufTrimRightSpace(result);
2025-12-28 19:13:46 +08:00
return NULL;
}
return "Failed to run command `niri --version`";
}
#ifdef __linux__
2026-03-29 09:28:49 +08:00
static const char* getWslg(FFstrbuf* result) {
if (!ffAppendFileBuffer("/mnt/wslg/versions.txt", result)) {
2025-01-02 20:19:15 +08:00
return "Failed to read /mnt/wslg/versions.txt";
2026-03-29 09:28:49 +08:00
}
2025-01-02 20:19:15 +08:00
2026-03-29 09:28:49 +08:00
if (!ffStrbufStartsWithS(result, "WSLg ")) {
2025-01-02 20:19:15 +08:00
return "Failed to find WSLg version";
2026-03-29 09:28:49 +08:00
}
2025-01-02 20:19:15 +08:00
ffStrbufSubstrBeforeFirstC(result, '\n');
ffStrbufSubstrBeforeFirstC(result, '+');
ffStrbufSubstrAfterFirstC(result, ':');
ffStrbufTrimLeft(result, ' ');
return NULL;
}
#endif
2025-01-02 20:19:15 +08:00
2025-09-01 09:59:24 +08:00
#endif // !__ANDROID__
2026-04-09 16:20:59 +08:00
static bool extractI3Version(const char* line, FF_A_UNUSED uint32_t len, void* userdata) {
2025-05-31 10:02:45 +08:00
int count = 0;
2025-09-01 09:59:24 +08:00
sscanf(line, "%*d.%*d%n", &count);
2026-03-29 09:28:49 +08:00
if (count == 0) {
return true;
}
2025-05-31 10:02:45 +08:00
ffStrbufSetNS((FFstrbuf*) userdata, len, line);
return false;
}
2026-03-29 09:28:49 +08:00
static const char* getI3(FFstrbuf* result) {
2025-09-01 09:59:24 +08:00
FF_STRBUF_AUTO_DESTROY path = ffStrbufCreate();
const char* error = ffFindExecutableInPath("i3", &path);
2026-03-29 09:28:49 +08:00
if (error) {
return "Failed to find i3 executable path";
}
2025-09-01 09:59:24 +08:00
ffBinaryExtractStrings(path.chars, extractI3Version, result, (uint32_t) strlen("0.0"));
2026-03-29 09:28:49 +08:00
if (result->length > 0) {
return NULL;
}
2026-03-30 10:27:23 +08:00
if (ffProcessAppendStdOut(result, (char* const[]) { path.chars, "--version", NULL }) == NULL) { // i3 version 1.10 C 2009...
2025-09-01 09:59:24 +08:00
ffStrbufSubstrAfterFirstS(result, "version ");
ffStrbufSubstrBeforeFirstC(result, ' ');
return NULL;
}
return "Failed to run command `i3 --version`";
}
2026-03-29 09:28:49 +08:00
static const char* getCtwm(FFstrbuf* result) {
2025-05-31 10:02:45 +08:00
FF_STRBUF_AUTO_DESTROY path = ffStrbufCreate();
const char* error = ffFindExecutableInPath("ctwm", &path);
2026-03-29 09:28:49 +08:00
if (error) {
return "Failed to find ctwm executable path";
}
2025-05-31 10:02:45 +08:00
2025-07-17 14:15:35 +08:00
ffBinaryExtractStrings(path.chars, extractCommonWmVersion, result, (uint32_t) strlen("0.0.0"));
2026-03-29 09:28:49 +08:00
if (result->length > 0) {
return NULL;
}
2026-03-30 10:27:23 +08:00
if (ffProcessAppendStdOut(result, (char* const[]) { path.chars, "--version", NULL }) == NULL) { // ctwm version 4.0.1\n...
2025-05-31 10:02:45 +08:00
ffStrbufSubstrBeforeFirstC(result, '\n');
ffStrbufSubstrAfterLastC(result, ' ');
return NULL;
}
return "Failed to run command `ctwm --version`";
}
2026-03-29 09:28:49 +08:00
static const char* getFvwm(FFstrbuf* result) {
2025-05-31 10:16:26 +08:00
FF_STRBUF_AUTO_DESTROY path = ffStrbufCreate();
const char* error = ffFindExecutableInPath("fvwm", &path);
2026-03-29 09:28:49 +08:00
if (error) {
return "Failed to find fvwm executable path";
}
2025-05-31 10:16:26 +08:00
2025-07-17 14:15:35 +08:00
ffBinaryExtractStrings(path.chars, extractCommonWmVersion, result, (uint32_t) strlen("0.0.0"));
2026-03-29 09:28:49 +08:00
if (result->length > 0) {
return NULL;
}
2026-03-30 10:27:23 +08:00
if (ffProcessAppendStdOut(result, (char* const[]) { path.chars, "-version", NULL }) == NULL) { // [FVWM][main]: fvwm Version 2.2.5\n...
2025-05-31 10:16:26 +08:00
ffStrbufSubstrBeforeFirstC(result, '\n');
ffStrbufSubstrAfterLastC(result, ' ');
return NULL;
}
return "Failed to run command `fvwm -version`";
}
2026-03-29 09:28:49 +08:00
static const char* getOpenbox(FFstrbuf* result) {
2025-07-17 14:15:35 +08:00
FF_STRBUF_AUTO_DESTROY path = ffStrbufCreate();
const char* error = ffFindExecutableInPath("openbox", &path);
2026-03-29 09:28:49 +08:00
if (error) {
return "Failed to find openbox executable path";
}
2025-07-17 14:15:35 +08:00
ffBinaryExtractStrings(path.chars, extractCommonWmVersion, result, (uint32_t) strlen("0.0.0"));
2026-03-29 09:28:49 +08:00
if (result->length > 0) {
return NULL;
}
2026-03-30 10:27:23 +08:00
if (ffProcessAppendStdOut(result, (char* const[]) { path.chars, "--version", NULL }) == NULL) { // Openbox 3.6.1\n...
2025-07-17 14:15:35 +08:00
ffStrbufSubstrBeforeFirstC(result, '\n');
ffStrbufSubstrAfterLastC(result, ' ');
return NULL;
}
2025-07-18 17:59:38 +08:00
return "Failed to run command `openbox --version`";
2025-07-17 14:15:35 +08:00
}
2026-04-09 16:20:59 +08:00
const char* ffDetectWMVersion(const FFstrbuf* wmName, FFstrbuf* result, FF_A_UNUSED FFWMOptions* options) {
2026-03-29 09:28:49 +08:00
if (!wmName) {
return "No WM detected";
2026-03-29 09:28:49 +08:00
}
2026-03-29 09:28:49 +08:00
#if !__ANDROID__
2025-09-01 09:59:24 +08:00
// Wayland compositors
2026-03-29 09:28:49 +08:00
if (ffStrbufIgnCaseEqualS(wmName, "Hyprland")) {
2025-01-02 20:19:15 +08:00
return getHyprland(result);
2026-03-29 09:28:49 +08:00
}
2025-01-02 16:36:31 +08:00
2026-03-29 09:28:49 +08:00
if (ffStrbufEqualS(wmName, "sway")) {
2025-01-02 20:19:15 +08:00
return getSway(result);
2026-03-29 09:28:49 +08:00
}
2025-01-02 20:19:15 +08:00
2026-03-29 09:28:49 +08:00
if (ffStrbufEqualS(wmName, "labwc")) {
2025-09-01 09:59:24 +08:00
return getLabwc(result);
2026-03-29 09:28:49 +08:00
}
2025-05-31 10:41:38 +08:00
2026-03-29 09:28:49 +08:00
if (ffStrbufEqualS(wmName, "niri")) {
2025-12-28 19:13:46 +08:00
return getNiri(result);
2026-03-29 09:28:49 +08:00
}
2025-12-28 19:13:46 +08:00
#if __linux__
2026-03-29 09:28:49 +08:00
if (ffStrbufEqualS(wmName, "WSLg")) {
2025-01-02 20:19:15 +08:00
return getWslg(result);
2026-03-29 09:28:49 +08:00
}
#endif
2026-03-29 09:28:49 +08:00
#endif
2025-09-01 09:59:24 +08:00
// X11 WMs
2026-03-29 09:28:49 +08:00
if (ffStrbufEqualS(wmName, "i3")) {
2025-09-01 09:59:24 +08:00
return getI3(result);
2026-03-29 09:28:49 +08:00
}
2026-03-29 09:28:49 +08:00
if (ffStrbufEqualS(wmName, "ctwm")) {
2025-05-31 10:02:45 +08:00
return getCtwm(result);
2026-03-29 09:28:49 +08:00
}
2025-05-31 10:02:45 +08:00
2026-03-29 09:28:49 +08:00
if (ffStrbufEqualS(wmName, "fvwm")) {
2025-05-31 10:16:26 +08:00
return getFvwm(result);
2026-03-29 09:28:49 +08:00
}
2025-05-31 10:16:26 +08:00
2026-03-29 09:28:49 +08:00
if (ffStrbufEqualS(wmName, "Openbox")) {
2025-07-17 14:15:35 +08:00
return getOpenbox(result);
2026-03-29 09:28:49 +08:00
}
2025-07-17 14:15:35 +08:00
2025-01-02 16:36:31 +08:00
return "Unsupported WM";
}