Files
fastfetch/src/common/detectWMDE.c
T

446 lines
16 KiB
C
Raw Normal View History

2021-05-01 13:03:30 +02:00
#include "fastfetch.h"
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <pthread.h>
typedef enum ProtocolHint
{
FF_PROTOCOL_HINT_UNKNOWN = 0,
FF_PROTOCOL_HINT_X11,
FF_PROTOCOL_HINT_WAYLAND
} ProtocolHint;
typedef enum DEHint
{
FF_DE_HINT_UNKNOWN = 0,
2021-05-01 21:22:07 +02:00
FF_DE_HINT_PLASMA,
2021-05-08 11:20:17 +02:00
FF_DE_HINT_GNOME,
2021-05-09 21:41:17 +02:00
FF_DE_HINT_CINNAMON,
2021-05-14 12:43:54 +02:00
FF_DE_HINT_XFCE4,
2021-05-16 14:00:04 +02:00
FF_DE_HINT_MATE,
FF_DE_HINT_LXQT
2021-05-01 13:03:30 +02:00
} DEHint;
typedef struct ProcData
{
DIR* proc;
struct dirent* dirent;
ProtocolHint protocolHint;
DEHint deHint;
} ProcData;
static inline void getSessionDesktop(FFWMDEResult* result)
{
result->sessionDesktop = getenv("XDG_CURRENT_DESKTOP");
if(result->sessionDesktop != NULL && result->sessionDesktop[0] != '\0')
return;
result->sessionDesktop = getenv("XDG_SESSION_DESKTOP");
if(result->sessionDesktop != NULL && result->sessionDesktop[0] != '\0')
return;
result->sessionDesktop = getenv("CURRENT_DESKTOP");
if(result->sessionDesktop != NULL && result->sessionDesktop[0] != '\0')
return;
result->sessionDesktop = getenv("SESSION_DESKTOP");
if(result->sessionDesktop != NULL && result->sessionDesktop[0] != '\0')
return;
const char* desktopSession = getenv("DESKTOP_SESSION");
if(desktopSession != NULL && desktopSession[0] != '\0')
{
if(strcasecmp(desktopSession, "plasma") == 0)
result->sessionDesktop = "KDE";
else
result->sessionDesktop = desktopSession;
return;
}
char* gnomeID = getenv("GNOME_DESKTOP_SESSION_ID");
if(gnomeID != NULL)
{
result->sessionDesktop = "Gnome";
return;
}
char* mateID = getenv("MATE_DESKTOP_SESSION_ID");
if(mateID != NULL)
{
result->sessionDesktop = "Mate";
return;
}
char* tdeID = getenv("TDE_FULL_SESSION");
if(tdeID != NULL)
{
result->sessionDesktop = "Trinity";
return;
}
}
static bool applyPrettyNameIfWM(FFWMDEResult* result, const FFstrbuf* processName, ProtocolHint* protocolHint)
{
if(ffStrbufIgnCaseCompS(processName, "kwin_wayland") == 0)
{
ffStrbufSetS(&result->wmPrettyName, "KWin");
*protocolHint = FF_PROTOCOL_HINT_WAYLAND;
}
else if(ffStrbufIgnCaseCompS(processName, "kwin_x11") == 0)
{
ffStrbufSetS(&result->wmPrettyName, "KWin");
*protocolHint = FF_PROTOCOL_HINT_X11;
}
else if(ffStrbufIgnCaseCompS(processName, "sway") == 0)
{
ffStrbufSetS(&result->wmPrettyName, "Sway");
*protocolHint = FF_PROTOCOL_HINT_WAYLAND;
}
else if(ffStrbufIgnCaseCompS(processName, "weston") == 0)
{
ffStrbufSetS(&result->wmPrettyName, "Weston");
*protocolHint = FF_PROTOCOL_HINT_WAYLAND;
}
else if(ffStrbufIgnCaseCompS(processName, "wayfire") == 0)
{
ffStrbufSetS(&result->wmPrettyName, "Wayfire");
*protocolHint = FF_PROTOCOL_HINT_WAYLAND;
}
else if(ffStrbufIgnCaseCompS(processName, "openbox") == 0)
{
ffStrbufSetS(&result->wmPrettyName, "Openbox");
*protocolHint = FF_PROTOCOL_HINT_X11;
}
else if(ffStrbufIgnCaseCompS(processName, "xfwm4") == 0)
{
2021-05-09 21:41:17 +02:00
ffStrbufSetS(&result->wmPrettyName, "Xfwm4");
2021-05-01 13:03:30 +02:00
*protocolHint = FF_PROTOCOL_HINT_X11;
}
2021-05-14 12:43:54 +02:00
else if(ffStrbufIgnCaseCompS(processName, "Marco") == 0)
{
ffStrbufSetS(&result->wmPrettyName, "Marco");
*protocolHint = FF_PROTOCOL_HINT_X11;
}
2021-05-23 20:35:24 +02:00
else if(ffStrbufIgnCaseCompS(processName, "gnome-session-binary") == 0 || ffStrbufIgnCaseCompS(processName, "Mutter") == 0)
2021-05-01 13:03:30 +02:00
ffStrbufSetS(&result->wmPrettyName, "Mutter");
2021-05-23 20:35:24 +02:00
else if(ffStrbufIgnCaseCompS(processName, "cinnamon-session") == 0 || ffStrbufIgnCaseCompS(processName, "Muffin") == 0)
2021-05-01 13:03:30 +02:00
ffStrbufSetS(&result->wmPrettyName, "Muffin");
if(result->wmPrettyName.length > 0)
{
ffStrbufSet(&result->wmProcessName, processName);
return true;
}
return false;
}
static bool applyDEHintIfDE(const FFstrbuf* processName, DEHint* deHint)
{
if(ffStrbufIgnCaseCompS(processName, "plasmashell") == 0)
2021-05-01 21:22:07 +02:00
*deHint = FF_DE_HINT_PLASMA;
else if(ffStrbufIgnCaseCompS(processName, "gnome-shell") == 0)
*deHint = FF_DE_HINT_GNOME;
2021-05-08 11:20:17 +02:00
else if(ffStrbufIgnCaseCompS(processName, "cinnamon") == 0)
*deHint = FF_DE_HINT_CINNAMON;
2021-05-09 21:41:17 +02:00
else if(ffStrbufIgnCaseCompS(processName, "xfce4-session") == 0)
*deHint = FF_DE_HINT_XFCE4;
2021-05-14 12:43:54 +02:00
else if(ffStrbufIgnCaseCompS(processName, "mate-session") == 0)
*deHint = FF_DE_HINT_MATE;
2021-05-16 14:00:04 +02:00
else if(ffStrbufIgnCaseCompS(processName, "lxqt-session") == 0)
*deHint = FF_DE_HINT_LXQT;
2021-05-01 13:03:30 +02:00
return *deHint != FF_DE_HINT_UNKNOWN;
}
static void getFromProcDir(FFWMDEResult* result, ProcData* procData, bool searchWM)
{
if(procData->proc == NULL)
return;
FFstrbuf procPath;
ffStrbufInitA(&procPath, 64);
ffStrbufAppendS(&procPath, "/proc/");
uint32_t procPathLength = procPath.length;
FFstrbuf processName;
2021-05-08 21:13:58 +02:00
ffStrbufInitA(&processName, 256); //Some processes have large command lines (looking at you chrome)
2021-05-01 13:03:30 +02:00
while((procData->dirent = readdir(procData->proc)) != NULL)
{
2021-05-08 21:13:58 +02:00
if(procData->dirent->d_type != DT_DIR || procData->dirent->d_name[0] == '.')
2021-05-01 13:03:30 +02:00
continue;
ffStrbufAppendS(&procPath, procData->dirent->d_name);
2021-05-01 21:22:07 +02:00
ffStrbufAppendS(&procPath, "/cmdline");
2021-05-01 13:03:30 +02:00
ffGetFileContent(procPath.chars, &processName);
2021-05-09 12:56:07 +02:00
ffStrbufRecalculateLength(&processName); //Arguments are seperated by a \0 char. We make use of this to extract only the executable path. This is needed if arguments contain the / char
2021-05-01 21:22:07 +02:00
ffStrbufSubstrAfterLastC(&processName, '/');
2021-05-01 13:03:30 +02:00
ffStrbufSubstrBefore(&procPath, procPathLength);
//If the are searching for WM, we are also always searching for DE. Therefore !searchWM must be last in the condition
if(applyDEHintIfDE(&processName, &procData->deHint) && !searchWM)
break;
2021-05-09 12:56:07 +02:00
//If we have a WM, dont overwrite it. Therefore searchWM must be first in the condition
2021-05-01 13:03:30 +02:00
if(searchWM && applyPrettyNameIfWM(result, &processName, &procData->protocolHint))
break;
}
ffStrbufDestroy(&processName);
ffStrbufDestroy(&procPath);
}
static inline void getWM(FFWMDEResult* result, ProcData* procData)
{
//If sessionDesktop env is a known WM, set it. Otherwise we might be running a DE, search /proc for known WMs
ffStrbufSetS(&result->wmProcessName, result->sessionDesktop);
if(!applyPrettyNameIfWM(result, &result->wmProcessName, &procData->protocolHint))
getFromProcDir(result, procData, true);
//Fallback for unknown window managers. This will falsely detect DEs as WMs if their WM is unknown
if(result->wmPrettyName.length == 0)
{
ffStrbufSetS(&result->wmProcessName, result->sessionDesktop);
ffStrbufSetS(&result->wmPrettyName, result->sessionDesktop);
}
}
static void getKDE(FFWMDEResult* result)
{
ffStrbufSetS(&result->deProcessName, "plasmashell");
ffStrbufSetS(&result->dePrettyName, "KDE Plasma");
2021-05-15 14:58:43 +02:00
ffParsePropFile("/usr/share/xsessions/plasma.desktop", "X-KDE-PluginInfo-Version =", &result->deVersion);
2021-05-01 21:22:07 +02:00
}
static void getGnome(FFWMDEResult* result)
{
2021-05-08 11:20:17 +02:00
ffStrbufSetS(&result->deProcessName, "gnome-shell");
2021-05-01 21:22:07 +02:00
ffStrbufSetS(&result->dePrettyName, "GNOME");
2021-05-15 14:58:43 +02:00
ffParsePropFile("/usr/share/gnome-shell/org.gnome.Extensions", "version :", &result->deVersion);
2021-05-01 13:03:30 +02:00
}
2021-05-08 11:20:17 +02:00
static void getCinnamon(FFWMDEResult* result)
{
ffStrbufSetS(&result->deProcessName, "cinnamon");
ffStrbufSetS(&result->dePrettyName, "Cinnamon");
2021-05-15 14:58:43 +02:00
ffParsePropFile("/usr/share/applications/cinnamon.desktop", "X-GNOME-Bugzilla-Version =", &result->deVersion);
2021-05-08 11:20:17 +02:00
}
2021-05-14 12:43:54 +02:00
static void getMate(FFinstance* instance, FFWMDEResult* result)
{
ffStrbufSetS(&result->deProcessName, "mate-session");
ffStrbufSetS(&result->dePrettyName, "MATE");
//I parse the file 3 times by purpose, because the properties are not guaranteed to be in order
ffParsePropFile("/usr/share/mate-about/mate-version.xml", "<platform>", &result->deVersion);
ffStrbufAppendC(&result->deVersion, '.');
ffParsePropFile("/usr/share/mate-about/mate-version.xml", "<minor>", &result->deVersion);
ffStrbufAppendC(&result->deVersion, '.');
ffParsePropFile("/usr/share/mate-about/mate-version.xml", "<micro>", &result->deVersion);
if(result->deVersion.length == 0 && instance->config.allowSlowOperations)
{
ffProcessAppendStdOut(&result->deVersion, (char* const[]){
"mate-session",
"--version",
NULL
});
ffStrbufSubstrAfterFirstC(&result->deVersion, ' ');
ffStrbufTrim(&result->deVersion, ' ');
}
}
2021-05-09 21:41:17 +02:00
static void getXFCE4(FFinstance* instance, FFWMDEResult* result)
{
ffStrbufSetS(&result->deProcessName, "xfce4-session");
ffStrbufSetS(&result->dePrettyName, "Xfce4");
2021-05-15 14:58:43 +02:00
ffParsePropFile("/usr/share/gtk-doc/html/libxfce4ui/index.html", "<div><p class=\"releaseinfo\">Version", &result->deVersion);
2021-05-09 21:41:17 +02:00
if(result->deVersion.length == 0 && instance->config.allowSlowOperations)
{
//This is really, really slow. Thank you, XFCE developers
ffProcessAppendStdOut(&result->deVersion, (char* const[]){
"xfce4-session",
"--version",
NULL
});
ffStrbufSubstrBeforeFirstC(&result->deVersion, '(');
ffStrbufSubstrAfterFirstC(&result->deVersion, ' ');
ffStrbufTrim(&result->deVersion, ' ');
}
}
2021-05-16 14:00:04 +02:00
static void getLXQt(FFinstance* instance, FFWMDEResult* result)
{
ffStrbufSetS(&result->deProcessName, "lxqt-session");
ffStrbufSetS(&result->dePrettyName, "LXQt");
2021-05-17 11:10:56 +02:00
ffParsePropFile("/usr/lib/pkgconfig/lxqt.pc", "Version:", &result->deVersion);
if(result->deVersion.length == 0)
ffParsePropFile("/usr/share/cmake/lxqt/lxqt-config.cmake", "set ( LXQT_VERSION", &result->deVersion);
if(result->deVersion.length == 0)
ffParsePropFile("/usr/share/cmake/lxqt/lxqt-config-version.cmake", "set ( PACKAGE_VERSION", &result->deVersion);
2021-05-16 14:00:04 +02:00
2021-05-17 06:34:48 +02:00
if(result->deVersion.length == 0 && instance->config.allowSlowOperations)
2021-05-16 14:00:04 +02:00
{
2021-05-17 06:34:48 +02:00
//This is really, really, really slow. Thank you, LXQt developers
2021-05-16 14:00:04 +02:00
ffProcessAppendStdOut(&result->deVersion, (char* const[]){
"lxqt-session",
"-v",
NULL
});
2021-05-17 11:10:56 +02:00
result->deVersion.length = 0; //don't set '\0' byte
ffGetPropValueFromLines(result->deVersion.chars , "liblxqt", &result->deVersion);
2021-05-16 14:00:04 +02:00
}
}
2021-05-09 21:41:17 +02:00
static inline void getDEFromHint(FFinstance* instance, FFWMDEResult* result, ProcData* procData)
2021-05-01 13:03:30 +02:00
{
getFromProcDir(result, procData, false);
if(procData->deHint == FF_DE_HINT_UNKNOWN)
return;
2021-05-01 21:22:07 +02:00
if(procData->deHint == FF_DE_HINT_PLASMA)
2021-05-01 13:03:30 +02:00
getKDE(result);
2021-05-01 21:22:07 +02:00
else if(procData->deHint == FF_DE_HINT_GNOME)
getGnome(result);
2021-05-08 11:20:17 +02:00
else if(procData->deHint == FF_DE_HINT_CINNAMON)
getCinnamon(result);
2021-05-09 21:41:17 +02:00
else if(procData->deHint == FF_DE_HINT_XFCE4)
getXFCE4(instance, result);
2021-05-14 12:43:54 +02:00
else if(procData->deHint == FF_DE_HINT_MATE)
getMate(instance, result);
2021-05-16 14:00:04 +02:00
else if(procData->deHint == FF_DE_HINT_LXQT)
getLXQt(instance, result);
2021-05-01 13:03:30 +02:00
}
2021-05-09 21:41:17 +02:00
static inline void getDE(FFinstance* instance, FFWMDEResult* result, ProcData* procData)
2021-05-01 13:03:30 +02:00
{
// if sessionDesktop is not set or sessionDesktiop == WM, try finding DE via /proc
if(
result->sessionDesktop == NULL ||
*result->sessionDesktop == '\0' ||
ffStrbufIgnCaseCompS(&result->wmProcessName, result->sessionDesktop) == 0 ||
ffStrbufIgnCaseCompS(&result->wmPrettyName, result->sessionDesktop) == 0
) {
2021-05-09 21:41:17 +02:00
getDEFromHint(instance, result, procData);
2021-05-01 13:03:30 +02:00
return;
}
2021-05-08 12:14:55 +02:00
if(strcasecmp(result->sessionDesktop, "KDE") == 0 || strcasecmp(result->sessionDesktop, "plasma") == 0 || strcasecmp(result->sessionDesktop, "plasmashell") == 0)
2021-05-01 13:03:30 +02:00
getKDE(result);
2021-05-01 21:22:07 +02:00
else if(strcasecmp(result->sessionDesktop, "Gnome") == 0 || strcasecmp(result->sessionDesktop, "ubuntu:GNOME") == 0 || strcasecmp(result->sessionDesktop, "ubuntu") == 0)
getGnome(result);
2021-05-08 11:20:17 +02:00
else if(strcasecmp(result->sessionDesktop, "X-Cinnamon") == 0 || strcasecmp(result->sessionDesktop, "Cinnamon") == 0)
getCinnamon(result);
2021-05-09 21:41:17 +02:00
else if(strcasecmp(result->sessionDesktop, "XFCE") == 0 || strcasecmp(result->sessionDesktop, "X-XFCE") == 0 || strcasecmp(result->sessionDesktop, "XFCE4") == 0 || strcasecmp(result->sessionDesktop, "X-XFCE4") == 0)
getXFCE4(instance, result);
2021-05-14 12:43:54 +02:00
else if(strcasecmp(result->sessionDesktop, "MATE") == 0 || strcasecmp(result->sessionDesktop, "X-MATE") == 0)
getMate(instance, result);
2021-05-16 14:00:04 +02:00
else if(strcasecmp(result->sessionDesktop, "LXQt") == 0 || strcasecmp(result->sessionDesktop, "X-LXQT") == 0)
getLXQt(instance, result);
2021-05-01 13:03:30 +02:00
else
{
ffStrbufSetS(&result->deProcessName, result->sessionDesktop);
ffStrbufSet(&result->dePrettyName, &result->deProcessName);
2021-05-05 19:49:40 +02:00
if(ffStrbufStartsWithIgnCaseS(&result->dePrettyName, "X-"))
2021-05-05 21:26:42 +02:00
ffStrbufSubstrAfter(&result->dePrettyName, 1);
2021-05-01 13:03:30 +02:00
}
}
static void getSessionTypeFallback(FFWMDEResult* result, ProtocolHint protocolHint)
{
if(protocolHint == FF_PROTOCOL_HINT_WAYLAND)
ffStrbufSetS(&result->wmProtocolName, "Wayland");
else if(protocolHint == FF_PROTOCOL_HINT_X11)
ffStrbufSetS(&result->wmProtocolName, "X11");
else if(getenv("WAYLAND_DISPLAY") != NULL)
ffStrbufSetS(&result->wmProtocolName, "Wayland");
}
static inline void getSessionType(FFWMDEResult* result, ProtocolHint protocolHint)
{
const char* xdgSessionType = getenv("XDG_SESSION_TYPE");
if (xdgSessionType == NULL)
{
getSessionTypeFallback(result, protocolHint);
return;
}
if(strcasecmp(xdgSessionType, "wayland") == 0)
ffStrbufSetS(&result->wmProtocolName, "Wayland");
else if(strcasecmp(xdgSessionType, "x11") == 0)
ffStrbufSetS(&result->wmProtocolName, "X11");
else if(strcasecmp(xdgSessionType, "tty") == 0)
ffStrbufSetS(&result->wmProtocolName, "TTY");
else if(strcasecmp(xdgSessionType, "mir") == 0)
ffStrbufSetS(&result->wmProtocolName, "Mir");
else
ffStrbufSetS(&result->wmProtocolName, xdgSessionType);
// $XDG_SESSION_TYPE is empty
if(result->wmProtocolName.length == 0)
getSessionTypeFallback(result, protocolHint);
}
2021-05-09 21:41:17 +02:00
static inline void getWMDE(FFinstance* instance, FFWMDEResult* result)
2021-05-01 16:54:59 +02:00
{
ProcData procData;
procData.proc = opendir("/proc");
procData.dirent = NULL;
procData.protocolHint = FF_PROTOCOL_HINT_UNKNOWN;
procData.deHint = FF_DE_HINT_UNKNOWN;
getSessionDesktop(result);
getWM(result, &procData);
2021-05-09 21:41:17 +02:00
getDE(instance, result, &procData);
2021-05-01 16:54:59 +02:00
if(result->wmProtocolName.length == 0 && procData.protocolHint != FF_PROTOCOL_HINT_UNKNOWN)
2021-05-01 17:19:17 +02:00
getSessionTypeFallback(result, procData.protocolHint);
2021-05-01 16:54:59 +02:00
if(procData.proc != NULL)
closedir(procData.proc);
}
2021-05-01 13:03:30 +02:00
const FFWMDEResult* ffDetectWMDE(FFinstance* instance)
{
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static FFWMDEResult result;
static bool init = false;
pthread_mutex_lock(&mutex);
if(init)
{
pthread_mutex_unlock(&mutex);
return &result;
}
init = true;
ffStrbufInit(&result.wmProcessName);
ffStrbufInit(&result.wmPrettyName);
ffStrbufInit(&result.wmProtocolName);
ffStrbufInit(&result.deProcessName);
ffStrbufInit(&result.dePrettyName);
ffStrbufInit(&result.deVersion);
2021-05-01 16:54:59 +02:00
getSessionType(&result, FF_PROTOCOL_HINT_UNKNOWN);
2021-05-01 13:03:30 +02:00
2021-05-01 16:54:59 +02:00
//Don't run anyting when on TTY. This prevents us to catch process from other users in at least that case.
if(ffStrbufIgnCaseCompS(&result.wmProtocolName, "TTY") != 0)
2021-05-09 21:41:17 +02:00
getWMDE(instance, &result);
2021-05-01 13:03:30 +02:00
pthread_mutex_unlock(&mutex);
return &result;
}