2023-12-07 11:41:09 +01:00
# include "PluginManager.hpp"
# include "../helpers/Colors.hpp"
2024-10-13 14:23:33 +02:00
# include "../helpers/StringUtils.hpp"
2023-12-07 11:41:09 +01:00
# include "../progress/CProgressBar.hpp"
# include "Manifest.hpp"
# include "DataState.hpp"
2024-10-13 14:23:33 +02:00
# include <cstdio>
2023-12-07 11:41:09 +01:00
# include <iostream>
# include <array>
# include <filesystem>
2024-10-13 14:23:33 +02:00
# include <print>
2023-12-07 11:41:09 +01:00
# include <thread>
# include <fstream>
# include <algorithm>
2024-02-01 20:38:43 +01:00
# include <format>
2023-12-07 11:41:09 +01:00
2024-04-28 21:27:44 +02:00
# include <sys/types.h>
# include <sys/stat.h>
# include <pwd.h>
2024-04-30 15:14:31 +02:00
# include <unistd.h>
2024-04-28 21:27:44 +02:00
2023-12-07 11:41:09 +01:00
# include <toml++/toml.hpp>
2024-06-11 17:17:45 +02:00
# include <hyprutils/string/String.hpp>
2024-11-09 18:11:38 +01:00
# include <hyprutils/os/Process.hpp>
2024-06-11 17:17:45 +02:00
using namespace Hyprutils : : String ;
2024-11-09 18:11:38 +01:00
using namespace Hyprutils : : OS ;
2023-12-21 22:01:50 +01:00
static std : : string execAndGet ( std : : string cmd ) {
2023-12-07 11:41:09 +01:00
cmd + = " 2>&1 " ;
2024-11-09 18:11:38 +01:00
2024-11-10 23:53:11 +01:00
CProcess proc ( " /bin/sh " , { " -c " , cmd } ) ;
2024-11-10 16:54:00 +01:00
2024-11-09 18:11:38 +01:00
if ( ! proc . runSync ( ) )
return " error " ;
return proc . stdOut ( ) ;
2023-12-07 11:41:09 +01:00
}
2024-11-23 15:18:13 +01:00
static std : : string getTempRoot ( ) {
static auto ENV = getenv ( " XDG_RUNTIME_DIR " ) ;
if ( ! ENV ) {
std : : cerr < < " \n ERROR: XDG_RUNTIME_DIR not set! \n " ;
exit ( 1 ) ;
}
const auto STR = ENV + std : : string { " /hyprpm/ " } ;
return STR ;
}
2024-12-11 17:49:09 +01:00
SHyprlandVersion CPluginManager : : getHyprlandVersion ( bool running ) {
static bool onceRunning = false ;
static bool onceInstalled = false ;
static SHyprlandVersion verRunning ;
static SHyprlandVersion verInstalled ;
2023-12-07 11:41:09 +01:00
2024-12-11 17:49:09 +01:00
if ( onceRunning & & running )
return verRunning ;
2023-12-07 11:41:09 +01:00
2024-12-11 17:49:09 +01:00
if ( onceInstalled & & ! running )
return verInstalled ;
if ( running )
onceRunning = true ;
else
onceInstalled = true ;
const auto HLVERCALL = running ? execAndGet ( " hyprctl version " ) : execAndGet ( " Hyprland --version " ) ;
2023-12-07 11:41:09 +01:00
if ( m_bVerbose )
2024-12-11 17:49:09 +01:00
std : : println ( " {} " , verboseString ( " {} version returned: {} " , running ? " running " : " installed " , HLVERCALL ) ) ;
2023-12-07 11:41:09 +01:00
if ( ! HLVERCALL . contains ( " Tag: " ) ) {
2024-10-13 14:23:33 +02:00
std : : println ( stderr , " \n {} " , failureString ( " You don't seem to be running Hyprland. " ) ) ;
2023-12-07 11:41:09 +01:00
return SHyprlandVersion { } ;
}
std : : string hlcommit = HLVERCALL . substr ( HLVERCALL . find ( " at commit " ) + 10 ) ;
hlcommit = hlcommit . substr ( 0 , hlcommit . find_first_of ( ' ' ) ) ;
std : : string hlbranch = HLVERCALL . substr ( HLVERCALL . find ( " from branch " ) + 12 ) ;
hlbranch = hlbranch . substr ( 0 , hlbranch . find ( " at commit " ) ) ;
2024-04-10 18:33:50 +02:00
std : : string hldate = HLVERCALL . substr ( HLVERCALL . find ( " Date: " ) + 6 ) ;
hldate = hldate . substr ( 0 , hldate . find ( " \n " ) ) ;
2024-04-05 01:44:21 +02:00
std : : string hlcommits ;
if ( HLVERCALL . contains ( " commits: " ) ) {
2024-04-05 05:40:44 +02:00
hlcommits = HLVERCALL . substr ( HLVERCALL . find ( " commits: " ) + 9 ) ;
2024-04-05 01:44:21 +02:00
hlcommits = hlcommits . substr ( 0 , hlcommits . find ( " " ) ) ;
}
int commits = 0 ;
try {
commits = std : : stoi ( hlcommits ) ;
} catch ( . . . ) { ; }
2023-12-07 11:41:09 +01:00
if ( m_bVerbose )
2024-10-13 14:23:33 +02:00
std : : println ( " {} " , verboseString ( " parsed commit {} at branch {} on {}, commits {} " , hlcommit , hlbranch , hldate , commits ) ) ;
2023-12-07 11:41:09 +01:00
2024-12-11 17:49:09 +01:00
auto ver = SHyprlandVersion { hlbranch , hlcommit , hldate , commits } ;
if ( running )
verRunning = ver ;
else
verInstalled = ver ;
2023-12-07 11:41:09 +01:00
return ver ;
}
2024-04-28 21:27:44 +02:00
bool CPluginManager : : createSafeDirectory ( const std : : string & path ) {
2024-11-23 15:18:13 +01:00
if ( path . empty ( ) | | ! path . starts_with ( getTempRoot ( ) ) )
2024-04-28 21:27:44 +02:00
return false ;
if ( std : : filesystem : : exists ( path ) )
std : : filesystem : : remove_all ( path ) ;
if ( std : : filesystem : : exists ( path ) )
return false ;
if ( mkdir ( path . c_str ( ) , S_IRWXU ) < 0 )
return false ;
return true ;
}
2024-03-06 13:01:04 +01:00
bool CPluginManager : : addNewPluginRepo ( const std : : string & url , const std : : string & rev ) {
2024-02-01 20:38:43 +01:00
const auto HLVER = getHyprlandVersion ( ) ;
2024-04-16 17:58:57 +02:00
if ( ! hasDeps ( ) ) {
2024-11-28 15:21:55 +01:00
std : : println ( stderr , " \n {} " , failureString ( " Could not clone the plugin repository. Dependencies not satisfied. Hyprpm requires: cmake, meson, cpio, pkg-config " ) ) ;
2024-04-16 17:58:57 +02:00
return false ;
}
2023-12-07 11:41:09 +01:00
if ( DataState : : pluginRepoExists ( url ) ) {
2024-10-13 14:23:33 +02:00
std : : println ( stderr , " \n {} " , failureString ( " Could not clone the plugin repository. Repository already installed. " ) ) ;
2023-12-07 11:41:09 +01:00
return false ;
}
auto GLOBALSTATE = DataState : : getGlobalState ( ) ;
if ( ! GLOBALSTATE . dontWarnInstall ) {
2024-10-13 14:23:33 +02:00
std : : println ( " {}!{} Disclaimer: {} " , Colors : : YELLOW , Colors : : RED , Colors : : RESET ) ;
std : : println ( " plugins, especially not official, have no guarantee of stability, availablity or security. \n "
" Run them at your own risk. \n "
" This message will not appear again. " ) ;
2023-12-07 11:41:09 +01:00
GLOBALSTATE . dontWarnInstall = true ;
DataState : : updateGlobalState ( GLOBALSTATE ) ;
}
std : : cout < < Colors : : GREEN < < " ✔ " < < Colors : : RESET < < Colors : : RED < < " adding a new plugin repository " < < Colors : : RESET < < " from " < < url < < " \n " < < Colors : : RED
< < " MAKE SURE " < < Colors : : RESET < < " that you trust the authors. " < < Colors : : RED < < " DO NOT " < < Colors : : RESET
< < " install random plugins without verifying the code and author. \n "
< < " Are you sure? [Y/n] " ;
std : : fflush ( stdout ) ;
std : : string input ;
std : : getline ( std : : cin , input ) ;
if ( input . size ( ) > 0 & & input [ 0 ] ! = ' Y ' & & input [ 0 ] ! = ' y ' ) {
2024-10-13 14:23:33 +02:00
std : : println ( stderr , " Aborting. " ) ;
2023-12-07 11:41:09 +01:00
return false ;
}
CProgressBar progress ;
progress . m_iMaxSteps = 5 ;
progress . m_iSteps = 0 ;
progress . m_szCurrentMessage = " Cloning the plugin repository " ;
progress . print ( ) ;
2024-11-23 15:18:13 +01:00
if ( ! std : : filesystem : : exists ( getTempRoot ( ) ) ) {
std : : filesystem : : create_directory ( getTempRoot ( ) ) ;
std : : filesystem : : permissions ( getTempRoot ( ) , std : : filesystem : : perms : : owner_all , std : : filesystem : : perm_options : : replace ) ;
} else if ( ! std : : filesystem : : is_directory ( getTempRoot ( ) ) ) {
2024-10-13 14:23:33 +02:00
std : : println ( stderr , " \n {} " , failureString ( " Could not prepare working dir for hyprpm " ) ) ;
2024-04-28 21:27:44 +02:00
return false ;
2023-12-07 11:41:09 +01:00
}
2024-04-28 21:27:44 +02:00
const std : : string USERNAME = getpwuid ( getuid ( ) ) - > pw_name ;
2024-11-23 15:18:13 +01:00
m_szWorkingPluginDirectory = getTempRoot ( ) + USERNAME ;
2024-04-28 21:27:44 +02:00
if ( ! createSafeDirectory ( m_szWorkingPluginDirectory ) ) {
2024-10-13 14:23:33 +02:00
std : : println ( stderr , " \n {} " , failureString ( " Could not prepare working dir for repo " ) ) ;
2024-04-28 21:27:44 +02:00
return false ;
2023-12-07 11:41:09 +01:00
}
2024-10-13 14:23:33 +02:00
progress . printMessageAbove ( infoString ( " Cloning {} " , url ) ) ;
2023-12-07 11:41:09 +01:00
2024-11-23 15:18:13 +01:00
std : : string ret = execAndGet ( std : : format ( " cd {} && git clone --recursive {} {} " , getTempRoot ( ) , url , USERNAME ) ) ;
2023-12-07 11:41:09 +01:00
2024-04-28 21:27:44 +02:00
if ( ! std : : filesystem : : exists ( m_szWorkingPluginDirectory + " /.git " ) ) {
2024-10-13 14:23:33 +02:00
std : : println ( stderr , " \n {} " , failureString ( " Could not clone the plugin repository. shell returned: \n {} " , ret ) ) ;
2023-12-07 11:41:09 +01:00
return false ;
}
2024-03-06 13:01:04 +01:00
if ( ! rev . empty ( ) ) {
2024-04-28 21:27:44 +02:00
std : : string ret = execAndGet ( " git -C " + m_szWorkingPluginDirectory + " reset --hard --recurse-submodules " + rev ) ;
2024-03-06 13:01:04 +01:00
if ( ret . compare ( 0 , 6 , " fatal: " ) = = 0 ) {
2024-10-13 14:23:33 +02:00
std : : println ( stderr , " \n {} " , failureString ( " Could not check out revision {}. shell returned: \n {} " , rev , ret ) ) ;
2024-03-06 13:01:04 +01:00
return false ;
}
2024-07-21 16:42:43 +02:00
ret = execAndGet ( " git -C " + m_szWorkingPluginDirectory + " submodule update --init " ) ;
if ( m_bVerbose )
2024-10-13 14:23:33 +02:00
std : : println ( " {} " , verboseString ( " git submodule update --init returned: {} " , ret ) ) ;
2024-03-06 13:01:04 +01:00
}
2023-12-07 11:41:09 +01:00
progress . m_iSteps = 1 ;
2024-10-13 14:23:33 +02:00
progress . printMessageAbove ( successString ( " cloned " ) ) ;
2023-12-07 11:41:09 +01:00
progress . m_szCurrentMessage = " Reading the manifest " ;
progress . print ( ) ;
std : : unique_ptr < CManifest > pManifest ;
2024-04-28 21:27:44 +02:00
if ( std : : filesystem : : exists ( m_szWorkingPluginDirectory + " /hyprpm.toml " ) ) {
2024-10-13 14:23:33 +02:00
progress . printMessageAbove ( successString ( " found hyprpm manifest " ) ) ;
2024-04-28 21:27:44 +02:00
pManifest = std : : make_unique < CManifest > ( MANIFEST_HYPRPM , m_szWorkingPluginDirectory + " /hyprpm.toml " ) ;
} else if ( std : : filesystem : : exists ( m_szWorkingPluginDirectory + " /hyprload.toml " ) ) {
2024-10-13 14:23:33 +02:00
progress . printMessageAbove ( successString ( " found hyprload manifest " ) ) ;
2024-04-28 21:27:44 +02:00
pManifest = std : : make_unique < CManifest > ( MANIFEST_HYPRLOAD , m_szWorkingPluginDirectory + " /hyprload.toml " ) ;
2023-12-07 11:41:09 +01:00
}
if ( ! pManifest ) {
2024-10-13 14:23:33 +02:00
std : : println ( stderr , " \n {} " , failureString ( " The provided plugin repository does not have a valid manifest " ) ) ;
2023-12-07 11:41:09 +01:00
return false ;
}
if ( ! pManifest - > m_bGood ) {
2024-10-13 14:23:33 +02:00
std : : println ( stderr , " \n {} " , failureString ( " The provided plugin repository has a corrupted manifest " ) ) ;
2023-12-07 11:41:09 +01:00
return false ;
}
progress . m_iSteps = 2 ;
2024-10-13 14:23:33 +02:00
progress . printMessageAbove ( successString ( " parsed manifest, found " + std : : to_string ( pManifest - > m_vPlugins . size ( ) ) + " plugins: " ) ) ;
2024-08-26 20:24:30 +02:00
for ( auto const & pl : pManifest - > m_vPlugins ) {
2024-10-13 14:23:33 +02:00
std : : string message = " → " + pl . name + " by " ;
2024-08-26 20:24:30 +02:00
for ( auto const & a : pl . authors ) {
2023-12-07 11:41:09 +01:00
message + = a + " , " ;
}
if ( pl . authors . size ( ) > 0 ) {
message . pop_back ( ) ;
message . pop_back ( ) ;
}
message + = " version " + pl . version ;
progress . printMessageAbove ( message ) ;
}
2024-02-01 20:38:43 +01:00
if ( ! pManifest - > m_sRepository . commitPins . empty ( ) ) {
// check commit pins
2024-10-13 14:23:33 +02:00
progress . printMessageAbove ( infoString ( " Manifest has {} pins, checking " , pManifest - > m_sRepository . commitPins . size ( ) ) ) ;
2024-02-01 20:38:43 +01:00
2024-08-26 20:24:30 +02:00
for ( auto const & [ hl , plugin ] : pManifest - > m_sRepository . commitPins ) {
2024-02-01 20:38:43 +01:00
if ( hl ! = HLVER . hash )
continue ;
2024-10-13 14:23:33 +02:00
progress . printMessageAbove ( successString ( " commit pin {} matched hl, resetting " , plugin ) ) ;
2024-02-01 20:38:43 +01:00
2024-04-28 21:27:44 +02:00
execAndGet ( " cd " + m_szWorkingPluginDirectory + " && git reset --hard --recurse-submodules " + plugin ) ;
2024-07-21 16:42:43 +02:00
ret = execAndGet ( " git -C " + m_szWorkingPluginDirectory + " submodule update --init " ) ;
if ( m_bVerbose )
2024-10-13 14:23:33 +02:00
std : : println ( " {} " , verboseString ( " git submodule update --init returned: {} " , ret ) ) ;
2024-07-21 16:42:43 +02:00
break ;
2024-02-01 20:38:43 +01:00
}
}
2023-12-07 11:41:09 +01:00
progress . m_szCurrentMessage = " Verifying headers " ;
progress . print ( ) ;
const auto HEADERSSTATUS = headersValid ( ) ;
if ( HEADERSSTATUS ! = HEADERS_OK ) {
2024-10-13 14:23:33 +02:00
std : : println ( " \n {} " , headerError ( HEADERSSTATUS ) ) ;
2023-12-07 11:41:09 +01:00
return false ;
}
progress . m_iSteps = 3 ;
2024-10-13 14:23:33 +02:00
progress . printMessageAbove ( successString ( " Hyprland headers OK " ) ) ;
2023-12-07 11:41:09 +01:00
progress . m_szCurrentMessage = " Building plugin(s) " ;
progress . print ( ) ;
for ( auto & p : pManifest - > m_vPlugins ) {
std : : string out ;
2024-04-05 04:00:34 +02:00
if ( p . since > HLVER . commits & & HLVER . commits > = 1 /* for --depth 1 clones, we can't check this. */ ) {
2024-10-13 14:23:33 +02:00
progress . printMessageAbove ( failureString ( " Not building {}: your Hyprland version is too old. \n " , p . name ) ) ;
2024-04-05 01:44:21 +02:00
p . failed = true ;
continue ;
}
2024-10-13 14:23:33 +02:00
progress . printMessageAbove ( infoString ( " Building {} " , p . name ) ) ;
2023-12-07 11:41:09 +01:00
2024-08-26 20:24:30 +02:00
for ( auto const & bs : p . buildSteps ) {
2024-10-13 14:23:33 +02:00
const std : : string & cmd = std : : format ( " cd {} && PKG_CONFIG_PATH= \" {}/share/pkgconfig \" {} " , m_szWorkingPluginDirectory , DataState : : getHeadersPath ( ) , bs ) ;
2024-02-01 20:38:43 +01:00
out + = " -> " + cmd + " \n " + execAndGet ( cmd ) + " \n " ;
2023-12-07 11:41:09 +01:00
}
2024-03-19 23:12:26 +01:00
if ( m_bVerbose )
2024-11-17 17:46:49 +01:00
std : : println ( " {} " , verboseString ( " shell returned: {} " , out ) ) ;
2024-03-19 23:12:26 +01:00
2024-04-28 21:27:44 +02:00
if ( ! std : : filesystem : : exists ( m_szWorkingPluginDirectory + " / " + p . output ) ) {
2024-10-13 14:23:33 +02:00
progress . printMessageAbove ( failureString ( " Plugin {} failed to build. \n "
" This likely means that the plugin is either outdated, not yet available for your version, or broken. \n "
" If you are on -git, update first \n "
" Try re-running with -v to see more verbose output. \n " ,
p . name ) ) ;
2023-12-07 11:41:09 +01:00
2024-01-07 18:15:51 +01:00
p . failed = true ;
continue ;
2023-12-07 11:41:09 +01:00
}
2024-10-13 14:23:33 +02:00
progress . printMessageAbove ( successString ( " built {} into {} " , p . name , p . output ) ) ;
2023-12-07 11:41:09 +01:00
}
2024-10-13 14:23:33 +02:00
progress . printMessageAbove ( successString ( " all plugins built " ) ) ;
2023-12-07 11:41:09 +01:00
progress . m_iSteps = 4 ;
progress . m_szCurrentMessage = " Installing repository " ;
progress . print ( ) ;
// add repo toml to DataState
SPluginRepository repo ;
2024-04-28 21:27:44 +02:00
std : : string repohash = execAndGet ( " cd " + m_szWorkingPluginDirectory + " && git rev-parse HEAD " ) ;
2023-12-07 11:41:09 +01:00
if ( repohash . length ( ) > 0 )
repohash . pop_back ( ) ;
repo . name = pManifest - > m_sRepository . name . empty ( ) ? url . substr ( url . find_last_of ( ' / ' ) + 1 ) : pManifest - > m_sRepository . name ;
repo . url = url ;
2024-03-06 13:01:04 +01:00
repo . rev = rev ;
2023-12-07 11:41:09 +01:00
repo . hash = repohash ;
2024-08-26 20:24:30 +02:00
for ( auto const & p : pManifest - > m_vPlugins ) {
2024-04-28 21:27:44 +02:00
repo . plugins . push_back ( SPlugin { p . name , m_szWorkingPluginDirectory + " / " + p . output , false , p . failed } ) ;
2023-12-07 11:41:09 +01:00
}
DataState : : addNewPluginRepo ( repo ) ;
2024-10-13 14:23:33 +02:00
progress . printMessageAbove ( successString ( " installed repository " ) ) ;
progress . printMessageAbove ( successString ( " you can now enable the plugin(s) with hyprpm enable " ) ) ;
2023-12-07 11:41:09 +01:00
progress . m_iSteps = 5 ;
progress . m_szCurrentMessage = " Done! " ;
progress . print ( ) ;
2024-10-13 14:23:33 +02:00
std : : print ( " \n " ) ;
2023-12-07 11:41:09 +01:00
// remove build files
2024-04-28 21:27:44 +02:00
std : : filesystem : : remove_all ( m_szWorkingPluginDirectory ) ;
2023-12-07 11:41:09 +01:00
return true ;
}
bool CPluginManager : : removePluginRepo ( const std : : string & urlOrName ) {
if ( ! DataState : : pluginRepoExists ( urlOrName ) ) {
2024-10-13 14:23:33 +02:00
std : : println ( stderr , " \n {} " , failureString ( " Could not remove the repository. Repository is not installed. " ) ) ;
2023-12-07 11:41:09 +01:00
return false ;
}
std : : cout < < Colors : : YELLOW < < " ! " < < Colors : : RESET < < Colors : : RED < < " removing a plugin repository: " < < Colors : : RESET < < urlOrName < < " \n "
< < " Are you sure? [Y/n] " ;
std : : fflush ( stdout ) ;
std : : string input ;
std : : getline ( std : : cin , input ) ;
if ( input . size ( ) > 0 & & input [ 0 ] ! = ' Y ' & & input [ 0 ] ! = ' y ' ) {
2024-10-13 14:23:33 +02:00
std : : println ( " Aborting. " ) ;
2023-12-07 11:41:09 +01:00
return false ;
}
DataState : : removePluginRepo ( urlOrName ) ;
return true ;
}
eHeadersErrors CPluginManager : : headersValid ( ) {
2024-12-11 17:49:09 +01:00
const auto HLVER = getHyprlandVersion ( false ) ;
2023-12-07 11:41:09 +01:00
2024-02-01 20:38:43 +01:00
if ( ! std : : filesystem : : exists ( DataState : : getHeadersPath ( ) + " /share/pkgconfig/hyprland.pc " ) )
return HEADERS_MISSING ;
2023-12-07 11:41:09 +01:00
// find headers commit
2024-10-13 14:23:33 +02:00
const std : : string & cmd = std : : format ( " PKG_CONFIG_PATH= \" {}/share/pkgconfig \" pkgconf --cflags --keep-system-cflags hyprland " , DataState : : getHeadersPath ( ) ) ;
auto headers = execAndGet ( cmd . c_str ( ) ) ;
2023-12-07 11:41:09 +01:00
if ( ! headers . contains ( " -I/ " ) )
return HEADERS_MISSING ;
headers . pop_back ( ) ; // pop newline
2024-10-13 14:23:33 +02:00
std : : string verHeader ;
2023-12-07 11:41:09 +01:00
while ( ! headers . empty ( ) ) {
const auto PATH = headers . substr ( 0 , headers . find ( " -I/ " , 3 ) ) ;
if ( headers . find ( " -I/ " , 3 ) ! = std : : string : : npos )
headers = headers . substr ( headers . find ( " -I/ " , 3 ) ) ;
else
headers = " " ;
2024-07-21 13:09:54 +02:00
if ( PATH . ends_with ( " protocols " ) )
2023-12-07 11:41:09 +01:00
continue ;
2024-06-11 17:17:45 +02:00
verHeader = trim ( PATH . substr ( 2 ) ) + " /hyprland/src/version.h " ;
2023-12-07 11:41:09 +01:00
break ;
}
if ( verHeader . empty ( ) )
return HEADERS_CORRUPTED ;
// read header
std : : ifstream ifs ( verHeader ) ;
if ( ! ifs . good ( ) )
return HEADERS_CORRUPTED ;
std : : string verHeaderContent ( ( std : : istreambuf_iterator < char > ( ifs ) ) , ( std : : istreambuf_iterator < char > ( ) ) ) ;
ifs . close ( ) ;
2024-03-30 04:09:22 +01:00
const auto HASHPOS = verHeaderContent . find ( " #define GIT_COMMIT_HASH " ) ;
if ( HASHPOS = = std : : string : : npos | | HASHPOS + 23 > = verHeaderContent . length ( ) )
return HEADERS_CORRUPTED ;
std : : string hash = verHeaderContent . substr ( HASHPOS + 23 ) ;
2023-12-07 11:41:09 +01:00
hash = hash . substr ( 0 , hash . find_first_of ( ' \n ' ) ) ;
hash = hash . substr ( hash . find_first_of ( ' " ' ) + 1 ) ;
hash = hash . substr ( 0 , hash . find_first_of ( ' " ' ) ) ;
2024-01-29 11:30:31 +01:00
if ( hash ! = HLVER . hash )
2023-12-07 11:41:09 +01:00
return HEADERS_MISMATCHED ;
return HEADERS_OK ;
}
2024-01-28 03:04:35 +01:00
bool CPluginManager : : updateHeaders ( bool force ) {
2023-12-07 11:41:09 +01:00
2024-02-01 20:38:43 +01:00
DataState : : ensureStateStoreExists ( ) ;
2024-12-11 17:49:09 +01:00
const auto HLVER = getHyprlandVersion ( false ) ;
2023-12-07 11:41:09 +01:00
2024-04-16 17:58:57 +02:00
if ( ! hasDeps ( ) ) {
2024-11-28 15:21:55 +01:00
std : : println ( " \n {} " , failureString ( " Could not update. Dependencies not satisfied. Hyprpm requires: cmake, meson, cpio, pkg-config " ) ) ;
2024-04-16 17:58:57 +02:00
return false ;
}
2024-11-23 15:18:13 +01:00
if ( ! std : : filesystem : : exists ( getTempRoot ( ) ) ) {
std : : filesystem : : create_directory ( getTempRoot ( ) ) ;
std : : filesystem : : permissions ( getTempRoot ( ) , std : : filesystem : : perms : : owner_all , std : : filesystem : : perm_options : : replace ) ;
2023-12-07 11:41:09 +01:00
}
2024-01-28 03:04:35 +01:00
if ( ! force & & headersValid ( ) = = HEADERS_OK ) {
2024-10-13 14:23:33 +02:00
std : : println ( " \n {} " , successString ( " Headers up to date. " ) ) ;
2023-12-07 11:41:09 +01:00
return true ;
}
CProgressBar progress ;
progress . m_iMaxSteps = 5 ;
progress . m_iSteps = 0 ;
progress . m_szCurrentMessage = " Cloning the hyprland repository " ;
progress . print ( ) ;
2024-04-28 21:27:44 +02:00
const std : : string USERNAME = getpwuid ( getuid ( ) ) - > pw_name ;
2024-11-23 15:18:13 +01:00
const auto WORKINGDIR = getTempRoot ( ) + " hyprland- " + USERNAME ;
2024-04-28 21:27:44 +02:00
if ( ! createSafeDirectory ( WORKINGDIR ) ) {
2024-10-13 14:23:33 +02:00
std : : println ( " \n {} " , failureString ( " Could not prepare working dir for hl " ) ) ;
2024-04-28 21:27:44 +02:00
return false ;
2023-12-07 11:41:09 +01:00
}
2024-10-13 14:23:33 +02:00
progress . printMessageAbove ( statusString ( " ! " , Colors : : YELLOW , " Cloning https://github.com/hyprwm/Hyprland, this might take a moment. " ) ) ;
2023-12-07 11:41:09 +01:00
2024-10-10 12:01:13 +02:00
const bool bShallow = ( HLVER . branch = = " main " ) & & ! m_bNoShallow ;
2024-05-12 01:53:50 +02:00
2024-04-16 16:41:11 +02:00
// let us give a bit of leg-room for shallowing
// due to timezones, etc.
2024-10-11 13:19:16 +02:00
const std : : string SHALLOW_DATE = trim ( HLVER . date ) . empty ( ) ? " " : execAndGet ( " LC_TIME= \" en_US.UTF-8 \" date --date=' " + HLVER . date + " - 1 weeks' '+%a %b %d %H:%M:%S %Y' " ) ;
2024-04-16 16:41:11 +02:00
2024-05-12 01:53:50 +02:00
if ( m_bVerbose & & bShallow )
2024-10-13 14:23:33 +02:00
progress . printMessageAbove ( verboseString ( " will shallow since: {} " , SHALLOW_DATE ) ) ;
2024-04-16 16:41:11 +02:00
2024-11-23 15:18:13 +01:00
std : : string ret = execAndGet ( std : : format ( " cd {} && git clone --recursive https://github.com/hyprwm/Hyprland hyprland-{}{} " , getTempRoot ( ) , USERNAME ,
( bShallow ? " --shallow-since=' " + SHALLOW_DATE + " ' " : " " ) ) ) ;
2023-12-07 11:41:09 +01:00
2024-04-28 21:27:44 +02:00
if ( ! std : : filesystem : : exists ( WORKINGDIR ) ) {
2024-10-13 14:23:33 +02:00
progress . printMessageAbove ( failureString ( " Clone failed. Retrying without shallow. " ) ) ;
2024-11-23 15:18:13 +01:00
ret = execAndGet ( std : : format ( " cd {} && git clone --recursive https://github.com/hyprwm/hyprland hyprland-{} " , getTempRoot ( ) , USERNAME ) ) ;
2024-04-15 02:57:04 +02:00
}
2024-04-28 21:27:44 +02:00
if ( ! std : : filesystem : : exists ( WORKINGDIR + " /.git " ) ) {
2024-10-13 14:23:33 +02:00
std : : println ( stderr , " \n {} " , failureString ( " Could not clone the Hyprland repository. shell returned: \n {} " , ret ) ) ;
2023-12-07 11:41:09 +01:00
return false ;
}
2024-10-13 14:23:33 +02:00
progress . printMessageAbove ( successString ( " Hyprland cloned " ) ) ;
2023-12-07 11:41:09 +01:00
progress . m_iSteps = 2 ;
progress . m_szCurrentMessage = " Checking out sources " ;
progress . print ( ) ;
2024-07-30 12:05:23 +02:00
if ( m_bVerbose )
2024-10-13 14:23:33 +02:00
progress . printMessageAbove ( verboseString ( " will run: cd {} && git checkout {} 2>&1 " , WORKINGDIR , HLVER . hash ) ) ;
2024-07-30 12:05:23 +02:00
2024-07-28 13:03:30 +02:00
ret = execAndGet ( " cd " + WORKINGDIR + " && git checkout " + HLVER . hash + " 2>&1 " ) ;
2023-12-07 11:41:09 +01:00
2024-07-30 12:05:23 +02:00
if ( ret . contains ( " fatal: unable to read tree " ) ) {
2024-10-13 14:23:33 +02:00
std : : println ( stderr , " \n {} " ,
failureString ( " Could not checkout the running Hyprland commit. If you are on -git, try updating. \n "
" You can also try re-running hyprpm update with --no-shallow. " ) ) ;
2024-07-30 12:05:23 +02:00
return false ;
}
2023-12-07 11:41:09 +01:00
if ( m_bVerbose )
2024-10-13 14:23:33 +02:00
progress . printMessageAbove ( verboseString ( " git returned (co): {} " , ret ) ) ;
2024-04-15 19:49:19 +02:00
2024-07-30 12:11:38 +02:00
ret = execAndGet ( " cd " + WORKINGDIR + " ; git rm subprojects/tracy ; git submodule update --init 2>&1 ; git reset --hard --recurse-submodules " + HLVER . hash ) ;
2024-04-15 19:49:19 +02:00
if ( m_bVerbose )
2024-10-13 14:23:33 +02:00
progress . printMessageAbove ( verboseString ( " git returned (rs): {} " , ret ) ) ;
2023-12-07 11:41:09 +01:00
2024-10-13 14:23:33 +02:00
progress . printMessageAbove ( successString ( " checked out to running ver " ) ) ;
2023-12-07 11:41:09 +01:00
progress . m_iSteps = 3 ;
progress . m_szCurrentMessage = " Building Hyprland " ;
progress . print ( ) ;
2024-10-13 14:23:33 +02:00
progress . printMessageAbove ( statusString ( " ! " , Colors : : YELLOW , " configuring Hyprland " ) ) ;
2023-12-07 11:41:09 +01:00
2024-02-01 20:38:43 +01:00
if ( m_bVerbose )
2024-10-13 14:23:33 +02:00
progress . printMessageAbove ( verboseString ( " setting PREFIX for cmake to {} " , DataState : : getHeadersPath ( ) ) ) ;
2024-02-01 20:38:43 +01:00
2024-04-28 21:27:44 +02:00
ret = execAndGet ( std : : format ( " cd {} && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:STRING= \" {} \" -S . -B ./build -G Ninja " , WORKINGDIR ,
DataState : : getHeadersPath ( ) ) ) ;
2024-01-28 04:00:05 +01:00
if ( m_bVerbose )
2024-10-13 14:23:33 +02:00
progress . printMessageAbove ( verboseString ( " cmake returned: {} " , ret ) ) ;
2024-01-28 04:00:05 +01:00
2024-06-13 11:33:20 +02:00
if ( ret . contains ( " CMake Error at " ) ) {
2024-06-09 09:42:14 +02:00
// missing deps, let the user know.
2024-06-13 11:33:20 +02:00
std : : string missing = ret . substr ( ret . find ( " CMake Error at " ) ) ;
missing = ret . substr ( ret . find_first_of ( ' \n ' ) + 1 ) ;
missing = missing . substr ( 0 , missing . find ( " -- Configuring incomplete " ) ) ;
2024-06-09 09:42:14 +02:00
missing = missing . substr ( 0 , missing . find_last_of ( ' \n ' ) ) ;
2024-10-13 14:23:33 +02:00
std : : println ( stderr , " \n {} " ,
failureString ( " Could not configure the hyprland source, cmake complained: \n {} \n \n "
" This likely means that you are missing the above dependencies or they are out of date. " ,
missing ) ) ;
2024-06-09 09:42:14 +02:00
return false ;
}
2024-10-13 14:23:33 +02:00
progress . printMessageAbove ( successString ( " configured Hyprland " ) ) ;
2023-12-07 11:41:09 +01:00
progress . m_iSteps = 4 ;
progress . m_szCurrentMessage = " Installing sources " ;
progress . print ( ) ;
2024-10-13 14:23:33 +02:00
const std : : string & cmd =
2024-04-28 22:32:22 +02:00
std : : format ( " sed -i -e \" s#PREFIX = /usr/local#PREFIX = {}# \" {}/Makefile && cd {} && make installheaders " , DataState : : getHeadersPath ( ) , WORKINGDIR , WORKINGDIR ) ;
2024-02-01 20:38:43 +01:00
if ( m_bVerbose )
2024-10-13 14:23:33 +02:00
progress . printMessageAbove ( verboseString ( " installation will run: {} " , cmd ) ) ;
2023-12-07 11:41:09 +01:00
2024-02-01 20:38:43 +01:00
ret = execAndGet ( cmd ) ;
2023-12-07 11:41:09 +01:00
if ( m_bVerbose )
2024-10-13 14:23:33 +02:00
std : : println ( " {} " , verboseString ( " installer returned: {} " , ret ) ) ;
2023-12-07 11:41:09 +01:00
// remove build files
2024-04-28 21:27:44 +02:00
std : : filesystem : : remove_all ( WORKINGDIR ) ;
2023-12-07 11:41:09 +01:00
auto HEADERSVALID = headersValid ( ) ;
if ( HEADERSVALID = = HEADERS_OK ) {
2024-10-13 14:23:33 +02:00
progress . printMessageAbove ( successString ( " installed headers " ) ) ;
2023-12-07 11:41:09 +01:00
progress . m_iSteps = 5 ;
progress . m_szCurrentMessage = " Done! " ;
progress . print ( ) ;
2024-10-13 14:23:33 +02:00
std : : print ( " \n " ) ;
2023-12-07 11:41:09 +01:00
} else {
2024-10-13 14:23:33 +02:00
progress . printMessageAbove ( failureString ( " failed to install headers with error code {} ({}) " , ( int ) HEADERSVALID , headerErrorShort ( HEADERSVALID ) ) ) ;
2023-12-07 11:41:09 +01:00
progress . m_iSteps = 5 ;
progress . m_szCurrentMessage = " Failed " ;
progress . print ( ) ;
2024-10-13 14:23:33 +02:00
std : : print ( stderr , " \n \n {} " , headerError ( HEADERSVALID ) ) ;
2023-12-13 03:32:57 +01:00
2023-12-07 11:41:09 +01:00
return false ;
}
return true ;
}
bool CPluginManager : : updatePlugins ( bool forceUpdateAll ) {
if ( headersValid ( ) ! = HEADERS_OK ) {
2024-10-13 14:23:33 +02:00
std : : println ( " {} " , failureString ( " headers are not up-to-date, please run hyprpm update. " ) ) ;
2023-12-07 11:41:09 +01:00
return false ;
}
const auto REPOS = DataState : : getAllRepositories ( ) ;
if ( REPOS . size ( ) < 1 ) {
2024-10-13 14:23:33 +02:00
std : : println ( " {} " , failureString ( " No repos to update. " ) ) ;
2023-12-07 11:41:09 +01:00
return true ;
}
2024-12-11 17:49:09 +01:00
const auto HLVER = getHyprlandVersion ( false ) ;
2023-12-07 11:41:09 +01:00
CProgressBar progress ;
2024-01-30 00:36:55 +01:00
progress . m_iMaxSteps = REPOS . size ( ) * 2 + 2 ;
2023-12-07 11:41:09 +01:00
progress . m_iSteps = 0 ;
progress . m_szCurrentMessage = " Updating repositories " ;
progress . print ( ) ;
2024-04-28 21:27:44 +02:00
const std : : string USERNAME = getpwuid ( getuid ( ) ) - > pw_name ;
2024-11-23 15:18:13 +01:00
m_szWorkingPluginDirectory = getTempRoot ( ) + USERNAME ;
2024-04-28 21:27:44 +02:00
2024-08-26 20:24:30 +02:00
for ( auto const & repo : REPOS ) {
2023-12-07 11:41:09 +01:00
bool update = forceUpdateAll ;
progress . m_iSteps + + ;
progress . m_szCurrentMessage = " Updating " + repo . name ;
progress . print ( ) ;
2024-10-13 14:23:33 +02:00
progress . printMessageAbove ( infoString ( " checking for updates for {} " , repo . name ) ) ;
2023-12-07 11:41:09 +01:00
2024-04-28 21:27:44 +02:00
createSafeDirectory ( m_szWorkingPluginDirectory ) ;
2023-12-07 11:41:09 +01:00
2024-10-13 14:23:33 +02:00
progress . printMessageAbove ( infoString ( " Cloning {} " , repo . url ) ) ;
2023-12-07 11:41:09 +01:00
2024-11-23 15:18:13 +01:00
std : : string ret = execAndGet ( std : : format ( " cd {} && git clone --recursive {} {} " , getTempRoot ( ) , repo . url , USERNAME ) ) ;
2023-12-07 11:41:09 +01:00
2024-04-28 21:27:44 +02:00
if ( ! std : : filesystem : : exists ( m_szWorkingPluginDirectory + " /.git " ) ) {
2024-10-13 14:23:33 +02:00
std : : println ( " {} " , failureString ( " could not clone repo: shell returned: {} " , ret ) ) ;
2023-12-07 11:41:09 +01:00
return false ;
}
2024-03-06 13:01:04 +01:00
if ( ! repo . rev . empty ( ) ) {
2024-10-13 14:23:33 +02:00
progress . printMessageAbove ( infoString ( " Plugin has revision set, resetting: {} " , repo . rev ) ) ;
2024-03-06 13:01:04 +01:00
2024-04-28 21:27:44 +02:00
std : : string ret = execAndGet ( " git -C " + m_szWorkingPluginDirectory + " reset --hard --recurse-submodules " + repo . rev ) ;
2024-03-06 13:01:04 +01:00
if ( ret . compare ( 0 , 6 , " fatal: " ) = = 0 ) {
2024-10-13 14:23:33 +02:00
std : : println ( stderr , " \n {} " , failureString ( " could not check out revision {}: shell returned: \n {} " , repo . rev , ret ) ) ;
2024-03-06 13:01:04 +01:00
return false ;
}
}
2023-12-07 11:41:09 +01:00
if ( ! update ) {
// check if git has updates
2024-04-28 21:27:44 +02:00
std : : string hash = execAndGet ( " cd " + m_szWorkingPluginDirectory + " && git rev-parse HEAD " ) ;
2023-12-07 11:41:09 +01:00
if ( ! hash . empty ( ) )
hash . pop_back ( ) ;
update = update | | hash ! = repo . hash ;
}
if ( ! update ) {
2024-04-28 21:27:44 +02:00
std : : filesystem : : remove_all ( m_szWorkingPluginDirectory ) ;
2024-10-13 14:23:33 +02:00
progress . printMessageAbove ( successString ( " repository {} is up-to-date. " , repo . name ) ) ;
2023-12-07 11:41:09 +01:00
progress . m_iSteps + + ;
progress . print ( ) ;
continue ;
}
// we need to update
2024-10-13 14:23:33 +02:00
progress . printMessageAbove ( successString ( " repository {} has updates. " , repo . name ) ) ;
progress . printMessageAbove ( infoString ( " Building {} " , repo . name ) ) ;
2023-12-07 11:41:09 +01:00
progress . m_iSteps + + ;
progress . print ( ) ;
std : : unique_ptr < CManifest > pManifest ;
2024-04-28 21:27:44 +02:00
if ( std : : filesystem : : exists ( m_szWorkingPluginDirectory + " /hyprpm.toml " ) ) {
2024-10-13 14:23:33 +02:00
progress . printMessageAbove ( successString ( " found hyprpm manifest " ) ) ;
2024-04-28 21:27:44 +02:00
pManifest = std : : make_unique < CManifest > ( MANIFEST_HYPRPM , m_szWorkingPluginDirectory + " /hyprpm.toml " ) ;
} else if ( std : : filesystem : : exists ( m_szWorkingPluginDirectory + " /hyprload.toml " ) ) {
2024-10-13 14:23:33 +02:00
progress . printMessageAbove ( successString ( " found hyprload manifest " ) ) ;
2024-04-28 21:27:44 +02:00
pManifest = std : : make_unique < CManifest > ( MANIFEST_HYPRLOAD , m_szWorkingPluginDirectory + " /hyprload.toml " ) ;
2023-12-07 11:41:09 +01:00
}
if ( ! pManifest ) {
2024-10-13 14:23:33 +02:00
std : : println ( stderr , " \n {} " , failureString ( " The provided plugin repository does not have a valid manifest " ) ) ;
2023-12-07 11:41:09 +01:00
continue ;
}
if ( ! pManifest - > m_bGood ) {
2024-10-13 14:23:33 +02:00
std : : println ( stderr , " \n {} " , failureString ( " The provided plugin repository has a corrupted manifest " ) ) ;
2023-12-07 11:41:09 +01:00
continue ;
}
2024-03-06 13:01:04 +01:00
if ( repo . rev . empty ( ) & & ! pManifest - > m_sRepository . commitPins . empty ( ) ) {
// check commit pins unless a revision is specified
2023-12-07 11:41:09 +01:00
2024-10-13 14:23:33 +02:00
progress . printMessageAbove ( infoString ( " Manifest has {} pins, checking " , pManifest - > m_sRepository . commitPins . size ( ) ) ) ;
2023-12-07 11:41:09 +01:00
2024-08-26 20:24:30 +02:00
for ( auto const & [ hl , plugin ] : pManifest - > m_sRepository . commitPins ) {
2023-12-07 11:41:09 +01:00
if ( hl ! = HLVER . hash )
continue ;
2024-10-13 14:23:33 +02:00
progress . printMessageAbove ( successString ( " commit pin {} matched hl, resetting " , plugin ) ) ;
2023-12-07 11:41:09 +01:00
2024-04-28 21:27:44 +02:00
execAndGet ( " cd " + m_szWorkingPluginDirectory + " && git reset --hard --recurse-submodules " + plugin ) ;
2023-12-07 11:41:09 +01:00
}
}
for ( auto & p : pManifest - > m_vPlugins ) {
std : : string out ;
2024-04-15 02:57:04 +02:00
if ( p . since > HLVER . commits & & HLVER . commits > = 1000 /* for shallow clones, we can't check this. 1000 is an arbitrary number I chose. */ ) {
2024-10-13 14:23:33 +02:00
progress . printMessageAbove ( failureString ( " Not building {}: your Hyprland version is too old. \n " , p . name ) ) ;
2024-04-05 01:44:21 +02:00
p . failed = true ;
continue ;
}
2024-10-13 14:23:33 +02:00
progress . printMessageAbove ( infoString ( " Building {} " , p . name ) ) ;
2023-12-07 11:41:09 +01:00
2024-08-26 20:24:30 +02:00
for ( auto const & bs : p . buildSteps ) {
2024-10-13 14:23:33 +02:00
const std : : string & cmd = std : : format ( " cd {} && PKG_CONFIG_PATH= \" {}/share/pkgconfig \" {} " , m_szWorkingPluginDirectory , DataState : : getHeadersPath ( ) , bs ) ;
2024-02-01 20:38:43 +01:00
out + = " -> " + cmd + " \n " + execAndGet ( cmd ) + " \n " ;
2023-12-07 11:41:09 +01:00
}
2024-03-19 23:12:26 +01:00
if ( m_bVerbose )
2024-10-13 14:23:33 +02:00
std : : println ( " {} " , verboseString ( " shell returned: {} " , out ) ) ;
2024-03-19 23:12:26 +01:00
2024-04-28 21:27:44 +02:00
if ( ! std : : filesystem : : exists ( m_szWorkingPluginDirectory + " / " + p . output ) ) {
2024-10-13 14:23:33 +02:00
std : : println ( stderr ,
" \n {} \n "
" This likely means that the plugin is either outdated, not yet available for your version, or broken. \n "
" If you are on -git, update first. \n "
" Try re-running with -v to see more verbose output. " ,
2024-10-16 23:13:59 +02:00
failureString ( " Plugin {} failed to build. " , p . name ) ) ;
2024-04-05 01:44:21 +02:00
p . failed = true ;
continue ;
2023-12-07 11:41:09 +01:00
}
2024-10-13 14:23:33 +02:00
progress . printMessageAbove ( successString ( " built {} into {} " , p . name , p . output ) ) ;
2023-12-07 11:41:09 +01:00
}
// add repo toml to DataState
SPluginRepository newrepo = repo ;
newrepo . plugins . clear ( ) ;
2024-04-28 21:27:44 +02:00
execAndGet ( " cd " + m_szWorkingPluginDirectory +
" && git pull --recurse-submodules && git reset --hard --recurse-submodules " ) ; // repo hash in the state.toml has to match head and not any pin
std : : string repohash = execAndGet ( " cd " + m_szWorkingPluginDirectory + " && git rev-parse HEAD " ) ;
2023-12-07 11:41:09 +01:00
if ( repohash . length ( ) > 0 )
repohash . pop_back ( ) ;
newrepo . hash = repohash ;
2024-08-26 20:24:30 +02:00
for ( auto const & p : pManifest - > m_vPlugins ) {
2023-12-07 11:41:09 +01:00
const auto OLDPLUGINIT = std : : find_if ( repo . plugins . begin ( ) , repo . plugins . end ( ) , [ & ] ( const auto & other ) { return other . name = = p . name ; } ) ;
2024-04-28 21:27:44 +02:00
newrepo . plugins . push_back ( SPlugin { p . name , m_szWorkingPluginDirectory + " / " + p . output , OLDPLUGINIT ! = repo . plugins . end ( ) ? OLDPLUGINIT - > enabled : false } ) ;
2023-12-07 11:41:09 +01:00
}
DataState : : removePluginRepo ( newrepo . name ) ;
DataState : : addNewPluginRepo ( newrepo ) ;
2024-04-28 21:27:44 +02:00
std : : filesystem : : remove_all ( m_szWorkingPluginDirectory ) ;
2023-12-07 11:41:09 +01:00
2024-10-13 14:23:33 +02:00
progress . printMessageAbove ( successString ( " updated {} " , repo . name ) ) ;
2023-12-07 11:41:09 +01:00
}
2024-01-30 00:36:55 +01:00
progress . m_iSteps + + ;
progress . m_szCurrentMessage = " Updating global state... " ;
progress . print ( ) ;
auto GLOBALSTATE = DataState : : getGlobalState ( ) ;
GLOBALSTATE . headersHashCompiled = HLVER . hash ;
DataState : : updateGlobalState ( GLOBALSTATE ) ;
2023-12-07 11:41:09 +01:00
progress . m_iSteps + + ;
progress . m_szCurrentMessage = " Done! " ;
progress . print ( ) ;
2024-10-13 14:23:33 +02:00
std : : print ( " \n " ) ;
2023-12-07 11:41:09 +01:00
return true ;
}
bool CPluginManager : : enablePlugin ( const std : : string & name ) {
bool ret = DataState : : setPluginEnabled ( name , true ) ;
if ( ret )
2024-10-13 14:23:33 +02:00
std : : println ( " {} " , successString ( " Enabled {} " , name ) ) ;
2023-12-07 11:41:09 +01:00
return ret ;
}
bool CPluginManager : : disablePlugin ( const std : : string & name ) {
bool ret = DataState : : setPluginEnabled ( name , false ) ;
if ( ret )
2024-10-13 14:23:33 +02:00
std : : println ( " {} " , successString ( " Disabled {} " , name ) ) ;
2023-12-07 11:41:09 +01:00
return ret ;
}
ePluginLoadStateReturn CPluginManager : : ensurePluginsLoadState ( ) {
if ( headersValid ( ) ! = HEADERS_OK ) {
2024-10-13 14:23:33 +02:00
std : : println ( stderr , " \n {} " , failureString ( " headers are not up-to-date, please run hyprpm update. " ) ) ;
2023-12-07 11:41:09 +01:00
return LOADSTATE_HEADERS_OUTDATED ;
}
const auto HOME = getenv ( " HOME " ) ;
const auto HIS = getenv ( " HYPRLAND_INSTANCE_SIGNATURE " ) ;
if ( ! HOME | | ! HIS ) {
2024-10-13 14:23:33 +02:00
std : : println ( stderr , " PluginManager: no $HOME or HIS " ) ;
2023-12-07 11:41:09 +01:00
return LOADSTATE_FAIL ;
}
const auto HYPRPMPATH = DataState : : getDataStatePath ( ) + " / " ;
auto pluginLines = execAndGet ( " hyprctl plugins list | grep Plugin " ) ;
std : : vector < std : : string > loadedPlugins ;
2024-10-13 14:23:33 +02:00
std : : println ( " {} " , successString ( " Ensuring plugin load state " ) ) ;
2023-12-07 11:41:09 +01:00
// iterate line by line
while ( ! pluginLines . empty ( ) ) {
2024-10-13 14:23:33 +02:00
auto plLine = pluginLines . substr ( 0 , pluginLines . find ( ' \n ' ) ) ;
2023-12-07 11:41:09 +01:00
2024-10-13 14:23:33 +02:00
if ( pluginLines . find ( ' \n ' ) ! = std : : string : : npos )
pluginLines = pluginLines . substr ( pluginLines . find ( ' \n ' ) + 1 ) ;
2023-12-07 11:41:09 +01:00
else
pluginLines = " " ;
if ( plLine . back ( ) ! = ' : ' )
continue ;
plLine = plLine . substr ( 7 ) ;
plLine = plLine . substr ( 0 , plLine . find ( " by " ) ) ;
loadedPlugins . push_back ( plLine ) ;
}
// get state
const auto REPOS = DataState : : getAllRepositories ( ) ;
auto enabled = [ REPOS ] ( const std : : string & plugin ) - > bool {
2024-08-26 20:24:30 +02:00
for ( auto const & r : REPOS ) {
for ( auto const & p : r . plugins ) {
2023-12-07 11:41:09 +01:00
if ( p . name = = plugin & & p . enabled )
return true ;
}
}
return false ;
} ;
auto repoForName = [ REPOS ] ( const std : : string & name ) - > std : : string {
2024-08-26 20:24:30 +02:00
for ( auto const & r : REPOS ) {
for ( auto const & p : r . plugins ) {
2023-12-07 11:41:09 +01:00
if ( p . name = = name )
return r . name ;
}
}
return " " ;
} ;
2024-12-11 17:49:09 +01:00
// if any of the loadUnloadPlugin calls return false, this is true
// bcs that means the header version doesn't match the running version
// (and Hyprland needs to restart)
bool hyprlandVersionMismatch = false ;
2023-12-07 11:41:09 +01:00
// unload disabled plugins
2024-08-26 20:24:30 +02:00
for ( auto const & p : loadedPlugins ) {
2023-12-07 11:41:09 +01:00
if ( ! enabled ( p ) ) {
// unload
2024-12-11 17:49:09 +01:00
if ( ! loadUnloadPlugin ( HYPRPMPATH + repoForName ( p ) + " / " + p + " .so " , false ) ) {
std : : println ( " {} " , infoString ( " {} will be unloaded after restarting Hyprland " , p ) ) ;
hyprlandVersionMismatch = true ;
} else
std : : println ( " {} " , successString ( " Unloaded {} " , p ) ) ;
2023-12-07 11:41:09 +01:00
}
}
// load enabled plugins
2024-08-26 20:24:30 +02:00
for ( auto const & r : REPOS ) {
for ( auto const & p : r . plugins ) {
2023-12-07 11:41:09 +01:00
if ( ! p . enabled )
continue ;
if ( std : : find_if ( loadedPlugins . begin ( ) , loadedPlugins . end ( ) , [ & ] ( const auto & other ) { return other = = p . name ; } ) ! = loadedPlugins . end ( ) )
continue ;
2024-12-11 17:49:09 +01:00
if ( ! loadUnloadPlugin ( HYPRPMPATH + repoForName ( p . name ) + " / " + p . filename , true ) ) {
std : : println ( " {} " , infoString ( " {} will be loaded after restarting Hyprland " , p . name ) ) ;
hyprlandVersionMismatch = true ;
} else
std : : println ( " {} " , successString ( " Loaded {} " , p . name ) ) ;
2023-12-07 11:41:09 +01:00
}
}
2024-10-13 14:23:33 +02:00
std : : println ( " {} " , successString ( " Plugin load state ensured " ) ) ;
2023-12-07 11:41:09 +01:00
2024-12-11 17:49:09 +01:00
return hyprlandVersionMismatch ? LOADSTATE_HYPRLAND_UPDATED : LOADSTATE_OK ;
2023-12-07 11:41:09 +01:00
}
bool CPluginManager : : loadUnloadPlugin ( const std : : string & path , bool load ) {
2024-12-11 17:49:09 +01:00
auto state = DataState : : getGlobalState ( ) ;
auto HLVER = getHyprlandVersion ( true ) ;
if ( state . headersHashCompiled ! = HLVER . hash ) {
std : : println ( " {} " , infoString ( " Running Hyprland version differs from plugin state, please restart Hyprland. " ) ) ;
return false ;
}
2023-12-07 11:41:09 +01:00
if ( load )
execAndGet ( " hyprctl plugin load " + path ) ;
else
execAndGet ( " hyprctl plugin unload " + path ) ;
return true ;
}
void CPluginManager : : listAllPlugins ( ) {
const auto REPOS = DataState : : getAllRepositories ( ) ;
2024-08-26 20:24:30 +02:00
for ( auto const & r : REPOS ) {
2024-10-13 14:23:33 +02:00
std : : println ( " {} " , infoString ( " Repository {}: " , r . name ) ) ;
2023-12-07 11:41:09 +01:00
2024-08-26 20:24:30 +02:00
for ( auto const & p : r . plugins ) {
2024-10-13 14:23:33 +02:00
std : : println ( " │ Plugin {} " , p . name ) ;
2024-01-07 18:15:51 +01:00
if ( ! p . failed )
2024-10-13 14:23:33 +02:00
std : : println ( " └─ enabled: {} " , ( p . enabled ? std : : string { Colors : : GREEN } + " true " : std : : string { Colors : : RED } + " false " ) ) ;
2024-01-07 18:15:51 +01:00
else
2024-10-13 14:23:33 +02:00
std : : println ( " └─ enabled: {}Plugin failed to build " , Colors : : RED ) ;
std : : println ( " {} " , Colors : : RESET ) ;
2023-12-07 11:41:09 +01:00
}
}
}
void CPluginManager : : notify ( const eNotifyIcons icon , uint32_t color , int durationMs , const std : : string & message ) {
execAndGet ( " hyprctl notify " + std : : to_string ( ( int ) icon ) + " " + std : : to_string ( durationMs ) + " " + std : : to_string ( color ) + " " + message ) ;
2023-12-13 03:32:57 +01:00
}
std : : string CPluginManager : : headerError ( const eHeadersErrors err ) {
switch ( err ) {
2024-10-13 14:23:33 +02:00
case HEADERS_CORRUPTED : return failureString ( " Headers corrupted. Please run hyprpm update to fix those. \n " ) ;
case HEADERS_MISMATCHED : return failureString ( " Headers version mismatch. Please run hyprpm update to fix those. \n " ) ;
case HEADERS_NOT_HYPRLAND : return failureString ( " It doesn't seem you are running on hyprland. \n " ) ;
case HEADERS_MISSING : return failureString ( " Headers missing. Please run hyprpm update to fix those. \n " ) ;
2023-12-13 03:32:57 +01:00
case HEADERS_DUPLICATED : {
2024-10-13 14:23:33 +02:00
return failureString ( " Headers duplicated!!! This is a very bad sign. \n "
" This could be due to e.g. installing hyprland manually while a system package of hyprland is also installed. \n "
" If the above doesn't apply, check your /usr/include and /usr/local/include directories \n and remove all the hyprland headers. \n " ) ;
2023-12-13 03:32:57 +01:00
}
default : break ;
}
2024-10-13 14:23:33 +02:00
return failureString ( " Unknown header error. Please run hyprpm update to fix those. \n " ) ;
2023-12-30 15:19:53 +01:00
}
2024-04-16 17:58:57 +02:00
2024-07-30 11:54:28 +02:00
std : : string CPluginManager : : headerErrorShort ( const eHeadersErrors err ) {
switch ( err ) {
case HEADERS_CORRUPTED : return " Headers corrupted " ;
case HEADERS_MISMATCHED : return " Headers version mismatched " ;
case HEADERS_NOT_HYPRLAND : return " Not running on Hyprland " ;
case HEADERS_MISSING : return " Headers missing " ;
case HEADERS_DUPLICATED : return " Headers duplicated " ;
default : break ;
}
return " ? " ;
}
2024-04-16 17:58:57 +02:00
bool CPluginManager : : hasDeps ( ) {
2024-08-25 13:13:48 +02:00
std : : vector < std : : string > deps = { " meson " , " cpio " , " cmake " , " pkg-config " } ;
2024-08-26 20:24:30 +02:00
for ( auto const & d : deps ) {
2024-08-25 13:13:48 +02:00
if ( ! execAndGet ( " command -v " + d ) . contains ( " / " ) )
2024-04-16 17:58:57 +02:00
return false ;
}
return true ;
}