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-01 21:22:07 +02:00
else if ( ffStrbufIgnCaseCompS ( processName , "gnome-session-binary" ) == 0 )
2021-05-01 13:03:30 +02:00
ffStrbufSetS ( & result -> wmPrettyName , "Mutter" );
2021-05-08 11:20:17 +02:00
else if ( ffStrbufIgnCaseCompS ( processName , "cinnamon-session" ) == 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 06:34:48 +02:00
ffParsePropFile ( "/usr/share/cmake/lxqt/lxqt-config.cmake" , "set(LXQT_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 06:34:48 +02:00
FF_STRBUF_CREATE ( tmp );
ffGetPropValueFromLines ( result -> deVersion . chars , "liblxqt" , & tmp );
ffStrbufSet ( & result -> deVersion , & tmp );
ffStrbufDestroy ( & tmp );
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 ;
}