From 836856604407da41e1c38324abd812b7884637b8 Mon Sep 17 00:00:00 2001 From: Vaxry <43317083+vaxerski@users.noreply.github.com> Date: Thu, 8 Jan 2026 10:57:56 +0100 Subject: [PATCH] start: use nixGL if Hyprland is nix but not NixOS (#12845) --------- Co-authored-by: Mihai Fufezan --- start/src/core/Instance.cpp | 8 ++- start/src/core/State.hpp | 1 + start/src/helpers/Nix.cpp | 110 ++++++++++++++++++++++++++++++++++++ start/src/helpers/Nix.hpp | 9 +++ start/src/main.cpp | 21 ++++++- 5 files changed, 147 insertions(+), 2 deletions(-) create mode 100644 start/src/helpers/Nix.cpp create mode 100644 start/src/helpers/Nix.hpp diff --git a/start/src/core/Instance.cpp b/start/src/core/Instance.cpp index ec56cc756..2f5007bd9 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 #include @@ -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 6cf73a96e..d00a17573 100644 --- a/start/src/core/State.hpp +++ b/start/src/core/State.hpp @@ -8,6 +8,7 @@ struct SState { std::span rawArgvNoBinPath; std::optional customPath; + bool noNixGl = false; }; inline UP g_state = makeUnique(); \ 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 000000000..07cd2a4ad --- /dev/null +++ b/start/src/helpers/Nix.cpp @@ -0,0 +1,110 @@ +#include "Nix.hpp" + +#include "Logger.hpp" +#include "../core/State.hpp" + +#include +#include +#include +#include +#include +#include + +#include + +using namespace Hyprutils::String; +using namespace Hyprutils::OS; + +using namespace Hyprutils::File; + +static std::optional 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 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(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 000000000..edc01b199 --- /dev/null +++ b/start/src/helpers/Nix.hpp @@ -0,0 +1,9 @@ +#pragma once + +#include +#include + +namespace Nix { + std::expected nixEnvironmentOk(); + bool shouldUseNixGL(); +}; \ No newline at end of file diff --git a/start/src/main.cpp b/start/src/main.cpp index 74de393c3..e73fcfa51 100644 --- a/start/src/main.cpp +++ b/start/src/main.cpp @@ -3,6 +3,7 @@ #include #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();