diff options
| author | Vaxry <43317083+vaxerski@users.noreply.github.com> | 2026-01-08 10:57:56 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-01-08 10:57:56 +0100 |
| commit | 836856604407da41e1c38324abd812b7884637b8 (patch) | |
| tree | 87796eaead9481e1ce1a034ab8d90e4f4eba343d /start | |
| parent | 918e2bb9be0e1d233f9394f1d569137788c43c01 (diff) | |
start: use nixGL if Hyprland is nix but not NixOS (#12845)
---------
Co-authored-by: Mihai Fufezan <mihai@fufexan.net>
Diffstat (limited to 'start')
| -rw-r--r-- | start/src/core/Instance.cpp | 8 | ||||
| -rw-r--r-- | start/src/core/State.hpp | 1 | ||||
| -rw-r--r-- | start/src/helpers/Nix.cpp | 110 | ||||
| -rw-r--r-- | start/src/helpers/Nix.hpp | 9 | ||||
| -rw-r--r-- | start/src/main.cpp | 21 |
5 files changed, 147 insertions, 2 deletions
diff --git a/start/src/core/Instance.cpp b/start/src/core/Instance.cpp index ec56cc75..2f5007bd 100644 --- a/start/src/core/Instance.cpp +++ b/start/src/core/Instance.cpp @@ -1,6 +1,7 @@ #include "Instance.hpp" #include "State.hpp" #include "../helpers/Logger.hpp" +#include "../helpers/Nix.hpp" #include <cstdlib> #include <cstring> @@ -54,7 +55,12 @@ void CHyprlandInstance::runHyprlandThread(bool safeMode) { procctl(P_PID, getpid(), PROC_PDEATHSIG_CTL, &sig); #endif - execvp(g_state->customPath.value_or("Hyprland").c_str(), args.data()); + if (Nix::shouldUseNixGL()) { + argsStd.insert(argsStd.begin(), g_state->customPath.value_or("Hyprland")); + args.insert(args.begin(), strdup(argsStd.front().c_str())); + execvp("nixGL", args.data()); + } else + execvp(g_state->customPath.value_or("Hyprland").c_str(), args.data()); g_logger->log(Hyprutils::CLI::LOG_ERR, "fork(): execvp failed: {}", strerror(errno)); std::fflush(stdout); diff --git a/start/src/core/State.hpp b/start/src/core/State.hpp index 6cf73a96..d00a1757 100644 --- a/start/src/core/State.hpp +++ b/start/src/core/State.hpp @@ -8,6 +8,7 @@ struct SState { std::span<const char*> rawArgvNoBinPath; std::optional<std::string> customPath; + bool noNixGl = false; }; inline UP<SState> g_state = makeUnique<SState>();
\ No newline at end of file diff --git a/start/src/helpers/Nix.cpp b/start/src/helpers/Nix.cpp new file mode 100644 index 00000000..07cd2a4a --- /dev/null +++ b/start/src/helpers/Nix.cpp @@ -0,0 +1,110 @@ +#include "Nix.hpp" + +#include "Logger.hpp" +#include "../core/State.hpp" + +#include <filesystem> +#include <algorithm> +#include <hyprutils/string/VarList2.hpp> +#include <hyprutils/string/String.hpp> +#include <hyprutils/os/Process.hpp> +#include <hyprutils/os/File.hpp> + +#include <glaze/glaze.hpp> + +using namespace Hyprutils::String; +using namespace Hyprutils::OS; + +using namespace Hyprutils::File; + +static std::optional<std::string> getFromEtcOsRelease(const std::string_view& sv) { + static std::string content = ""; + static bool once = true; + + if (once) { + once = false; + + auto read = readFileAsString("/etc/os-release"); + content = read.value_or(""); + } + + static CVarList2 vars(std::move(content), 0, '\n', true); + + for (const auto& v : vars) { + if (v.starts_with(sv) && v.contains('=')) { + // found + auto value = trim(v.substr(v.find('=') + 1)); + + if (value.back() == value.front() && value.back() == '"') + value = value.substr(1, value.size() - 2); + + return std::string{value}; + } + } + + return std::nullopt; +} + +static bool executableExistsInPath(const std::string& exe) { + const char* PATHENV = std::getenv("PATH"); + if (!PATHENV) + return false; + + CVarList2 paths(PATHENV, 0, ':', true); + std::error_code ec; + + for (const auto& PATH : paths) { + std::filesystem::path candidate = std::filesystem::path(PATH) / exe; + if (!std::filesystem::exists(candidate, ec) || ec) + continue; + if (!std::filesystem::is_regular_file(candidate, ec) || ec) + continue; + auto perms = std::filesystem::status(candidate, ec).permissions(); + if (ec) + continue; + if ((perms & std::filesystem::perms::others_exec) != std::filesystem::perms::none) + return true; + } + + return false; +} + +std::expected<void, std::string> Nix::nixEnvironmentOk() { + if (!shouldUseNixGL()) + return {}; + + if (!executableExistsInPath("nixGL")) + return std::unexpected( + "Hyprland was installed using Nix, but you're not on NixOS. This requires nixGL to be installed as well.\nYou can install nixGL by running \"nix profile install " + "github:guibou/nixGL --impure\" in your terminal."); + + return {}; +} + +bool Nix::shouldUseNixGL() { + if (g_state->noNixGl) + return false; + + // check if installed hyprland is nix'd + CProcess proc("Hyprland", {"--version-json"}); + if (!proc.runSync()) { + g_logger->log(Hyprutils::CLI::LOG_ERR, "failed to obtain hyprland version string"); + return false; + } + + auto json = glz::read_json<glz::generic>(proc.stdOut()); + if (!json) { + g_logger->log(Hyprutils::CLI::LOG_ERR, "failed to obtain hyprland version string (bad json)"); + return false; + } + + const auto FLAGS = (*json)["flags"].get_array(); + const bool IS_NIX = std::ranges::any_of(FLAGS, [](const auto& e) { return e.get_string() == std::string_view{"nix"}; }); + + if (IS_NIX) { + const auto NAME = getFromEtcOsRelease("NAME"); + return !NAME || *NAME != "NixOS"; + } + + return false; +} diff --git a/start/src/helpers/Nix.hpp b/start/src/helpers/Nix.hpp new file mode 100644 index 00000000..edc01b19 --- /dev/null +++ b/start/src/helpers/Nix.hpp @@ -0,0 +1,9 @@ +#pragma once + +#include <expected> +#include <string> + +namespace Nix { + std::expected<void, std::string> nixEnvironmentOk(); + bool shouldUseNixGL(); +};
\ No newline at end of file diff --git a/start/src/main.cpp b/start/src/main.cpp index 74de393c..e73fcfa5 100644 --- a/start/src/main.cpp +++ b/start/src/main.cpp @@ -3,6 +3,7 @@ #include <print> #include "helpers/Logger.hpp" +#include "helpers/Nix.hpp" #include "core/State.hpp" #include "core/Instance.hpp" @@ -16,7 +17,12 @@ using namespace Hyprutils::CLI; } constexpr const char* HELP_INFO = R"#(start-hyprland - A binary to properly start Hyprland via a watchdog process. -Any arguments after -- are passed to Hyprland. For Hyprland help, run start-hyprland -- --help or Hyprland --help)#"; +Any arguments after -- are passed to Hyprland. For Hyprland help, run start-hyprland -- --help or Hyprland --help + +Additional arguments for start-hyprland: + --path [path] -> Override Hyprland path + --no-nixgl -> Force disable nixGL +)#"; // static void onSignal(int sig) { @@ -69,6 +75,10 @@ int main(int argc, const char** argv, const char** envp) { g_state->customPath = argv[++i]; continue; } + if (arg == "--no-nixgl") { + g_state->noNixGl = true; + continue; + } } if (startArgv != -1) @@ -77,6 +87,15 @@ int main(int argc, const char** argv, const char** envp) { if (!g_state->rawArgvNoBinPath.empty()) g_logger->log(Hyprutils::CLI::LOG_WARN, "Arguments after -- are passed to Hyprland"); + // check if our environment is OK + if (const auto RET = Nix::nixEnvironmentOk(); !RET) { + g_logger->log(Hyprutils::CLI::LOG_ERR, "Nix environment check failed:\n{}", RET.error()); + return 1; + } + + if (Nix::shouldUseNixGL()) + g_logger->log(Hyprutils::CLI::LOG_DEBUG, "Hyprland was compiled with Nix - will use nixGL"); + bool safeMode = false; while (true) { g_instance = makeUnique<CHyprlandInstance>(); |
