summaryrefslogtreecommitdiff
path: root/start
diff options
context:
space:
mode:
authorVaxry <43317083+vaxerski@users.noreply.github.com>2026-01-08 10:57:56 +0100
committerGitHub <noreply@github.com>2026-01-08 10:57:56 +0100
commit836856604407da41e1c38324abd812b7884637b8 (patch)
tree87796eaead9481e1ce1a034ab8d90e4f4eba343d /start
parent918e2bb9be0e1d233f9394f1d569137788c43c01 (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.cpp8
-rw-r--r--start/src/core/State.hpp1
-rw-r--r--start/src/helpers/Nix.cpp110
-rw-r--r--start/src/helpers/Nix.hpp9
-rw-r--r--start/src/main.cpp21
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>();