mirror of
https://github.com/hyprwm/Hyprland.git
synced 2026-08-24 10:04:19 -05:00
start: re-show display lock died screen on restart after crash (#15334)
* fix: display lock died screen on restart after crash * refactor to expose events in SessionLockManager and listen from Compositor * update Hyprland CLI help output * add clearSessionLock() and fix allow_session_lock_restore * implement --locked CLI flag * exec lockscreen directly without lua * separate --locked into --locked-cmd and --locked * fix clang-format problems
This commit is contained in:
+34
-3
@@ -130,6 +130,14 @@ bool CCompositor::setWatchdogFd(int fd) {
|
||||
return m_watchdogWriteFd.isValid() && !m_watchdogWriteFd.isClosed();
|
||||
}
|
||||
|
||||
bool CCompositor::writeWatchdogFd(std::string str) {
|
||||
if (!m_watchdogWriteFd.isValid())
|
||||
return false;
|
||||
str += '\n';
|
||||
auto w = write(m_watchdogWriteFd.get(), str.c_str(), str.size());
|
||||
return w >= 0;
|
||||
}
|
||||
|
||||
void CCompositor::bumpNofile() {
|
||||
if (!getrlimit(RLIMIT_NOFILE, &m_originalNofile))
|
||||
Log::logger->log(Log::DEBUG, "Old rlimit: soft -> {}, hard -> {}", m_originalNofile.rlim_cur, m_originalNofile.rlim_max);
|
||||
@@ -416,6 +424,30 @@ void CCompositor::initServer(std::string socketName, int socketFd) {
|
||||
|
||||
initManagers(STAGE_LATE);
|
||||
|
||||
m_listeners.lock = g_pSessionLockManager->m_events.lock.listen([this] {
|
||||
static int lock_count = 0;
|
||||
// lock_count used to avoid triggering condition on initial forceLock()
|
||||
if (m_startLocked && lock_count >= 1) {
|
||||
// Lock manager has taken over
|
||||
m_startLocked = false;
|
||||
m_startLockedCommand.clear();
|
||||
}
|
||||
lock_count++;
|
||||
writeWatchdogFd("lock");
|
||||
});
|
||||
|
||||
m_listeners.unlock = g_pSessionLockManager->m_events.unlock.listen([this] {
|
||||
m_startLocked = false;
|
||||
writeWatchdogFd("unlock");
|
||||
});
|
||||
|
||||
if (m_startLocked) {
|
||||
g_pSessionLockManager->forceLock();
|
||||
|
||||
if (!m_startLockedCommand.empty())
|
||||
Config::Supplementary::executor()->spawn(m_startLockedCommand);
|
||||
}
|
||||
|
||||
for (auto const& o : pendingOutputs) {
|
||||
State::monitorState()->add(o);
|
||||
}
|
||||
@@ -529,8 +561,7 @@ void CCompositor::cleanup() {
|
||||
if (!m_wlDisplay)
|
||||
return;
|
||||
|
||||
if (m_watchdogWriteFd.isValid()) [[maybe_unused]]
|
||||
auto w = write(m_watchdogWriteFd.get(), "end", 3);
|
||||
writeWatchdogFd("end");
|
||||
|
||||
signal(SIGABRT, SIG_DFL);
|
||||
signal(SIGSEGV, SIG_DFL);
|
||||
@@ -783,7 +814,7 @@ void CCompositor::startCompositor() {
|
||||
Event::bus()->m_events.ready.emit();
|
||||
|
||||
if (m_watchdogWriteFd.isValid()) {
|
||||
if (write(m_watchdogWriteFd.get(), "vax", 3) < 0)
|
||||
if (!writeWatchdogFd("vax"))
|
||||
Log::logger->log(Log::ERR, "startCompositor: failed to write to watchdogWriteFd {}: {}", m_watchdogWriteFd.get(), strerror(errno));
|
||||
}
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ class CCompositor {
|
||||
|
||||
bool m_initialized = false;
|
||||
bool m_safeMode = false;
|
||||
bool m_startLocked = false;
|
||||
SP<Aquamarine::CBackend> m_aqBackend;
|
||||
|
||||
std::string m_hyprTempDataRoot = "";
|
||||
@@ -55,6 +56,8 @@ class CCompositor {
|
||||
std::string m_instancePath = "";
|
||||
std::string m_currentSplash = "error";
|
||||
|
||||
std::string m_startLockedCommand = "";
|
||||
|
||||
void initServer(std::string socketName, int socketFd);
|
||||
void startCompositor();
|
||||
void stopCompositor();
|
||||
@@ -62,6 +65,7 @@ class CCompositor {
|
||||
void bumpNofile();
|
||||
void restoreNofile();
|
||||
bool setWatchdogFd(int fd);
|
||||
bool writeWatchdogFd(std::string);
|
||||
|
||||
bool m_sessionActive = true;
|
||||
bool m_dpmsStateOn = true;
|
||||
@@ -99,6 +103,11 @@ class CCompositor {
|
||||
wl_event_source* m_critSigSource = nullptr;
|
||||
rlimit m_originalNofile = {};
|
||||
Hyprutils::OS::CFileDescriptor m_watchdogWriteFd;
|
||||
|
||||
struct {
|
||||
CHyprSignalListener lock;
|
||||
CHyprSignalListener unlock;
|
||||
} m_listeners;
|
||||
};
|
||||
|
||||
inline UP<CCompositor> g_pCompositor;
|
||||
|
||||
+23
-2
@@ -37,7 +37,8 @@ static void help() {
|
||||
--i-am-really-stupid - Omits root user privileges check (why would you do that?)
|
||||
--verify-config - Do not run Hyprland, only print if the config has any errors
|
||||
--version -v - Print this binary's version
|
||||
--version-json - Print this binary's version as json)#");
|
||||
--version-json - Print this binary's version as json
|
||||
--locked-cmd [COMMAND] - Launches locker on startup via the provided command)#");
|
||||
}
|
||||
|
||||
static void reapZombieChildrenAutomatically() {
|
||||
@@ -70,8 +71,9 @@ int main(int argc, char** argv) {
|
||||
// parse some args
|
||||
std::string configPath;
|
||||
std::string socketName;
|
||||
std::string startLockedCommand;
|
||||
int socketFd = -1;
|
||||
bool ignoreSudo = false, verifyConfig = false, safeMode = false;
|
||||
bool ignoreSudo = false, verifyConfig = false, safeMode = false, startLocked = false;
|
||||
int watchdogFd = -1;
|
||||
|
||||
if (argc > 1) {
|
||||
@@ -176,6 +178,20 @@ int main(int argc, char** argv) {
|
||||
help();
|
||||
return 1;
|
||||
}
|
||||
} else if (value == "--locked-cmd") {
|
||||
if (std::next(it) == args.end()) {
|
||||
help();
|
||||
return 1;
|
||||
}
|
||||
|
||||
startLocked = true;
|
||||
startLockedCommand = *std::next(it);
|
||||
it++;
|
||||
|
||||
continue;
|
||||
} else if (value == "--locked") {
|
||||
startLocked = true;
|
||||
continue;
|
||||
} else {
|
||||
std::println(stderr, "[ ERROR ] Unknown option '{}' !", value);
|
||||
help();
|
||||
@@ -260,6 +276,11 @@ int main(int argc, char** argv) {
|
||||
if (safeMode)
|
||||
g_pCompositor->m_safeMode = true;
|
||||
|
||||
if (startLocked) {
|
||||
g_pCompositor->m_startLocked = true;
|
||||
g_pCompositor->m_startLockedCommand = startLockedCommand;
|
||||
}
|
||||
|
||||
if (!watchdogOk && !verifyConfig)
|
||||
Log::logger->log(Log::WARN, "WARNING: Hyprland is being launched without start-hyprland. This is highly advised against.");
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ CSessionLockManager::CSessionLockManager() {
|
||||
void CSessionLockManager::onNewSessionLock(SP<CSessionLock> pLock) {
|
||||
static auto PALLOWRELOCK = CConfigValue<Config::INTEGER>("misc:allow_session_lock_restore");
|
||||
|
||||
if (PROTO::sessionLock->isLocked() && !*PALLOWRELOCK) {
|
||||
if (PROTO::sessionLock->isLocked() && !*PALLOWRELOCK && g_pCompositor->m_startLockedCommand.empty()) {
|
||||
LOGM(Log::DEBUG, "Cannot re-lock, misc:allow_session_lock_restore is disabled");
|
||||
pLock->sendDenied();
|
||||
return;
|
||||
@@ -78,6 +78,8 @@ void CSessionLockManager::onNewSessionLock(SP<CSessionLock> pLock) {
|
||||
});
|
||||
|
||||
m_sessionLock->listeners.unlock = pLock->m_events.unlockAndDestroy.listen([this] {
|
||||
m_events.unlock.emit();
|
||||
|
||||
m_sessionLock.reset();
|
||||
g_pInputManager->refocus();
|
||||
|
||||
@@ -93,6 +95,8 @@ void CSessionLockManager::onNewSessionLock(SP<CSessionLock> pLock) {
|
||||
g_pHyprRenderer->damageMonitor(m);
|
||||
});
|
||||
|
||||
m_events.lock.emit();
|
||||
|
||||
Desktop::focusState()->rawSurfaceFocus(nullptr);
|
||||
g_pSeatManager->setGrab(nullptr);
|
||||
|
||||
@@ -216,9 +220,14 @@ bool CSessionLockManager::clientDenied() {
|
||||
return m_sessionLock && m_sessionLock->hasSentDenied;
|
||||
}
|
||||
|
||||
void CSessionLockManager::clearSessionLock() {
|
||||
m_events.unlock.emit();
|
||||
m_sessionLock = {};
|
||||
}
|
||||
|
||||
void CSessionLockManager::forceUnlock() {
|
||||
PROTO::sessionLock->forceUnlock();
|
||||
m_sessionLock = {};
|
||||
clearSessionLock();
|
||||
|
||||
Desktop::focusState()->rawSurfaceFocus(nullptr);
|
||||
for (auto const& m : State::monitorState()->monitors())
|
||||
@@ -227,6 +236,11 @@ void CSessionLockManager::forceUnlock() {
|
||||
g_pInputManager->refocus();
|
||||
}
|
||||
|
||||
void CSessionLockManager::forceLock() {
|
||||
PROTO::sessionLock->forceLock();
|
||||
m_events.lock.emit();
|
||||
}
|
||||
|
||||
bool CSessionLockManager::shallConsiderLockMissing() {
|
||||
if (!m_sessionLock)
|
||||
return true;
|
||||
|
||||
@@ -65,6 +65,7 @@ class CSessionLockManager {
|
||||
bool anySessionLockSurfacesPresent();
|
||||
|
||||
void forceUnlock();
|
||||
void forceLock();
|
||||
|
||||
void removeSessionLockSurface(SSessionLockSurface*);
|
||||
|
||||
@@ -72,6 +73,11 @@ class CSessionLockManager {
|
||||
|
||||
bool shallConsiderLockMissing();
|
||||
|
||||
struct {
|
||||
CSignalT<> lock;
|
||||
CSignalT<> unlock;
|
||||
} m_events;
|
||||
|
||||
private:
|
||||
UP<SSessionLock> m_sessionLock;
|
||||
|
||||
@@ -81,6 +87,7 @@ class CSessionLockManager {
|
||||
|
||||
void onNewSessionLock(SP<CSessionLock> pWlrLock);
|
||||
void removeSendLockedTimer();
|
||||
void clearSessionLock();
|
||||
};
|
||||
|
||||
inline UP<CSessionLockManager> g_pSessionLockManager;
|
||||
|
||||
@@ -248,3 +248,13 @@ void CSessionLockProtocol::forceUnlock() {
|
||||
|
||||
PROTO::lockNotify->onUnlocked();
|
||||
}
|
||||
|
||||
void CSessionLockProtocol::forceLock() {
|
||||
m_locked = true;
|
||||
|
||||
for (const auto& l : m_locks) {
|
||||
l->sendLocked();
|
||||
}
|
||||
|
||||
PROTO::lockNotify->onLocked();
|
||||
}
|
||||
|
||||
@@ -74,6 +74,7 @@ class CSessionLockProtocol : public IWaylandProtocol {
|
||||
|
||||
bool isLocked();
|
||||
void forceUnlock();
|
||||
void forceLock();
|
||||
|
||||
struct {
|
||||
CSignalT<SP<CSessionLock>> newLock;
|
||||
|
||||
@@ -1614,6 +1614,7 @@ void IHyprRenderer::ensureLockTexturesRendered(bool load) {
|
||||
// this will cause a small hitch. I don't think we can do much, other than wasting VRAM and having this loaded all the time.
|
||||
m_lockDeadTexture = loadAsset("lockdead.png");
|
||||
m_lockDead2Texture = loadAsset("lockdead2.png");
|
||||
m_lockDead3Texture = loadAsset("lockdead.png");
|
||||
|
||||
const auto VT = g_pCompositor->getVTNr();
|
||||
|
||||
@@ -1621,6 +1622,7 @@ void IHyprRenderer::ensureLockTexturesRendered(bool load) {
|
||||
} else {
|
||||
m_lockDeadTexture.reset();
|
||||
m_lockDead2Texture.reset();
|
||||
m_lockDead3Texture.reset();
|
||||
m_lockTtyTextTexture.reset();
|
||||
}
|
||||
}
|
||||
@@ -1676,13 +1678,19 @@ void IHyprRenderer::renderSessionLockPrimer(PHLMONITOR pMonitor) {
|
||||
}
|
||||
|
||||
void IHyprRenderer::renderSessionLockMissing(PHLMONITOR pMonitor) {
|
||||
if (g_pCompositor->m_startLocked && !g_pCompositor->m_startLockedCommand.empty())
|
||||
return;
|
||||
|
||||
const bool ANY_PRESENT = g_pSessionLockManager->anySessionLockSurfacesPresent();
|
||||
|
||||
// ANY_PRESENT: render image2, without instructions. Lock still "alive", unless texture dead
|
||||
// else: render image, with instructions. Lock is gone.
|
||||
CBox monbox = {{}, pMonitor->m_pixelSize};
|
||||
CTexPassElement::SRenderData data;
|
||||
data.tex = (ANY_PRESENT) ? m_lockDead2Texture : m_lockDeadTexture;
|
||||
if (g_pCompositor->m_startLocked && g_pCompositor->m_startLockedCommand.empty())
|
||||
data.tex = m_lockDead3Texture;
|
||||
else
|
||||
data.tex = (ANY_PRESENT) ? m_lockDead2Texture : m_lockDeadTexture;
|
||||
data.box = monbox;
|
||||
data.a = 1;
|
||||
|
||||
|
||||
@@ -250,6 +250,7 @@ namespace Render {
|
||||
|
||||
SP<ITexture> m_lockDeadTexture;
|
||||
SP<ITexture> m_lockDead2Texture;
|
||||
SP<ITexture> m_lockDead3Texture;
|
||||
SP<ITexture> m_lockTtyTextTexture;
|
||||
CRenderPass* m_currentPass = nullptr;
|
||||
bool m_monitorTransformEnabled = false; // do not modify directly
|
||||
|
||||
@@ -25,13 +25,16 @@ using namespace Hyprutils::OS;
|
||||
using namespace std::string_literals;
|
||||
|
||||
//
|
||||
void CHyprlandInstance::runHyprlandThread(bool safeMode) {
|
||||
void CHyprlandInstance::runHyprlandThread(bool safeMode, bool lockedCrash) {
|
||||
std::vector<std::string> argsStd;
|
||||
argsStd.emplace_back("--watchdog-fd");
|
||||
argsStd.emplace_back(std::format("{}", m_toHlPid.get()));
|
||||
if (safeMode)
|
||||
argsStd.emplace_back("--safe-mode");
|
||||
|
||||
if (lockedCrash)
|
||||
argsStd.emplace_back("--locked");
|
||||
|
||||
for (const auto& a : g_state->rawArgvNoBinPath) {
|
||||
argsStd.emplace_back(a);
|
||||
}
|
||||
@@ -134,10 +137,22 @@ void CHyprlandInstance::dispatchHyprlandEvent() {
|
||||
m_hyprlandExiting = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sv == "lock") {
|
||||
// session locked
|
||||
m_hyprlandLocked = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sv == "unlock") {
|
||||
// session unlocked
|
||||
m_hyprlandLocked = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CHyprlandInstance::run(bool safeMode) {
|
||||
bool CHyprlandInstance::run(bool safeMode, bool lockedCrash) {
|
||||
int pipefds[2];
|
||||
if (pipe(pipefds) != 0) {
|
||||
g_logger->log(Hyprutils::CLI::LOG_ERR, "pipe() failed, exiting");
|
||||
@@ -159,7 +174,9 @@ bool CHyprlandInstance::run(bool safeMode) {
|
||||
m_wakeupRead.setFlags(m_wakeupRead.getFlags() | FD_CLOEXEC);
|
||||
m_wakeupWrite.setFlags(m_wakeupWrite.getFlags() | FD_CLOEXEC);
|
||||
|
||||
runHyprlandThread(safeMode);
|
||||
m_hyprlandLocked = lockedCrash;
|
||||
|
||||
runHyprlandThread(safeMode, lockedCrash);
|
||||
|
||||
pollfd pollfds[2] = {
|
||||
{
|
||||
|
||||
@@ -14,11 +14,13 @@ class CHyprlandInstance {
|
||||
CHyprlandInstance(CHyprlandInstance&) = delete;
|
||||
CHyprlandInstance(CHyprlandInstance&&) = delete;
|
||||
|
||||
bool run(bool safeMode = false); // if returns false, restart.
|
||||
bool run(bool safeMode = false, bool lockedCrash = false); // if returns false, restart.
|
||||
void forceQuit();
|
||||
|
||||
bool m_hyprlandLocked = false;
|
||||
|
||||
private:
|
||||
void runHyprlandThread(bool safeMode);
|
||||
void runHyprlandThread(bool safeMode, bool lockedCrash);
|
||||
void clearFd(const Hyprutils::OS::CFileDescriptor& fd);
|
||||
void dispatchHyprlandEvent();
|
||||
|
||||
|
||||
+5
-2
@@ -101,10 +101,13 @@ int main(int argc, const char** argv, const char** envp) {
|
||||
if (Nix::shouldUseNixGL())
|
||||
g_logger->log(Hyprutils::CLI::LOG_DEBUG, "Hyprland was compiled with Nix - will use nixGL");
|
||||
|
||||
bool safeMode = false;
|
||||
bool safeMode = false;
|
||||
bool lockedCrash = false;
|
||||
|
||||
while (true) {
|
||||
g_instance = makeUnique<CHyprlandInstance>();
|
||||
const bool RET = g_instance->run(safeMode);
|
||||
const bool RET = g_instance->run(safeMode, lockedCrash);
|
||||
lockedCrash = g_instance->m_hyprlandLocked;
|
||||
g_instance.reset();
|
||||
|
||||
if (!RET) {
|
||||
|
||||
Reference in New Issue
Block a user