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 ;
2021-05-30 20:28:15 +02:00
static void getSessionDesktop ( FFWMDEResult * result )
2021-05-01 13:03:30 +02:00
{
result -> sessionDesktop = getenv ( "XDG_CURRENT_DESKTOP" );
2021-05-30 20:28:15 +02:00
if ( result -> sessionDesktop != NULL && * result -> sessionDesktop != '\0' )
2021-05-01 13:03:30 +02:00
return ;
result -> sessionDesktop = getenv ( "XDG_SESSION_DESKTOP" );
2021-05-30 20:28:15 +02:00
if ( result -> sessionDesktop != NULL && * result -> sessionDesktop != '\0' )
2021-05-01 13:03:30 +02:00
return ;
result -> sessionDesktop = getenv ( "CURRENT_DESKTOP" );
2021-05-30 20:28:15 +02:00
if ( result -> sessionDesktop != NULL && * result -> sessionDesktop != '\0' )
2021-05-01 13:03:30 +02:00
return ;
result -> sessionDesktop = getenv ( "SESSION_DESKTOP" );
2021-05-30 20:28:15 +02:00
if ( result -> sessionDesktop != NULL && * result -> sessionDesktop != '\0' )
2021-05-01 13:03:30 +02:00
return ;
2021-05-30 20:28:15 +02:00
char * env ;
env = getenv ( "DESKTOP_SESSION" );
if ( env != NULL && * env != '\0' )
2021-05-01 13:03:30 +02:00
{
2021-05-30 20:28:15 +02:00
if ( strcasecmp ( env , "plasma" ) == 0 )
2021-05-01 13:03:30 +02:00
result -> sessionDesktop = "KDE" ;
else
2021-05-30 20:28:15 +02:00
result -> sessionDesktop = env ;
2021-05-01 13:03:30 +02:00
return ;
}
2021-05-30 20:28:15 +02:00
env = getenv ( "GNOME_DESKTOP_SESSION_ID" );
if ( env != NULL )
2021-05-01 13:03:30 +02:00
{
result -> sessionDesktop = "Gnome" ;
return ;
}
2021-05-30 20:28:15 +02:00
env = getenv ( "MATE_DESKTOP_SESSION_ID" );
if ( env != NULL )
2021-05-01 13:03:30 +02:00
{
result -> sessionDesktop = "Mate" ;
return ;
}
2021-05-30 20:28:15 +02:00
env = getenv ( "TDE_FULL_SESSION" );
if ( env != NULL )
2021-05-01 13:03:30 +02:00
{
result -> sessionDesktop = "Trinity" ;
return ;
}
}
2021-05-30 20:28:15 +02:00
static void applyPrettyNameIfWM ( FFWMDEResult * result , const char * processName , ProtocolHint * protocolHint )
2021-05-01 13:03:30 +02:00
{
2021-05-30 20:28:15 +02:00
if ( strcasecmp ( processName , "kwin_wayland" ) == 0 )
2021-05-01 13:03:30 +02:00
{
ffStrbufSetS ( & result -> wmPrettyName , "KWin" );
* protocolHint = FF_PROTOCOL_HINT_WAYLAND ;
}
2021-05-30 20:28:15 +02:00
else if ( strcasecmp ( processName , "kwin_x11" ) == 0 )
2021-05-01 13:03:30 +02:00
{
ffStrbufSetS ( & result -> wmPrettyName , "KWin" );
* protocolHint = FF_PROTOCOL_HINT_X11 ;
}
2021-05-30 20:28:15 +02:00
else if ( strcasecmp ( processName , "sway" ) == 0 )
2021-05-01 13:03:30 +02:00
{
ffStrbufSetS ( & result -> wmPrettyName , "Sway" );
* protocolHint = FF_PROTOCOL_HINT_WAYLAND ;
}
2021-05-30 20:28:15 +02:00
else if ( strcasecmp ( processName , "weston" ) == 0 )
2021-05-01 13:03:30 +02:00
{
ffStrbufSetS ( & result -> wmPrettyName , "Weston" );
* protocolHint = FF_PROTOCOL_HINT_WAYLAND ;
}
2021-05-30 20:28:15 +02:00
else if ( strcasecmp ( processName , "wayfire" ) == 0 )
2021-05-01 13:03:30 +02:00
{
ffStrbufSetS ( & result -> wmPrettyName , "Wayfire" );
* protocolHint = FF_PROTOCOL_HINT_WAYLAND ;
}
2021-05-30 20:28:15 +02:00
else if ( strcasecmp ( processName , "openbox" ) == 0 )
2021-05-01 13:03:30 +02:00
{
ffStrbufSetS ( & result -> wmPrettyName , "Openbox" );
* protocolHint = FF_PROTOCOL_HINT_X11 ;
}
2021-05-30 20:28:15 +02:00
else if ( strcasecmp ( processName , "xfwm4" ) == 0 )
2021-05-01 13:03:30 +02:00
{
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-30 20:28:15 +02:00
else if ( strcasecmp ( processName , "Marco" ) == 0 )
2021-05-14 12:43:54 +02:00
{
ffStrbufSetS ( & result -> wmPrettyName , "Marco" );
* protocolHint = FF_PROTOCOL_HINT_X11 ;
}
2021-05-30 20:28:15 +02:00
else if ( strcasecmp ( processName , "gnome-session-binary" ) == 0 || strcasecmp ( processName , "Mutter" ) == 0 )
2021-05-01 13:03:30 +02:00
ffStrbufSetS ( & result -> wmPrettyName , "Mutter" );
2021-05-30 20:28:15 +02:00
else if ( strcasecmp ( processName , "cinnamon-session" ) == 0 || strcasecmp ( processName , "Muffin" ) == 0 )
2021-05-01 13:03:30 +02:00
ffStrbufSetS ( & result -> wmPrettyName , "Muffin" );
2021-05-30 20:28:15 +02:00
if ( result -> wmPrettyName . length > 0 && result -> wmProcessName . length == 0 )
ffStrbufSetS ( & result -> wmProcessName , processName );
2021-05-01 13:03:30 +02:00
}
2021-05-30 20:28:15 +02:00
static void applyBetterWM ( FFWMDEResult * result , const char * processName , ProtocolHint * protocolHint )
2021-05-01 13:03:30 +02:00
{
2021-05-30 20:28:15 +02:00
if ( processName == NULL || * processName == '\0' )
2021-05-01 13:03:30 +02:00
return ;
2021-05-30 20:28:15 +02:00
ffStrbufSetS ( & result -> wmProcessName , processName );
applyPrettyNameIfWM ( result , processName , protocolHint );
2021-05-01 13:03:30 +02:00
if ( result -> wmPrettyName . length == 0 )
2021-05-30 20:28:15 +02:00
ffStrbufAppend ( & result -> wmPrettyName , & result -> wmProcessName );
2021-05-01 13:03:30 +02:00
}
2021-05-30 20:28:15 +02:00
static void getKDE ( FFWMDEResult * result , ProtocolHint * protocolHint )
2021-05-01 13:03:30 +02:00
{
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-30 20:28:15 +02:00
applyBetterWM ( result , getenv ( "KDEWM" ), protocolHint );
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 );
2021-05-30 20:28:15 +02:00
if ( result -> deVersion . length > 0 )
{
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 );
}
2021-05-14 12:43:54 +02:00
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 )
{
2021-05-30 20:28:15 +02:00
//This is somewhat slow
2021-05-09 21:41:17 +02:00
ffProcessAppendStdOut ( & result -> deVersion , ( char * const []){
"xfce4-session" ,
"--version" ,
NULL
});
ffStrbufSubstrBeforeFirstC ( & result -> deVersion , '(' );
ffStrbufSubstrAfterFirstC ( & result -> deVersion , ' ' );
ffStrbufTrim ( & result -> deVersion , ' ' );
}
}
2021-05-30 20:28:15 +02:00
static void getLXQt ( FFinstance * instance , FFWMDEResult * result , ProtocolHint * protocolHint )
2021-05-16 14:00:04 +02:00
{
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-30 20:28:15 +02:00
FFstrbuf wmProcessNameBuffer ;
ffStrbufInit ( & wmProcessNameBuffer );
2021-05-01 13:03:30 +02:00
2021-05-30 20:28:15 +02:00
ffParsePropFileConfig ( instance , "lxqt/session.conf" , "window_manager =" , & wmProcessNameBuffer );
applyBetterWM ( result , wmProcessNameBuffer . chars , protocolHint );
2021-05-01 13:03:30 +02:00
2021-05-30 20:28:15 +02:00
ffStrbufDestroy ( & wmProcessNameBuffer );
2021-05-01 13:03:30 +02:00
}
2021-05-30 20:28:15 +02:00
static void applyPrettyNameIfDE ( FFinstance * instance , FFWMDEResult * result , const char * name , ProtocolHint * protocolHint )
2021-05-01 13:03:30 +02:00
{
2021-05-30 20:28:15 +02:00
if ( name == NULL || * name == '\0' )
2021-05-01 13:03:30 +02:00
return ;
2021-05-30 20:28:15 +02:00
if ( strcasecmp ( name , "KDE" ) == 0 || strcasecmp ( name , "plasma" ) == 0 || strcasecmp ( name , "plasmashell" ) == 0 )
getKDE ( result , protocolHint );
else if ( strcasecmp ( name , "Gnome" ) == 0 || strcasecmp ( name , "ubuntu:GNOME" ) == 0 || strcasecmp ( name , "ubuntu" ) == 0 || strcasecmp ( name , "gnome-shell" ) == 0 )
2021-05-01 21:22:07 +02:00
getGnome ( result );
2021-05-30 20:28:15 +02:00
else if ( strcasecmp ( name , "X-Cinnamon" ) == 0 || strcasecmp ( name , "Cinnamon" ) == 0 )
2021-05-08 11:20:17 +02:00
getCinnamon ( result );
2021-05-30 20:28:15 +02:00
else if ( strcasecmp ( name , "XFCE" ) == 0 || strcasecmp ( name , "X-XFCE" ) == 0 || strcasecmp ( name , "XFCE4" ) == 0 || strcasecmp ( name , "X-XFCE4" ) == 0 || strcasecmp ( name , "xfce4-session" ) == 0 )
2021-05-09 21:41:17 +02:00
getXFCE4 ( instance , result );
2021-05-30 20:28:15 +02:00
else if ( strcasecmp ( name , "MATE" ) == 0 || strcasecmp ( name , "X-MATE" ) == 0 || strcasecmp ( name , "mate-session" ) == 0 )
2021-05-14 12:43:54 +02:00
getMate ( instance , result );
2021-05-30 20:28:15 +02:00
else if ( strcasecmp ( name , "LXQt" ) == 0 || strcasecmp ( name , "X-LXQT" ) == 0 || strcasecmp ( name , "lxqt-session" ) == 0 )
getLXQt ( instance , result , protocolHint );
2021-05-01 13:03:30 +02:00
}
2021-05-30 20:28:15 +02:00
static void getSessionTypeFromHint ( FFWMDEResult * result , ProtocolHint protocolHint )
2021-05-01 13:03:30 +02:00
{
2021-05-30 20:28:15 +02:00
if (
result -> wmProtocolName . length > 0 ||
protocolHint == FF_PROTOCOL_HINT_UNKNOWN
) return ;
2021-05-01 13:03:30 +02:00
if ( protocolHint == FF_PROTOCOL_HINT_WAYLAND )
ffStrbufSetS ( & result -> wmProtocolName , "Wayland" );
else if ( protocolHint == FF_PROTOCOL_HINT_X11 )
ffStrbufSetS ( & result -> wmProtocolName , "X11" );
}
2021-05-30 20:28:15 +02:00
static void getSessionTypeFromEnv ( FFWMDEResult * result )
2021-05-01 13:03:30 +02:00
{
2021-05-30 20:28:15 +02:00
const char * env = getenv ( "XDG_SESSION_TYPE" );
if ( env != NULL && * env != '\0' )
2021-05-01 13:03:30 +02:00
{
2021-05-30 20:28:15 +02:00
if ( strcasecmp ( env , "wayland" ) == 0 )
ffStrbufSetS ( & result -> wmProtocolName , "Wayland" );
else if ( strcasecmp ( env , "x11" ) == 0 )
ffStrbufSetS ( & result -> wmProtocolName , "X11" );
else if ( strcasecmp ( env , "tty" ) == 0 )
ffStrbufSetS ( & result -> wmProtocolName , "TTY" );
else if ( strcasecmp ( env , "mir" ) == 0 )
ffStrbufSetS ( & result -> wmProtocolName , "Mir" );
else
ffStrbufSetS ( & result -> wmProtocolName , env );
2021-05-01 13:03:30 +02:00
return ;
}
2021-05-30 20:28:15 +02:00
env = getenv ( "WAYLAND_DISPLAY" );
if ( env != NULL && * env != '\0' )
{
2021-05-01 13:03:30 +02:00
ffStrbufSetS ( & result -> wmProtocolName , "Wayland" );
2021-05-30 20:28:15 +02:00
return ;
}
env = getenv ( "TERM" );
if ( env != NULL && * env != '\0' )
{
if ( strcasecmp ( env , "linux" ) == 0 )
{
ffStrbufSetS ( & result -> wmProtocolName , "TTY" );
return ;
}
}
}
static void getFromProcDir ( FFinstance * instance , FFWMDEResult * result , ProtocolHint * protocolHint )
{
DIR * proc = opendir ( "/proc" );
if ( proc == NULL )
return ;
FFstrbuf procPath ;
ffStrbufInitA ( & procPath , 64 );
ffStrbufAppendS ( & procPath , "/proc/" );
uint32_t procPathLength = procPath . length ;
FFstrbuf processName ;
ffStrbufInitA ( & processName , 256 ); //Some processes have large command lines (looking at you chrome)
struct dirent * dirent ;
while (( dirent = readdir ( proc )) != NULL )
{
if ( dirent -> d_type != DT_DIR || dirent -> d_name [ 0 ] == '.' )
continue ;
ffStrbufAppendS ( & procPath , dirent -> d_name );
ffStrbufAppendS ( & procPath , "/cmdline" );
ffGetFileContent ( procPath . chars , & processName );
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
ffStrbufSubstrAfterLastC ( & processName , '/' );
ffStrbufSubstrBefore ( & procPath , procPathLength );
if ( result -> dePrettyName . length == 0 )
applyPrettyNameIfDE ( instance , result , processName . chars , protocolHint );
if ( result -> wmPrettyName . length == 0 )
applyPrettyNameIfWM ( result , processName . chars , protocolHint );
if ( result -> deProcessName . length > 0 && result -> wmPrettyName . length > 0 )
break ;
}
closedir ( proc );
ffStrbufDestroy ( & processName );
ffStrbufDestroy ( & procPath );
2021-05-01 13:03:30 +02:00
}
2021-05-30 20:28:15 +02:00
void getWMDE ( FFinstance * instance , FFWMDEResult * result )
2021-05-01 16:54:59 +02:00
{
2021-05-30 20:28:15 +02:00
ProtocolHint protocolHint ;
//If sessionDesktop is a known DE, set it. Some DEs even have config files which tell us the WM, so we can return very fast in this case
applyPrettyNameIfDE ( instance , result , result -> sessionDesktop , & protocolHint );
if ( result -> dePrettyName . length > 0 && result -> wmPrettyName . length > 0 )
{
getSessionTypeFromHint ( result , protocolHint );
return ;
}
2021-05-01 16:54:59 +02:00
2021-05-30 20:28:15 +02:00
//If sessionDesktop is a known WM, set it.
applyPrettyNameIfWM ( result , result -> sessionDesktop , & protocolHint );
2021-05-01 16:54:59 +02:00
2021-05-30 20:28:15 +02:00
//Search for missing DE / WM in processes
getFromProcDir ( instance , result , & protocolHint );
//Fallback for unknown WMs and DEs. This will falsely detect DEs as WMs if they (and their used WM) are unknown
if ( result -> wmPrettyName . length == 0 && result -> dePrettyName . length == 0 )
{
ffStrbufSetS ( & result -> wmProcessName , result -> sessionDesktop );
ffStrbufSetS ( & result -> wmPrettyName , result -> sessionDesktop );
}
2021-05-01 16:54:59 +02:00
2021-05-30 20:28:15 +02:00
getSessionTypeFromHint ( result , protocolHint );
2021-05-01 16:54:59 +02:00
}
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-30 20:28:15 +02:00
getSessionDesktop ( & result );
getSessionTypeFromEnv ( & result );
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 ;
}