summaryrefslogtreecommitdiffstats
path: root/src/detection/de/de_linux.c
diff options
context:
space:
mode:
authorsumuel <samuel@yakubos.org>2026-08-17 20:44:55 +0000
committersumuel <samuel@yakubos.org>2026-08-17 20:44:55 +0000
commit76424950e373d3b04ac3dd13019151bfba3e8423 (patch)
tree4c3cfdbda039e592b9186be3e28f8d7cfd439e8a /src/detection/de/de_linux.c
Add the files
Diffstat (limited to 'src/detection/de/de_linux.c')
-rw-r--r--src/detection/de/de_linux.c284
1 files changed, 284 insertions, 0 deletions
diff --git a/src/detection/de/de_linux.c b/src/detection/de/de_linux.c
new file mode 100644
index 0000000..de7a220
--- /dev/null
+++ b/src/detection/de/de_linux.c
@@ -0,0 +1,284 @@
+#include "de.h"
+
+#include "common/dbus.h"
+#include "common/io.h"
+#include "common/library.h"
+#include "common/parsing.h"
+#include "common/properties.h"
+#include "common/processing.h"
+#include "common/binary.h"
+#include "common/path.h"
+#include "detection/displayserver/displayserver.h"
+
+#include <ctype.h>
+#ifdef __FreeBSD__
+ #include <paths.h>
+ #ifndef _PATH_LOCALBASE
+ #define _PATH_LOCALBASE "/usr/local"
+ #endif
+#elif __OpenBSD__
+ #define _PATH_LOCALBASE "/usr/local"
+#elif __NetBSD__
+ #define _PATH_LOCALBASE "/usr/pkg"
+#endif
+
+static void getKDE(FFstrbuf* result, FF_A_UNUSED FFDEOptions* options) {
+#ifdef _PATH_LOCALBASE
+ ffParsePropFile(_PATH_LOCALBASE "/share/wayland-sessions/plasma.desktop", "X-KDE-PluginInfo-Version =", result);
+ if (result->length == 0) {
+ ffParsePropFile(_PATH_LOCALBASE "/share/xsessions/plasmax11.desktop", "X-KDE-PluginInfo-Version =", result);
+ }
+#else
+ ffParsePropFile(FASTFETCH_TARGET_DIR_USR "/share/wayland-sessions/plasma.desktop", "X-KDE-PluginInfo-Version =", result);
+ if (result->length == 0) {
+ ffParsePropFile(FASTFETCH_TARGET_DIR_USR "/share/xsessions/plasmax11.desktop", "X-KDE-PluginInfo-Version =", result);
+ }
+#endif
+
+ if (result->length == 0) {
+ ffParsePropFileData("xsessions/plasma.desktop", "X-KDE-PluginInfo-Version =", result);
+ }
+ if (result->length == 0) {
+ ffParsePropFileData("xsessions/plasma5.desktop", "X-KDE-PluginInfo-Version =", result);
+ }
+
+ if (result->length == 0) {
+ ffParsePropFileData("wayland-sessions/plasmawayland.desktop", "X-KDE-PluginInfo-Version =", result);
+ }
+ if (result->length == 0) {
+ ffParsePropFileData("wayland-sessions/plasmawayland5.desktop", "X-KDE-PluginInfo-Version =", result);
+ }
+
+ if (result->length == 0) {
+ if (ffProcessAppendStdOut(result, (char* const[]) { "plasmashell", "--version", NULL }) == NULL) { // plasmashell 5.27.5
+ ffStrbufSubstrAfterLastC(result, ' ');
+ }
+ }
+}
+
+static const char* getGnomeByDbus(FF_A_UNUSED FFstrbuf* result) {
+#ifdef FF_HAVE_DBUS
+ FF_DBUS_AUTO_DESTROY_DATA FFDBusData dbus = {};
+ if (ffDBusLoadData(DBUS_BUS_SESSION, &dbus) != NULL) {
+ return "ffDBusLoadData() failed";
+ }
+
+ ffDBusGetPropertyString(&dbus, "org.gnome.Shell", "/org/gnome/Shell", "org.gnome.Shell", "ShellVersion", result);
+ return NULL;
+#else // FF_HAVE_DBUS
+ return "ffDBusLoadData() failed: dbus support not compiled in";
+#endif // FF_HAVE_DBUS
+}
+
+static void getGnome(FFstrbuf* result, FF_A_UNUSED FFDEOptions* options) {
+ getGnomeByDbus(result);
+
+ if (result->length == 0) {
+ if (ffProcessAppendStdOut(result, (char* const[]) { "gnome-shell", "--version", NULL }) == NULL) { // GNOME Shell 44.1
+ ffStrbufSubstrAfterLastC(result, ' ');
+ }
+ }
+}
+
+static void getCinnamon(FFstrbuf* result, FF_A_UNUSED FFDEOptions* options) {
+ ffStrbufSetS(result, getenv("CINNAMON_VERSION"));
+
+ if (result->length == 0) {
+ ffParsePropFileData("applications/cinnamon.desktop", "X-GNOME-Bugzilla-Version =", result);
+ }
+
+ if (result->length == 0) {
+ if (ffProcessAppendStdOut(result, (char* const[]) { "cinnamon", "--version", NULL }) == NULL) { // Cinnamon 6.2.2
+ ffStrbufSubstrAfterLastC(result, ' ');
+ }
+ }
+}
+
+static void getMate(FFstrbuf* result, FF_A_UNUSED FFDEOptions* options) {
+ FF_STRBUF_AUTO_DESTROY major = ffStrbufCreate();
+ FF_STRBUF_AUTO_DESTROY minor = ffStrbufCreate();
+ FF_STRBUF_AUTO_DESTROY micro = ffStrbufCreate();
+
+ ffParsePropFileDataValues("mate-about/mate-version.xml", 3, (FFpropquery[]) { { "<platform>", &major }, { "<minor>", &minor }, { "<micro>", &micro } });
+
+ ffParseSemver(result, &major, &minor, &micro);
+
+ if (result->length == 0) {
+ ffProcessAppendStdOut(result, (char* const[]) { "mate-session", "--version", NULL });
+
+ ffStrbufSubstrAfterFirstC(result, ' ');
+ ffStrbufTrim(result, ' ');
+ }
+}
+
+static const char* getXfce4ByLib(FFstrbuf* result) {
+#ifndef FF_DISABLE_DLOPEN
+ const char* xfce_version_string(void); // from `xfce4/libxfce4util/xfce-misutils.h
+ FF_LIBRARY_LOAD_MESSAGE(xfce4util, "libxfce4util" FF_LIBRARY_EXTENSION, 7);
+ FF_LIBRARY_LOAD_SYMBOL_MESSAGE(xfce4util, xfce_version_string);
+ ffStrbufSetS(result, ffxfce_version_string());
+ return NULL;
+#else
+ FF_UNUSED(result);
+ return "dlopen is disabled";
+#endif
+}
+
+static void getXFCE4(FFstrbuf* result, FF_A_UNUSED FFDEOptions* options) {
+ getXfce4ByLib(result);
+
+ if (result->length == 0) {
+ // This is somewhat slow
+ ffProcessAppendStdOut(result, (char* const[]) { "xfce4-session", "--version", NULL });
+
+ ffStrbufSubstrBeforeFirstC(result, ')');
+ ffStrbufSubstrAfterLastC(result, ' ');
+ ffStrbufTrim(result, ' ');
+ }
+}
+
+static void getLXQt(FFstrbuf* result, FF_A_UNUSED FFDEOptions* options) {
+ ffParsePropFileData("gconfig/lxqt.pc", "Version:", result);
+
+ if (result->length == 0) {
+ ffParsePropFileData("cmake/lxqt/lxqt-config.cmake", "set ( LXQT_VERSION", result);
+ }
+ if (result->length == 0) {
+ ffParsePropFileData("cmake/lxqt/lxqt-config-version.cmake", "set ( PACKAGE_VERSION", result);
+ }
+
+ if (result->length == 0) {
+ // This is really, really, really slow. Thank you, LXQt developers
+ ffProcessAppendStdOut(result, (char* const[]) { "lxqt-session", "-v", NULL });
+
+ result->length = 0; // don't set '\0' byte
+ ffParsePropLines(result->chars, "liblxqt", result);
+ }
+}
+
+static void getBudgie(FFstrbuf* result, FF_A_UNUSED FFDEOptions* options) {
+ ffParsePropFileData("budgie/budgie-version.xml", "<str>", result);
+}
+
+static void getUnity(FFstrbuf* result, FF_A_UNUSED FFDEOptions* options) {
+ if (ffParsePropFile("/usr/bin/unity", "parser = OptionParser(version= \"%prog ", result)) {
+ ffStrbufSubstrBeforeFirstC(result, '"');
+ }
+}
+
+static bool extractTdeVersion(const char* line, uint32_t len, void* userdata) {
+ int count = 0;
+ sscanf(line, "R%*d.%*d.%*d%n", &count);
+ if (count == 0) {
+ return true;
+ }
+
+ ffStrbufSetNS((FFstrbuf*) userdata, len, line);
+ return false;
+}
+
+static const char* getTrinity(FFstrbuf* result, FF_A_UNUSED FFDEOptions* options) {
+ FF_STRBUF_AUTO_DESTROY path = ffStrbufCreate();
+ const char* error = ffFindExecutableInPath("tde-config", &path);
+ if (error) {
+ return "Failed to find tde-config path";
+ }
+
+ ffStrbufSubstrBeforeLastC(&path, '/');
+ ffStrbufAppendS(&path, "/../lib/libtdecore.so");
+
+ if (ffBinaryExtractStrings(path.chars, extractTdeVersion, result, strlen("R0.0.0")) == NULL) {
+ return NULL;
+ }
+
+ ffStrbufClear(&path);
+ if (ffProcessAppendStdOut(&path, (char* const[]) { "tde-config", "--version", NULL }) == NULL) {
+ ffParsePropLines(path.chars, "TDE: ", result);
+ return NULL;
+ }
+
+ return "All methods failed";
+}
+
+static const char* getCosmic(FFstrbuf* result, FF_A_UNUSED FFDEOptions* options) {
+ if (ffProcessAppendStdOut(result, (char* const[]) { "cosmic-comp", "--version", NULL }) == NULL) {
+ // cosmic-comp 0.1.0 (git commit fa88002ba41d2edec25dd7ffdee9719fbb928fc0)
+ ffStrbufSubstrAfterFirstC(result, ' ');
+ ffStrbufSubstrBeforeFirstC(result, ' ');
+ return NULL;
+ }
+
+ return "All methods failed";
+}
+
+static const char* getEnlightenmentByDbus(FF_A_UNUSED FFstrbuf* result) {
+#ifdef FF_HAVE_DBUS
+ FF_DBUS_AUTO_DESTROY_DATA FFDBusData dbus = {};
+ if (ffDBusLoadData(DBUS_BUS_SESSION, &dbus) != NULL) {
+ return "ffDBusLoadData() failed";
+ }
+
+ DBusMessage* reply = ffDBusGetMethodReply(&dbus, "org.enlightenment.wm.service", "/org/enlightenment/wm/RemoteObject", "org.enlightenment.wm.Core", "Version", NULL, NULL);
+ if (!reply) {
+ return "ffDBusGetMethodReply() failed";
+ }
+
+ DBusMessageIter rootIterator;
+ if (!dbus.lib->ffdbus_message_iter_init(reply, &rootIterator)) {
+ dbus.lib->ffdbus_message_unref(reply);
+ return "dbus_message_iter_init() failed";
+ }
+ if (!ffDBusGetString(&dbus, &rootIterator, result)) {
+ dbus.lib->ffdbus_message_unref(reply);
+ return "ffDBusGetString() failed";
+ }
+ dbus.lib->ffdbus_message_unref(reply);
+
+ return NULL;
+#else // FF_HAVE_DBUS
+ return "ffDBusLoadData() failed: dbus support not compiled in";
+#endif // FF_HAVE_DBUS
+}
+
+static void getEnlightenment(FFstrbuf* result, FF_A_UNUSED FFDEOptions* options) {
+ getEnlightenmentByDbus(result);
+
+ if (result->length == 0) {
+ if (ffProcessAppendStdOut(result, (char* const[]) { "enlightenment", "--version", NULL }) == NULL) { // ...\nVersion: 0.27.1\n...
+ ffStrbufSubstrAfterFirstS(result, "Version: ");
+ ffStrbufSubstrBeforeFirstC(result, '\n');
+ }
+ }
+}
+const char* ffDetectDEVersion(const FFstrbuf* deName, FFstrbuf* result, FFDEOptions* options) {
+ if (!instance.config.general.detectVersion) {
+ return "Disabled by config";
+ }
+
+ if (ffStrbufEqualS(deName, FF_DE_PRETTY_PLASMA)) {
+ getKDE(result, options);
+ } else if (ffStrbufEqualS(deName, FF_DE_PRETTY_GNOME)) {
+ getGnome(result, options);
+ } else if (ffStrbufEqualS(deName, FF_DE_PRETTY_CINNAMON)) {
+ getCinnamon(result, options);
+ } else if (ffStrbufEqualS(deName, FF_DE_PRETTY_XFCE4)) {
+ getXFCE4(result, options);
+ } else if (ffStrbufEqualS(deName, FF_DE_PRETTY_MATE)) {
+ getMate(result, options);
+ } else if (ffStrbufEqualS(deName, FF_DE_PRETTY_LXQT)) {
+ getLXQt(result, options);
+ } else if (ffStrbufEqualS(deName, FF_DE_PRETTY_BUDGIE)) {
+ getBudgie(result, options);
+ } else if (ffStrbufEqualS(deName, FF_DE_PRETTY_UNITY)) {
+ getUnity(result, options);
+ } else if (ffStrbufEqualS(deName, "trinity")) {
+ getTrinity(result, options);
+ } else if (ffStrbufEqualS(deName, "COSMIC")) {
+ getCosmic(result, options);
+ } else if (ffStrbufEqualS(deName, FF_DE_PRETTY_ENLIGHTENMENT)) {
+ getEnlightenment(result, options);
+ } else {
+ return "Unsupported DE";
+ }
+ return NULL;
+}