Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ set(GLES_VERSION "GLES3")
find_package(OpenGL REQUIRED COMPONENTS ${GLES_VERSION})

pkg_check_modules(aquamarine_dep REQUIRED IMPORTED_TARGET aquamarine>=0.9.3)
pkg_check_modules(hyprlang_dep REQUIRED IMPORTED_TARGET hyprlang>=0.3.2)
pkg_check_modules(hyprlang_dep REQUIRED IMPORTED_TARGET hyprlang>=0.6.7)
pkg_check_modules(hyprcursor_dep REQUIRED IMPORTED_TARGET hyprcursor>=0.1.7)
pkg_check_modules(hyprutils_dep REQUIRED IMPORTED_TARGET hyprutils>=0.10.2)
pkg_check_modules(hyprgraphics_dep REQUIRED IMPORTED_TARGET hyprgraphics>=0.1.6)
Expand Down Expand Up @@ -500,6 +500,7 @@ protocolwayland()

# tools
add_subdirectory(hyprctl)
add_subdirectory(start)

if(NO_HYPRPM)
message(STATUS "hyprpm is disabled")
Expand Down
12 changes: 11 additions & 1 deletion example/hyprland.conf
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ monitor=,preferred,auto,auto
# Set programs that you use
$terminal = kitty
$fileManager = dolphin
$menu = wofi --show drun
$menu = hyprlauncher


#################
Expand Down Expand Up @@ -329,3 +329,13 @@ windowrule {

no_focus = true
}

# Hyprland-run windowrule
windowrule {
name = move-hyprland-run

match:class = hyprland-run

move = 20 monitor_h-120
float = yes
}
2 changes: 1 addition & 1 deletion example/hyprland.desktop
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[Desktop Entry]
Name=Hyprland
Comment=An intelligent dynamic tiling Wayland compositor
Exec=Hyprland
Exec=start-hyprland
Type=Application
DesktopNames=Hyprland
Keywords=tiling;wayland;compositor;
6 changes: 3 additions & 3 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion hyprpm/src/core/PluginManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -589,7 +589,7 @@ bool CPluginManager::updateHeaders(bool force) {
std::filesystem::remove_all(WORKINGDIR);

auto HEADERSVALID = headersValid();
if (HEADERSVALID == HEADERS_OK) {
if (HEADERSVALID == HEADERS_OK || HEADERSVALID == HEADERS_MISMATCHED) {
progress.printMessageAbove(successString("installed headers"));
progress.m_iSteps = 5;
progress.m_szCurrentMessage = "Done!";
Expand Down
62 changes: 60 additions & 2 deletions src/Compositor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,10 @@ static void aqLog(Aquamarine::eBackendLogLevel level, std::string msg) {
Debug::log(aqLevelToHl(level), "[AQ] {}", msg);
}

void CCompositor::setWatchdogFd(int fd) {
m_watchdogWriteFd = Hyprutils::OS::CFileDescriptor{fd};
}

void CCompositor::bumpNofile() {
if (!getrlimit(RLIMIT_NOFILE, &m_originalNofile))
Debug::log(LOG, "Old rlimit: soft -> {}, hard -> {}", m_originalNofile.rlim_cur, m_originalNofile.rlim_max);
Expand Down Expand Up @@ -542,6 +546,9 @@ void CCompositor::cleanup() {
if (!m_wlDisplay)
return;

if (m_watchdogWriteFd.isValid())
write(m_watchdogWriteFd.get(), "end", 3);

signal(SIGABRT, SIG_DFL);
signal(SIGSEGV, SIG_DFL);

Expand Down Expand Up @@ -791,6 +798,8 @@ void CCompositor::startCompositor() {
createLockFile();

EMIT_HOOK_EVENT("ready", nullptr);
if (m_watchdogWriteFd.isValid())
write(m_watchdogWriteFd.get(), "vax", 3);

// This blocks until we are done.
Debug::log(LOG, "Hyprland is ready, running the event loop!");
Expand Down Expand Up @@ -2455,6 +2464,7 @@ std::vector<PHLWORKSPACE> CCompositor::getWorkspacesCopy() {
void CCompositor::performUserChecks() {
static auto PNOCHECKXDG = CConfigValue<Hyprlang::INT>("misc:disable_xdg_env_checks");
static auto PNOCHECKGUIUTILS = CConfigValue<Hyprlang::INT>("misc:disable_hyprland_guiutils_check");
static auto PNOWATCHDOG = CConfigValue<Hyprlang::INT>("misc:disable_watchdog_warning");

if (!*PNOCHECKXDG) {
const auto CURRENT_DESKTOP_ENV = getenv("XDG_CURRENT_DESKTOP");
Expand All @@ -2466,15 +2476,63 @@ void CCompositor::performUserChecks() {
}

if (!*PNOCHECKGUIUTILS) {
if (!NFsUtils::executableExistsInPath("hyprland-dialog")) {
if (!NFsUtils::executableExistsInPath("hyprland-dialog"))
g_pHyprNotificationOverlay->addNotification(I18n::i18nEngine()->localize(I18n::TXT_KEY_NOTIF_NO_GUIUTILS), CHyprColor{}, 15000, ICON_WARNING);
}
}

if (g_pHyprOpenGL->m_failedAssetsNo > 0) {
g_pHyprNotificationOverlay->addNotification(I18n::i18nEngine()->localize(I18n::TXT_KEY_NOTIF_FAILED_ASSETS, {{"count", std::to_string(g_pHyprOpenGL->m_failedAssetsNo)}}),
CHyprColor{1.0, 0.1, 0.1, 1.0}, 15000, ICON_ERROR);
}

if (!m_watchdogWriteFd.isValid() && !*PNOWATCHDOG)
g_pHyprNotificationOverlay->addNotification(I18n::i18nEngine()->localize(I18n::TXT_KEY_NOTIF_NO_WATCHDOG), CHyprColor{1.0, 0.1, 0.1, 1.0}, 15000, ICON_WARNING);

if (m_safeMode)
openSafeModeBox();
}

void CCompositor::openSafeModeBox() {
const auto OPT_LOAD = I18n::i18nEngine()->localize(I18n::TXT_KEY_SAFE_MODE_BUTTON_LOAD_CONFIG);
const auto OPT_OPEN = I18n::i18nEngine()->localize(I18n::TXT_KEY_SAFE_MODE_BUTTON_OPEN_CRASH_REPORT_DIR);
const auto OPT_OK = I18n::i18nEngine()->localize(I18n::TXT_KEY_SAFE_MODE_BUTTON_UNDERSTOOD);

auto box = CAsyncDialogBox::create(I18n::i18nEngine()->localize(I18n::TXT_KEY_SAFE_MODE_TITLE), I18n::i18nEngine()->localize(I18n::TXT_KEY_SAFE_MODE_DESCRIPTION),
{
OPT_LOAD,
OPT_OPEN,
OPT_OK,
});

box->open()->then([OPT_LOAD, OPT_OK, OPT_OPEN, this](SP<CPromiseResult<std::string>> result) {
if (result->hasError())
return;

const auto RES = result->result();

if (RES.starts_with(OPT_LOAD)) {
m_safeMode = false;
g_pConfigManager->reload();
} else if (RES.starts_with(OPT_OPEN)) {
std::string reportPath;
const auto HOME = getenv("HOME");
const auto CACHE_HOME = getenv("XDG_CACHE_HOME");

if (CACHE_HOME && CACHE_HOME[0] != '\0') {
reportPath += CACHE_HOME;
reportPath += "/hyprland/";
} else if (HOME && HOME[0] != '\0') {
reportPath += HOME;
reportPath += "/.cache/hyprland/";
}
Hyprutils::OS::CProcess proc("xdg-open", {reportPath});

proc.runAsync();

// reopen
openSafeModeBox();
}
});
}

void CCompositor::moveWindowToWorkspaceSafe(PHLWINDOW pWindow, PHLWORKSPACE pWorkspace) {
Expand Down
34 changes: 19 additions & 15 deletions src/Compositor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class CCompositor {
} m_drmRenderNode;

bool m_initialized = false;
bool m_safeMode = false;
SP<Aquamarine::CBackend> m_aqBackend;

std::string m_hyprTempDataRoot = "";
Expand All @@ -65,6 +66,7 @@ class CCompositor {
void cleanup();
void bumpNofile();
void restoreNofile();
void setWatchdogFd(int fd);

bool m_readyToProcess = false;
bool m_sessionActive = true;
Expand Down Expand Up @@ -166,21 +168,23 @@ class CCompositor {
std::string m_explicitConfigPath;

private:
void initAllSignals();
void removeAllSignals();
void cleanEnvironment();
void setRandomSplash();
void initManagers(eManagersInitStage stage);
void prepareFallbackOutput();
void createLockFile();
void removeLockFile();
void setMallocThreshold();

uint64_t m_hyprlandPID = 0;
wl_event_source* m_critSigSource = nullptr;
rlimit m_originalNofile = {};

std::vector<PHLWORKSPACEREF> m_workspaces;
void initAllSignals();
void removeAllSignals();
void cleanEnvironment();
void setRandomSplash();
void initManagers(eManagersInitStage stage);
void prepareFallbackOutput();
void createLockFile();
void removeLockFile();
void setMallocThreshold();
void openSafeModeBox();

uint64_t m_hyprlandPID = 0;
wl_event_source* m_critSigSource = nullptr;
rlimit m_originalNofile = {};
Hyprutils::OS::CFileDescriptor m_watchdogWriteFd;

std::vector<PHLWORKSPACEREF> m_workspaces;
};

inline UP<CCompositor> g_pCompositor;
6 changes: 6 additions & 0 deletions src/config/ConfigDescriptions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1321,6 +1321,12 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
SConfigOptionDescription{
.value = "misc:disable_watchdog_warning",
.description = "whether to disable the warning about not using start-hyprland.",
.type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{false},
},
SConfigOptionDescription{
.value = "misc:lockdead_screen_delay",
.description = "the delay in ms after the lockdead screen appears if the lock screen did not appear after a lock event occurred.",
Expand Down
39 changes: 33 additions & 6 deletions src/config/ConfigManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,7 @@ CConfigManager::CConfigManager() {
registerConfigVar("misc:render_unfocused_fps", Hyprlang::INT{15});
registerConfigVar("misc:disable_xdg_env_checks", Hyprlang::INT{0});
registerConfigVar("misc:disable_hyprland_guiutils_check", Hyprlang::INT{0});
registerConfigVar("misc:disable_watchdog_warning", Hyprlang::INT{0});
registerConfigVar("misc:lockdead_screen_delay", Hyprlang::INT{1000});
registerConfigVar("misc:enable_anr_dialog", Hyprlang::INT{1});
registerConfigVar("misc:anr_missed_pings", Hyprlang::INT{5});
Expand Down Expand Up @@ -912,7 +913,7 @@ void CConfigManager::reloadRuleConfigs() {
}
}

std::optional<std::string> CConfigManager::generateConfig(std::string configPath) {
std::optional<std::string> CConfigManager::generateConfig(std::string configPath, bool safeMode) {
std::string parentPath = std::filesystem::path(configPath).parent_path();

if (!parentPath.empty()) {
Expand All @@ -929,7 +930,14 @@ std::optional<std::string> CConfigManager::generateConfig(std::string configPath
Debug::log(WARN, "No config file found; attempting to generate.");
std::ofstream ofs;
ofs.open(configPath, std::ios::trunc);
ofs << AUTOGENERATED_PREFIX << EXAMPLE_CONFIG;
if (!safeMode) {
ofs << AUTOGENERATED_PREFIX;
ofs << EXAMPLE_CONFIG;
} else {
std::string n = std::string{EXAMPLE_CONFIG};
replaceInString(n, "\n$menu = hyprlauncher\n", "\n$menu = hyprland-run\n");
ofs << n;
}
ofs.close();

if (ofs.fail())
Expand All @@ -939,7 +947,16 @@ std::optional<std::string> CConfigManager::generateConfig(std::string configPath
}

std::string CConfigManager::getMainConfigPath() {
static std::string CONFIG_PATH = [this]() -> std::string {
static bool lastSafeMode = g_pCompositor->m_safeMode;
static auto getCfgPath = [this]() -> std::string {
lastSafeMode = g_pCompositor->m_safeMode;
m_firstExecDispatched = false;

if (g_pCompositor->m_safeMode) {
const auto CONFIGPATH = g_pCompositor->m_instancePath + "/recoverycfg.conf";
return generateConfig(CONFIGPATH, false).value();
}

if (!g_pCompositor->m_explicitConfigPath.empty())
return g_pCompositor->m_explicitConfigPath;

Expand All @@ -954,7 +971,13 @@ std::string CConfigManager::getMainConfigPath() {
return generateConfig(CONFIGPATH).value();
} else
throw std::runtime_error("Neither HOME nor XDG_CONFIG_HOME are set in the environment. Could not find config in XDG_CONFIG_DIRS or /etc/xdg.");
}();
};
static std::string CONFIG_PATH = getCfgPath();

if (lastSafeMode != g_pCompositor->m_safeMode) {
CONFIG_PATH = getCfgPath();
m_config->changeRootPath(CONFIG_PATH.c_str());
}

return CONFIG_PATH;
}
Expand Down Expand Up @@ -1413,7 +1436,6 @@ void CConfigManager::init() {
reload();
});

const std::string CONFIGPATH = getMainConfigPath();
reload();

m_isFirstLaunch = false;
Expand Down Expand Up @@ -1635,7 +1657,12 @@ void CConfigManager::dispatchExecOnce() {
g_pInputManager->setTabletConfigs();

// check for user's possible errors with their setup and notify them if needed
g_pCompositor->performUserChecks();
// this is additionally guarded because exiting safe mode will re-run this.
static bool once = true;
if (once) {
g_pCompositor->performUserChecks();
once = false;
}
}

void CConfigManager::dispatchExecShutdown() {
Expand Down
2 changes: 1 addition & 1 deletion src/config/ConfigManager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ class CConfigManager {
// internal methods
void setDefaultAnimationVars();
std::optional<std::string> resetHLConfig();
std::optional<std::string> generateConfig(std::string configPath);
std::optional<std::string> generateConfig(std::string configPath, bool safeMode = false);
std::optional<std::string> verifyConfigExists();
void reloadRuleConfigs();

Expand Down
35 changes: 35 additions & 0 deletions src/i18n/Engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,18 @@ I18n::CI18nEngine::CI18nEngine() {
huEngine->registerEntry("en_US", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "Failed to load plugin {name}: {error}");
huEngine->registerEntry("en_US", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "CM shader reload failed, falling back to rgba/rgbx.");
huEngine->registerEntry("en_US", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "Monitor {name}: wide color gamut is enabled but the display is not in 10-bit mode.");
huEngine->registerEntry("en_US", TXT_KEY_NOTIF_NO_WATCHDOG,
"Hyprland was started without start-hyprland. This is highly not recommended unless you are in a debugging environment.");

huEngine->registerEntry("en_US", TXT_KEY_SAFE_MODE_TITLE, "Safe Mode");
huEngine->registerEntry("en_US", TXT_KEY_SAFE_MODE_DESCRIPTION,
"Hyprland has been launched in safe mode, which means your last session crashed.\nSafe mode prevents your config from being loaded. You can "
"troubleshoot in this environment, or load your config with the button below.\nDefault keybinds apply: SUPER+Q for kitty, SUPER+R for a basic runner, "
"SUPER+M to exit.\nRestarting "
"Hyprland will launch in normal mode again.");
huEngine->registerEntry("en_US", TXT_KEY_SAFE_MODE_BUTTON_LOAD_CONFIG, "Load config");
huEngine->registerEntry("en_US", TXT_KEY_SAFE_MODE_BUTTON_OPEN_CRASH_REPORT_DIR, "Open crash report directory");
huEngine->registerEntry("en_US", TXT_KEY_SAFE_MODE_BUTTON_UNDERSTOOD, "Ok, close this");

// as_IN (Assamese)
huEngine->registerEntry("as_IN", TXT_KEY_ANR_TITLE, "এপ্লিকেচনে উত্তৰ দিয়া নাই");
Expand Down Expand Up @@ -578,6 +590,17 @@ I18n::CI18nEngine::CI18nEngine() {
huEngine->registerEntry("ja_JP", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "プラグイン{name}のロード失敗: {error}");
huEngine->registerEntry("ja_JP", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "CMシェーダーのリロード失敗、rgba/rgbxを使いました。");
huEngine->registerEntry("ja_JP", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "画面{name}:広い色域は設定していますけど、画面は10ビットモードに設定されていません。");
huEngine->registerEntry("ja_JP", TXT_KEY_NOTIF_NO_WATCHDOG, "Hyprlandはstart-hyprlandなしで実行されました。これはデバグ環境以外でおすすめしません。");

huEngine->registerEntry("ja_JP", TXT_KEY_SAFE_MODE_TITLE, "安全モード");
huEngine->registerEntry(
"ja_JP", TXT_KEY_SAFE_MODE_DESCRIPTION,
"Hyprlandは安全モードに実行しました。これは、Hyprlandはクラッシュしましたから。\n安全モードはコンフィグをロードしなくて、問題を修正できる環境です。下のボタンでコンフィグを"
"ロードできます。\nデフォルトなキーバインドがあります。SUPER+Qはkitty、SUPER+Rは簡素なランチャー、SUPER+"
"MはHyprlandから退出。\nHyprlandを再び実行すれば、普通モードで実行します。");
huEngine->registerEntry("ja_JP", TXT_KEY_SAFE_MODE_BUTTON_LOAD_CONFIG, "コンフィグをロード");
huEngine->registerEntry("ja_JP", TXT_KEY_SAFE_MODE_BUTTON_OPEN_CRASH_REPORT_DIR, "クラッシュレポートフォルダーを開く");
huEngine->registerEntry("ja_JP", TXT_KEY_SAFE_MODE_BUTTON_UNDERSTOOD, "分かりました、このウィンドウをクローズ");

// lv_LV (Latvian)
huEngine->registerEntry("lv_LV", TXT_KEY_ANR_TITLE, "Lietotne nereaģē");
Expand Down Expand Up @@ -824,6 +847,18 @@ I18n::CI18nEngine::CI18nEngine() {
huEngine->registerEntry("pl_PL", TXT_KEY_NOTIF_FAILED_TO_LOAD_PLUGIN, "Nie udało się załadować plugin'a {name}: {error}");
huEngine->registerEntry("pl_PL", TXT_KEY_NOTIF_CM_RELOAD_FAILED, "Nie udało się przeładować shader'a CM, użyto rgba/rgbx.");
huEngine->registerEntry("pl_PL", TXT_KEY_NOTIF_WIDE_COLOR_NOT_10B, "Monitor {name}: skonfigurowano szeroką głębię barw, ale monitor nie jest w trybie 10-bit.");
huEngine->registerEntry("pl_PL", TXT_KEY_NOTIF_NO_WATCHDOG,
"Hyprland został uruchomiony bez start-hyprland. Nie jest to zalecane, chyba, że jest to środowisko do debugowania.");

huEngine->registerEntry("pl_PL", TXT_KEY_SAFE_MODE_TITLE, "Tryb Bezpieczny");
huEngine->registerEntry("pl_PL", TXT_KEY_SAFE_MODE_DESCRIPTION,
"Hyprland został uruchomiony w trybie bezpiecznym, co oznacza, że twoja ostatnia sesja uległa awarii.\nTryb bezpieczny zapobiega ładowaniu twojej "
"konfiguracji. Możesz próbować rozwiązać"
"problem w tym środowisku, lub załadować swoją konfigurację przyciskiem poniżej.\nDomyślne skróty klawiszowe są dostępne: SUPER+Q uruchamia kitty, "
"SUPER+R otwiera podstawowy launcher, SUPER+M zamyka Hyprland.\nUruchomienie ponowne Hyprland'a uruchomi go w trybie normalnym.");
huEngine->registerEntry("pl_PL", TXT_KEY_SAFE_MODE_BUTTON_LOAD_CONFIG, "Załaduj konfigurację");
huEngine->registerEntry("pl_PL", TXT_KEY_SAFE_MODE_BUTTON_OPEN_CRASH_REPORT_DIR, "Otwórz folder z raportami awarii");
huEngine->registerEntry("pl_PL", TXT_KEY_SAFE_MODE_BUTTON_UNDERSTOOD, "Ok, zamknij to okno");

// pt_PT (Portuguese Portugal)
huEngine->registerEntry("pt_PT", TXT_KEY_ANR_TITLE, "A aplicação não está a responder");
Expand Down
Loading
Loading