mirror of
https://github.com/hyprwm/hyprlock.git
synced 2025-05-12 21:30:37 +01:00
Merge e9644ef21d
into f7f0c9c6b0
This commit is contained in:
commit
95a0ed15a9
20 changed files with 1105 additions and 44 deletions
|
@ -72,6 +72,7 @@ find_package(Threads REQUIRED)
|
|||
find_package(PkgConfig REQUIRED)
|
||||
find_package(OpenGL REQUIRED COMPONENTS EGL GLES3)
|
||||
find_package(hyprwayland-scanner 0.4.4 REQUIRED)
|
||||
find_package(glaze REQUIRED)
|
||||
pkg_check_modules(
|
||||
deps
|
||||
REQUIRED
|
||||
|
@ -93,7 +94,7 @@ pkg_check_modules(
|
|||
file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp")
|
||||
add_executable(hyprlock ${SRCFILES})
|
||||
target_link_libraries(hyprlock PRIVATE pam rt Threads::Threads PkgConfig::deps
|
||||
OpenGL::EGL OpenGL::GLES3)
|
||||
OpenGL::EGL OpenGL::GLES3 glaze::glaze)
|
||||
|
||||
# protocols
|
||||
pkg_get_variable(WAYLAND_PROTOCOLS_DIR wayland-protocols pkgdatadir)
|
||||
|
@ -128,7 +129,7 @@ function(protocolWayland)
|
|||
endfunction()
|
||||
|
||||
make_directory(${CMAKE_SOURCE_DIR}/protocols) # we don't ship any custom ones so
|
||||
# the dir won't be there
|
||||
# the dir won't be there
|
||||
|
||||
protocolwayland()
|
||||
|
||||
|
|
24
flake.lock
24
flake.lock
|
@ -13,11 +13,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1743953322,
|
||||
"narHash": "sha256-prQ5JKopXtzCMX2eT3dXbaVvGmzjMRE2bXStQDdazpM=",
|
||||
"lastModified": 1745015490,
|
||||
"narHash": "sha256-apEJ9zoSzmslhJ2vOKFcXTMZLUFYzh1ghfB6Rbw3Low=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprgraphics",
|
||||
"rev": "9d7f2687c84c729afbc3b13f7937655570f2978d",
|
||||
"rev": "60754910946b4e2dc1377b967b7156cb989c5873",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -39,11 +39,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1744468525,
|
||||
"narHash": "sha256-9HySx+EtsbbKlZDlY+naqqOV679VdxP6x6fP3wxDXJk=",
|
||||
"lastModified": 1745357019,
|
||||
"narHash": "sha256-q/C3qj9FWHQenObXuw/nGIT8iIsWFjgmcQYcA+ZfpPs=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprlang",
|
||||
"rev": "f1000c54d266e6e4e9d646df0774fac5b8a652df",
|
||||
"rev": "397600c42b8d7a443a5b4e92aa15f46650a90f18",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -62,11 +62,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1743950287,
|
||||
"narHash": "sha256-/6IAEWyb8gC/NKZElxiHChkouiUOrVYNq9YqG0Pzm4Y=",
|
||||
"lastModified": 1745975815,
|
||||
"narHash": "sha256-s3GzsRxBL/N/xYgUXZhQh4t62uR1BN4zxXgWBtJ3lWM=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprutils",
|
||||
"rev": "f2dc70e448b994cef627a157ee340135bd68fbc6",
|
||||
"rev": "05878d9470c9e5cbc8807813f9ec2006627a0ca0",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -100,11 +100,11 @@
|
|||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1744463964,
|
||||
"narHash": "sha256-LWqduOgLHCFxiTNYi3Uj5Lgz0SR+Xhw3kr/3Xd0GPTM=",
|
||||
"lastModified": 1746461020,
|
||||
"narHash": "sha256-7+pG1I9jvxNlmln4YgnlW4o+w0TZX24k688mibiFDUE=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "2631b0b7abcea6e640ce31cd78ea58910d31e650",
|
||||
"rev": "3730d8a308f94996a9ba7c7138ede69c1b9ac4ae",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
|
@ -2,8 +2,9 @@
|
|||
lib,
|
||||
stdenv,
|
||||
cmake,
|
||||
pkg-config,
|
||||
cairo,
|
||||
glaze,
|
||||
pkg-config,
|
||||
libdrm,
|
||||
libGL,
|
||||
libxkbcommon,
|
||||
|
@ -37,6 +38,7 @@ stdenv.mkDerivation {
|
|||
|
||||
buildInputs = [
|
||||
cairo
|
||||
glaze
|
||||
libdrm
|
||||
libGL
|
||||
libxkbcommon
|
||||
|
|
|
@ -1,20 +1,26 @@
|
|||
#include "Auth.hpp"
|
||||
#include "Pam.hpp"
|
||||
#include "Fingerprint.hpp"
|
||||
#include "../config/ConfigManager.hpp"
|
||||
#include "../core/hyprlock.hpp"
|
||||
#include "Fingerprint.hpp"
|
||||
#include "GreetdLogin.hpp"
|
||||
#include "Pam.hpp"
|
||||
#include "src/helpers/Log.hpp"
|
||||
|
||||
#include <hyprlang.hpp>
|
||||
#include <memory>
|
||||
|
||||
CAuth::CAuth() {
|
||||
static const auto ENABLEPAM = g_pConfigManager->getValue<Hyprlang::INT>("auth:pam:enabled");
|
||||
if (*ENABLEPAM)
|
||||
m_vImpls.emplace_back(makeShared<CPam>());
|
||||
CAuth::CAuth(bool sessionLogin) {
|
||||
static const auto ENABLEPAM = g_pConfigManager->getValue<Hyprlang::INT>("auth:pam:enabled");
|
||||
static const auto ENABLEFINGERPRINT = g_pConfigManager->getValue<Hyprlang::INT>("auth:fingerprint:enabled");
|
||||
if (*ENABLEFINGERPRINT)
|
||||
m_vImpls.emplace_back(makeShared<CFingerprint>());
|
||||
|
||||
if (sessionLogin)
|
||||
m_vImpls.emplace_back(makeShared<CGreetdLogin>());
|
||||
else {
|
||||
if (*ENABLEPAM)
|
||||
m_vImpls.emplace_back(makeShared<CPam>());
|
||||
if (*ENABLEFINGERPRINT)
|
||||
m_vImpls.emplace_back(makeShared<CFingerprint>());
|
||||
}
|
||||
|
||||
RASSERT(!m_vImpls.empty(), "At least one authentication method must be enabled!");
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
enum eAuthImplementations {
|
||||
AUTH_IMPL_PAM = 0,
|
||||
AUTH_IMPL_FINGERPRINT = 1,
|
||||
AUTH_IMPL_GREETD = 2,
|
||||
};
|
||||
|
||||
class IAuthImplementation {
|
||||
|
@ -28,7 +29,7 @@ class IAuthImplementation {
|
|||
|
||||
class CAuth {
|
||||
public:
|
||||
CAuth();
|
||||
CAuth(bool sessionLogin);
|
||||
|
||||
void start();
|
||||
|
||||
|
|
357
src/auth/GreetdLogin.cpp
Normal file
357
src/auth/GreetdLogin.cpp
Normal file
|
@ -0,0 +1,357 @@
|
|||
#include "GreetdLogin.hpp"
|
||||
#include "../config/ConfigManager.hpp"
|
||||
#include "../config/LoginSessionManager.hpp"
|
||||
#include "../core/hyprlock.hpp"
|
||||
#include "../helpers/Log.hpp"
|
||||
#include "../helpers/MiscFunctions.hpp"
|
||||
|
||||
#include <hyprutils/string/VarList.hpp>
|
||||
#include <hyprutils/os/Process.hpp>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
static constexpr std::string getErrorString(eRequestError error) {
|
||||
switch (error) {
|
||||
case GREETD_REQUEST_ERROR_SEND: return "Failed to send payload to greetd";
|
||||
case GREETD_REQUEST_ERROR_READ: return "Failed to read response from greetd";
|
||||
case GREETD_REQUEST_ERROR_PARSE: return "Failed to parse response from greetd";
|
||||
case GREETD_REQUEST_ERROR_FORMAT: return "Invalid greetd response";
|
||||
default: return "Unknown error";
|
||||
}
|
||||
};
|
||||
|
||||
static int socketConnect() {
|
||||
const int FD = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (FD < 0) {
|
||||
Debug::log(ERR, "Failed to create socket");
|
||||
return -1;
|
||||
}
|
||||
|
||||
sockaddr_un serverAddress = {.sun_family = 0};
|
||||
serverAddress.sun_family = AF_UNIX;
|
||||
|
||||
const auto PGREETDSOCK = std::getenv("GREETD_SOCK");
|
||||
|
||||
if (PGREETDSOCK == nullptr) {
|
||||
Debug::log(ERR, "GREETD_SOCK not set!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
strncpy(serverAddress.sun_path, PGREETDSOCK, sizeof(serverAddress.sun_path) - 1);
|
||||
|
||||
if (connect(FD, (sockaddr*)&serverAddress, SUN_LEN(&serverAddress)) < 0) {
|
||||
Debug::log(ERR, "Failed to connect to greetd socket");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return FD;
|
||||
}
|
||||
|
||||
// send <payload-length> <payload>
|
||||
static int sendToSock(int fd, const std::string& PAYLOAD) {
|
||||
const uint32_t LEN = PAYLOAD.size();
|
||||
uint32_t wrote = 0;
|
||||
|
||||
while (wrote < sizeof(LEN)) {
|
||||
auto n = write(fd, (char*)&LEN + wrote, sizeof(LEN) - wrote);
|
||||
if (n < 1) {
|
||||
Debug::log(ERR, "Failed to write to greetd socket");
|
||||
return -1;
|
||||
}
|
||||
|
||||
wrote += n;
|
||||
}
|
||||
|
||||
wrote = 0;
|
||||
|
||||
while (wrote < LEN) {
|
||||
auto n = write(fd, PAYLOAD.c_str() + wrote, LEN - wrote);
|
||||
if (n < 1) {
|
||||
Debug::log(ERR, "Failed to write to greetd socket");
|
||||
return -1;
|
||||
}
|
||||
|
||||
wrote += n;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// read <payload-length> <payload>
|
||||
static std::string readFromSock(int fd) {
|
||||
uint32_t len = 0;
|
||||
uint32_t numRead = 0;
|
||||
|
||||
while (numRead < sizeof(len)) {
|
||||
auto n = read(fd, (char*)&len + numRead, sizeof(len) - numRead);
|
||||
if (n < 1) {
|
||||
Debug::log(ERR, "Failed to read from greetd socket");
|
||||
return "";
|
||||
}
|
||||
|
||||
numRead += n;
|
||||
}
|
||||
|
||||
numRead = 0;
|
||||
std::string msg(len, '\0');
|
||||
|
||||
while (numRead < len) {
|
||||
auto n = read(fd, msg.data() + numRead, len - numRead);
|
||||
if (n < 1) {
|
||||
Debug::log(ERR, "Failed to read from greetd socket");
|
||||
return "";
|
||||
}
|
||||
|
||||
numRead += n;
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
static bool sendGreetdRequest(int fd, const VGreetdRequest& request) {
|
||||
if (fd < 0) {
|
||||
Debug::log(ERR, "[GreetdLogin] Invalid socket fd");
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto GLZRESULT = glz::write_json(request);
|
||||
|
||||
if (!GLZRESULT.has_value()) {
|
||||
const auto GLZERRORSTR = glz::format_error(GLZRESULT.error());
|
||||
Debug::log(ERR, "[GreetdLogin] Failed to serialize request: {}", GLZERRORSTR);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (std::holds_alternative<SGreetdPostAuthMessageResponse>(request))
|
||||
Debug::log(TRACE, "[GreetdLogin] Request: REDACTED");
|
||||
else
|
||||
Debug::log(TRACE, "[GreetdLogin] Request: {}", GLZRESULT.value());
|
||||
|
||||
if (sendToSock(fd, GLZRESULT.value()) < 0) {
|
||||
Debug::log(ERR, "[GreetdLogin] Failed to send payload to greetd");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CGreetdLogin::init() {
|
||||
const auto LOGINUSER = g_pConfigManager->getValue<Hyprlang::STRING>("login:user");
|
||||
m_loginUserName = *LOGINUSER;
|
||||
|
||||
if (m_loginUserName.empty()) {
|
||||
Debug::log(ERR, "[GreetdLogin] No user specified");
|
||||
m_ok = false;
|
||||
return;
|
||||
} else
|
||||
Debug::log(LOG, "[GreetdLogin] Login user: {}", m_loginUserName);
|
||||
|
||||
m_thread = std::thread([this]() {
|
||||
m_socketFD = socketConnect();
|
||||
if (m_socketFD < 0) {
|
||||
Debug::log(ERR, "[GreetdLogin] Failed to connect to greetd socket");
|
||||
m_ok = false;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
waitForInput();
|
||||
processInput();
|
||||
m_state.inputSubmitted = false;
|
||||
m_state.input.clear();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
std::expected<VGreetdResponse, eRequestError> CGreetdLogin::request(const VGreetdRequest& req) {
|
||||
if (!sendGreetdRequest(m_socketFD, req)) {
|
||||
m_ok = false;
|
||||
return std::unexpected(GREETD_REQUEST_ERROR_SEND);
|
||||
}
|
||||
|
||||
const auto RESPONSESTR = readFromSock(m_socketFD);
|
||||
if (RESPONSESTR.empty()) {
|
||||
Debug::log(ERR, "[GreetdLogin] Failed to read response from greetd");
|
||||
m_ok = false;
|
||||
return std::unexpected(GREETD_REQUEST_ERROR_READ);
|
||||
}
|
||||
|
||||
Debug::log(TRACE, "[GreetdLogin] Response: {}", RESPONSESTR);
|
||||
|
||||
const auto GLZRESULT = glz::read_json<VGreetdResponse>(RESPONSESTR);
|
||||
if (!GLZRESULT.has_value()) {
|
||||
const auto GLZERRORSTR = glz::format_error(GLZRESULT.error(), RESPONSESTR);
|
||||
Debug::log(ERR, "[GreetdLogin] Failed to parse response from greetd: {}", GLZERRORSTR);
|
||||
m_ok = false;
|
||||
return std::unexpected(GREETD_REQUEST_ERROR_PARSE);
|
||||
}
|
||||
|
||||
return GLZRESULT.value();
|
||||
}
|
||||
|
||||
void CGreetdLogin::handleResponse(const VGreetdRequest& request, const VGreetdResponse& response) {
|
||||
if (std::holds_alternative<SGreetdErrorResponse>(response)) {
|
||||
const auto ERRORRESPONSE = std::get<SGreetdErrorResponse>(response);
|
||||
m_state.errorType = ERRORRESPONSE.error_type;
|
||||
m_state.error = ERRORRESPONSE.description;
|
||||
Debug::log(ERR, "[GreetdLogin] Request failed: {} - {}", (int)m_state.errorType, m_state.error);
|
||||
// Don't post a fail if this is a response to "cancel_session"
|
||||
if (!m_state.error.empty() && !std::holds_alternative<SGreetdCancelSession>(request))
|
||||
g_pAuth->enqueueFail(m_state.error, AUTH_IMPL_GREETD);
|
||||
|
||||
// We don't have to cancel if "create_session" failed
|
||||
if (!std::holds_alternative<SGreetdCreateSession>(request))
|
||||
cancelSession();
|
||||
} else if (std::holds_alternative<SGreetdAuthMessageResponse>(response)) {
|
||||
const auto AUTHMESSAGERESPONSE = std::get<SGreetdAuthMessageResponse>(response);
|
||||
m_state.authMessageType = AUTHMESSAGERESPONSE.auth_message_type;
|
||||
m_state.message = AUTHMESSAGERESPONSE.auth_message;
|
||||
Debug::log(LOG, "[GreetdLogin] Auth message: {} - {}", (int)m_state.authMessageType, m_state.message);
|
||||
if (m_state.authMessageType == GREETD_AUTH_ERROR && !m_state.message.empty())
|
||||
g_pAuth->enqueueFail(m_state.message, AUTH_IMPL_GREETD);
|
||||
|
||||
} else if (std::holds_alternative<SGreetdSuccessResponse>(response)) {
|
||||
if (std::holds_alternative<SGreetdCreateSession>(request) || std::holds_alternative<SGreetdPostAuthMessageResponse>(request))
|
||||
startSessionAfterSuccess();
|
||||
} else
|
||||
Debug::log(ERR, "Unknown response from greetd");
|
||||
}
|
||||
|
||||
void CGreetdLogin::startSessionAfterSuccess() {
|
||||
const auto SELECTEDSESSION = g_pLoginSessionManager->getSelectedLoginSession();
|
||||
Hyprutils::String::CVarList args(SELECTEDSESSION.exec, 0, ' ');
|
||||
|
||||
SGreetdStartSession startSession;
|
||||
startSession.cmd = std::vector<std::string>{args.begin(), args.end()};
|
||||
|
||||
const auto REQUEST = VGreetdRequest{startSession};
|
||||
// TODO: Is there a response for this? Should we check it?
|
||||
if (!sendGreetdRequest(m_socketFD, REQUEST))
|
||||
m_ok = false;
|
||||
else {
|
||||
if (g_pHyprlock->m_currentDesktop == "Hyprland")
|
||||
spawnSync("hyprctl dispatch exit");
|
||||
else
|
||||
g_pAuth->enqueueUnlock();
|
||||
}
|
||||
}
|
||||
|
||||
void CGreetdLogin::cancelSession() {
|
||||
SGreetdCancelSession cancelSession;
|
||||
|
||||
const auto REQUEST = VGreetdRequest{cancelSession};
|
||||
const auto RESPONSE = request(REQUEST);
|
||||
|
||||
if (!RESPONSE.has_value()) {
|
||||
Debug::log(ERR, "Failed to cancel session: {}", getErrorString(RESPONSE.error()));
|
||||
return;
|
||||
}
|
||||
|
||||
handleResponse(REQUEST, RESPONSE.value());
|
||||
|
||||
m_state.authMessageType = GREETD_INITIAL;
|
||||
m_state.errorType = GREETD_OK;
|
||||
}
|
||||
|
||||
void CGreetdLogin::createSession() {
|
||||
if (m_state.authMessageType != GREETD_INITIAL && m_state.errorType != GREETD_ERROR_AUTH)
|
||||
Debug::log(WARN, "[GreetdLogin] Trying to create a session, but last one still active?");
|
||||
|
||||
Debug::log(LOG, "Creating session for user {}", m_loginUserName);
|
||||
|
||||
SGreetdCreateSession createSession;
|
||||
createSession.username = m_loginUserName;
|
||||
|
||||
const auto REQUEST = VGreetdRequest{createSession};
|
||||
const auto RESPONSE = request(REQUEST);
|
||||
|
||||
if (!RESPONSE.has_value()) {
|
||||
Debug::log(ERR, "Failed to create session: {}", getErrorString(RESPONSE.error()));
|
||||
return;
|
||||
}
|
||||
|
||||
handleResponse(REQUEST, RESPONSE.value());
|
||||
}
|
||||
|
||||
void CGreetdLogin::processInput() {
|
||||
if (!m_ok) {
|
||||
g_pAuth->enqueueFail("Greetd login NOK", AUTH_IMPL_GREETD);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_state.authMessageType == GREETD_INITIAL)
|
||||
createSession();
|
||||
|
||||
while (m_ok && (m_state.authMessageType == GREETD_AUTH_INFO || m_state.authMessageType == GREETD_AUTH_ERROR)) {
|
||||
// Empty reply
|
||||
SGreetdPostAuthMessageResponse postAuthMessageResponse;
|
||||
|
||||
const auto REQUEST = VGreetdRequest{postAuthMessageResponse};
|
||||
const auto RESPONSE = request(REQUEST);
|
||||
|
||||
if (!RESPONSE.has_value()) {
|
||||
Debug::log(ERR, "Failed to create session: {}", getErrorString(RESPONSE.error()));
|
||||
return;
|
||||
}
|
||||
|
||||
handleResponse(REQUEST, RESPONSE.value());
|
||||
}
|
||||
|
||||
if (m_state.errorType != GREETD_OK) {
|
||||
// TODO: this error message is not good
|
||||
Debug::log(LOG, "Empty response to a info message failed!");
|
||||
return;
|
||||
}
|
||||
|
||||
SGreetdPostAuthMessageResponse postAuthMessageResponse;
|
||||
postAuthMessageResponse.response = m_state.input;
|
||||
|
||||
const auto REQUEST = VGreetdRequest{postAuthMessageResponse};
|
||||
const auto RESPONSE = request(REQUEST);
|
||||
|
||||
if (!RESPONSE.has_value()) {
|
||||
Debug::log(ERR, "Failed to send auth response: {}", getErrorString(RESPONSE.error()));
|
||||
return;
|
||||
}
|
||||
|
||||
handleResponse(REQUEST, RESPONSE.value());
|
||||
};
|
||||
|
||||
void CGreetdLogin::waitForInput() {
|
||||
std::unique_lock<std::mutex> lk(m_state.inputMutex);
|
||||
m_state.inputSubmittedCondition.wait(lk, [this] { return m_state.inputSubmitted; });
|
||||
}
|
||||
|
||||
void CGreetdLogin::handleInput(const std::string& input) {
|
||||
std::unique_lock<std::mutex> lk(m_state.inputMutex);
|
||||
|
||||
m_state.input = input;
|
||||
m_state.inputSubmitted = true;
|
||||
|
||||
m_state.inputSubmittedCondition.notify_all();
|
||||
}
|
||||
|
||||
bool CGreetdLogin::checkWaiting() {
|
||||
return m_state.inputSubmitted;
|
||||
}
|
||||
|
||||
std::optional<std::string> CGreetdLogin::getLastFailText() {
|
||||
if (!m_state.error.empty()) {
|
||||
return m_state.error;
|
||||
} else if (m_state.authMessageType == GREETD_AUTH_ERROR)
|
||||
return m_state.message;
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> CGreetdLogin::getLastPrompt() {
|
||||
if (!m_state.message.empty())
|
||||
return m_state.message;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void CGreetdLogin::terminate() {
|
||||
if (m_socketFD > 0)
|
||||
close(m_socketFD);
|
||||
|
||||
m_socketFD = -1;
|
||||
}
|
66
src/auth/GreetdLogin.hpp
Normal file
66
src/auth/GreetdLogin.hpp
Normal file
|
@ -0,0 +1,66 @@
|
|||
#pragma once
|
||||
|
||||
#include "Auth.hpp"
|
||||
#include "GreetdProto.hpp"
|
||||
#include "../config/LoginSessionManager.hpp"
|
||||
#include <condition_variable>
|
||||
#include <string>
|
||||
#include <expected>
|
||||
#include <thread>
|
||||
|
||||
// INTERNAL
|
||||
enum eRequestError : uint8_t {
|
||||
GREETD_REQUEST_ERROR_SEND = 0,
|
||||
GREETD_REQUEST_ERROR_READ = 1,
|
||||
GREETD_REQUEST_ERROR_PARSE = 2,
|
||||
GREETD_REQUEST_ERROR_FORMAT = 3,
|
||||
};
|
||||
|
||||
class CGreetdLogin : public IAuthImplementation {
|
||||
public:
|
||||
virtual ~CGreetdLogin() = default;
|
||||
|
||||
virtual eAuthImplementations getImplType() {
|
||||
return AUTH_IMPL_GREETD;
|
||||
}
|
||||
virtual void init();
|
||||
virtual void handleInput(const std::string& input);
|
||||
virtual bool checkWaiting();
|
||||
virtual std::optional<std::string> getLastFailText();
|
||||
virtual std::optional<std::string> getLastPrompt();
|
||||
virtual void terminate();
|
||||
|
||||
struct {
|
||||
std::string error = "";
|
||||
eGreetdErrorMessageType errorType = GREETD_OK;
|
||||
std::string message = "";
|
||||
eGreetdAuthMessageType authMessageType = GREETD_INITIAL;
|
||||
|
||||
std::mutex inputMutex;
|
||||
std::string input;
|
||||
bool inputSubmitted = false;
|
||||
std::condition_variable inputSubmittedCondition;
|
||||
} m_state;
|
||||
|
||||
friend class CAuth;
|
||||
|
||||
private:
|
||||
std::expected<VGreetdResponse, eRequestError> request(const VGreetdRequest& req);
|
||||
|
||||
//
|
||||
void createSession();
|
||||
void cancelSession();
|
||||
void recreateSession();
|
||||
void startSessionAfterSuccess();
|
||||
|
||||
void handleResponse(const VGreetdRequest& request, const VGreetdResponse& response);
|
||||
void processInput();
|
||||
void waitForInput();
|
||||
|
||||
std::thread m_thread;
|
||||
|
||||
int m_socketFD = -1;
|
||||
std::string m_loginUserName = "";
|
||||
|
||||
bool m_ok = true;
|
||||
};
|
89
src/auth/GreetdProto.hpp
Normal file
89
src/auth/GreetdProto.hpp
Normal file
|
@ -0,0 +1,89 @@
|
|||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <variant>
|
||||
#include <glaze/glaze.hpp>
|
||||
#include <glaze/util/string_literal.hpp>
|
||||
|
||||
// GREETD PROTOCOL
|
||||
enum eGreetdResponse : uint8_t {
|
||||
GREETD_RESPONSE_UNKNOWN = 0xff,
|
||||
GREETD_RESPONSE_SUCCESS = 0,
|
||||
GREETD_RESPONSE_ERROR = 1,
|
||||
GREETD_RESPONSE_AUTH = 2,
|
||||
};
|
||||
|
||||
enum eGreetdErrorMessageType : uint8_t {
|
||||
GREETD_OK = 0,
|
||||
GREETD_ERROR_AUTH = 1,
|
||||
GREETD_ERROR = 2,
|
||||
};
|
||||
|
||||
enum eGreetdAuthMessageType : uint8_t {
|
||||
GREETD_INITIAL = 0,
|
||||
GREETD_AUTH_VISIBLE = 1,
|
||||
GREETD_AUTH_SECRET = 2,
|
||||
GREETD_AUTH_INFO = 3,
|
||||
GREETD_AUTH_ERROR = 4,
|
||||
};
|
||||
|
||||
// REQUEST TYPES
|
||||
struct SGreetdCreateSession {
|
||||
std::string type = "create_session";
|
||||
std::string username = "";
|
||||
};
|
||||
|
||||
struct SGreetdPostAuthMessageResponse {
|
||||
std::string type = "post_auth_message_response";
|
||||
std::string response = "";
|
||||
};
|
||||
|
||||
struct SGreetdStartSession {
|
||||
std::string type = "start_session";
|
||||
std::vector<std::string> cmd;
|
||||
std::vector<std::string> env;
|
||||
};
|
||||
|
||||
struct SGreetdCancelSession {
|
||||
std::string type = "cancel_session";
|
||||
};
|
||||
|
||||
// RESPONSE TYPES
|
||||
struct SGreetdErrorResponse {
|
||||
eGreetdErrorMessageType error_type;
|
||||
std::string description;
|
||||
};
|
||||
|
||||
struct SGreetdAuthMessageResponse {
|
||||
eGreetdAuthMessageType auth_message_type;
|
||||
std::string auth_message;
|
||||
};
|
||||
|
||||
struct SGreetdSuccessResponse {
|
||||
char DUMMY; // Without any field in SGreetdSuccessResponse, I get unknown_key for "type".
|
||||
};
|
||||
|
||||
// RESPONSE and REQUEST VARIANTS
|
||||
using VGreetdRequest = std::variant<SGreetdCreateSession, SGreetdPostAuthMessageResponse, SGreetdStartSession, SGreetdCancelSession>;
|
||||
using VGreetdResponse = std::variant<SGreetdSuccessResponse, SGreetdErrorResponse, SGreetdAuthMessageResponse>;
|
||||
|
||||
template <>
|
||||
struct glz::meta<eGreetdResponse> {
|
||||
static constexpr auto value = enumerate("success", GREETD_RESPONSE_SUCCESS, "error", GREETD_RESPONSE_ERROR, "auth_message", GREETD_RESPONSE_AUTH);
|
||||
};
|
||||
|
||||
template <>
|
||||
struct glz::meta<eGreetdAuthMessageType> {
|
||||
static constexpr auto value = enumerate("visible", GREETD_AUTH_VISIBLE, "secret", GREETD_AUTH_SECRET, "info", GREETD_AUTH_INFO, "error", GREETD_AUTH_ERROR);
|
||||
};
|
||||
|
||||
template <>
|
||||
struct glz::meta<eGreetdErrorMessageType> {
|
||||
static constexpr auto value = enumerate("auth_error", GREETD_ERROR_AUTH, "error", GREETD_ERROR);
|
||||
};
|
||||
|
||||
template <>
|
||||
struct glz::meta<VGreetdResponse> {
|
||||
static constexpr std::string_view tag = "type";
|
||||
static constexpr std::array ids{"success", "error", "auth_message"};
|
||||
};
|
|
@ -27,8 +27,8 @@ class ICustomConfigValueData {
|
|||
|
||||
class CLayoutValueData : public ICustomConfigValueData {
|
||||
public:
|
||||
CLayoutValueData() = default;
|
||||
virtual ~CLayoutValueData() {};
|
||||
CLayoutValueData() = default;
|
||||
virtual ~CLayoutValueData() = default;
|
||||
|
||||
virtual eConfigValueDataTypes getDataType() {
|
||||
return CVD_TYPE_LAYOUT;
|
||||
|
@ -61,12 +61,12 @@ class CLayoutValueData : public ICustomConfigValueData {
|
|||
|
||||
class CGradientValueData : public ICustomConfigValueData {
|
||||
public:
|
||||
CGradientValueData() {};
|
||||
CGradientValueData() = default;
|
||||
CGradientValueData(CHyprColor col) {
|
||||
m_vColors.push_back(col);
|
||||
updateColorsOk();
|
||||
};
|
||||
virtual ~CGradientValueData() {};
|
||||
virtual ~CGradientValueData() = default;
|
||||
|
||||
virtual eConfigValueDataTypes getDataType() {
|
||||
return CVD_TYPE_GRADIENT;
|
||||
|
@ -131,3 +131,9 @@ class CGradientValueData : public ICustomConfigValueData {
|
|||
return P;
|
||||
}
|
||||
};
|
||||
|
||||
struct SLoginSessionConfig {
|
||||
std::string name = "";
|
||||
std::string exec = "";
|
||||
std::string desktopFilePath = "";
|
||||
};
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
#include "../helpers/MiscFunctions.hpp"
|
||||
#include "../helpers/Log.hpp"
|
||||
#include "../core/AnimationManager.hpp"
|
||||
#include "../config/LoginSessionManager.hpp"
|
||||
#include <algorithm>
|
||||
#include <hyprlang.hpp>
|
||||
#include <hyprutils/string/String.hpp>
|
||||
#include <hyprutils/path/Path.hpp>
|
||||
|
@ -230,6 +232,12 @@ void CConfigManager::init() {
|
|||
|
||||
m_config.addConfigValue("animations:enabled", Hyprlang::INT{1});
|
||||
|
||||
m_config.addConfigValue("login:user", Hyprlang::STRING{"max"});
|
||||
m_config.addConfigValue("login:default_session", Hyprlang::STRING{""});
|
||||
m_config.addSpecialCategory("login-session", Hyprlang::SSpecialCategoryOptions{.key = nullptr, .anonymousKeyBased = true});
|
||||
m_config.addSpecialConfigValue("login-session", "name", Hyprlang::STRING{""});
|
||||
m_config.addSpecialConfigValue("login-session", "exec", Hyprlang::STRING{""});
|
||||
|
||||
m_config.addSpecialCategory("background", Hyprlang::SSpecialCategoryOptions{.key = nullptr, .anonymousKeyBased = true});
|
||||
m_config.addSpecialConfigValue("background", "monitor", Hyprlang::STRING{""});
|
||||
m_config.addSpecialConfigValue("background", "path", Hyprlang::STRING{""});
|
||||
|
@ -327,6 +335,22 @@ void CConfigManager::init() {
|
|||
SHADOWABLE("label");
|
||||
CLICKABLE("label");
|
||||
|
||||
m_config.addSpecialCategory("session-picker", Hyprlang::SSpecialCategoryOptions{.key = nullptr, .anonymousKeyBased = true});
|
||||
m_config.addSpecialConfigValue("session-picker", "monitor", Hyprlang::STRING{""});
|
||||
m_config.addSpecialConfigValue("session-picker", "position", LAYOUTCONFIG("0,0"));
|
||||
m_config.addSpecialConfigValue("session-picker", "size", LAYOUTCONFIG("10%,10%"));
|
||||
m_config.addSpecialConfigValue("session-picker", "rounding", Hyprlang::INT{0});
|
||||
m_config.addSpecialConfigValue("session-picker", "border_size", Hyprlang::INT{3});
|
||||
m_config.addSpecialConfigValue("session-picker", "entry_spacing", Hyprlang::FLOAT{10});
|
||||
m_config.addSpecialConfigValue("session-picker", "inner_color", Hyprlang::INT{0x00DDDDDD});
|
||||
m_config.addSpecialConfigValue("session-picker", "selected_color", Hyprlang::INT{0xFFA5A5A5});
|
||||
m_config.addSpecialConfigValue("session-picker", "border_color", GRADIENTCONFIG("0x00000000"));
|
||||
m_config.addSpecialConfigValue("session-picker", "selected_border_color", GRADIENTCONFIG("0xff6633ee"));
|
||||
m_config.addSpecialConfigValue("session-picker", "halign", Hyprlang::STRING{"none"});
|
||||
m_config.addSpecialConfigValue("session-picker", "valign", Hyprlang::STRING{"none"});
|
||||
m_config.addSpecialConfigValue("session-picker", "zindex", Hyprlang::INT{0});
|
||||
SHADOWABLE("session-picker");
|
||||
|
||||
m_config.registerHandler(&::handleSource, "source", {.allowFlags = false});
|
||||
m_config.registerHandler(&::handleBezier, "bezier", {.allowFlags = false});
|
||||
m_config.registerHandler(&::handleAnimation, "animation", {.allowFlags = false});
|
||||
|
@ -522,9 +546,54 @@ std::vector<CConfigManager::SWidgetConfig> CConfigManager::getWidgetConfigs() {
|
|||
// clang-format on
|
||||
}
|
||||
|
||||
keys = m_config.listKeysForSpecialCategory("session-picker");
|
||||
for (auto& k : keys) {
|
||||
// clang-format off
|
||||
result.push_back(CConfigManager::SWidgetConfig{
|
||||
.type="session-picker",
|
||||
.monitor=std::any_cast<Hyprlang::STRING>(m_config.getSpecialConfigValue("session-picker", "monitor", k.c_str())),
|
||||
.values={
|
||||
{"position", m_config.getSpecialConfigValue("session-picker", "position", k.c_str())},
|
||||
{"size", m_config.getSpecialConfigValue("session-picker", "size", k.c_str())},
|
||||
{"rounding", m_config.getSpecialConfigValue("session-picker", "rounding", k.c_str())},
|
||||
{"border_size", m_config.getSpecialConfigValue("session-picker", "border_size", k.c_str())},
|
||||
{"entry_spacing", m_config.getSpecialConfigValue("session-picker", "entry_spacing", k.c_str())},
|
||||
{"inner_color", m_config.getSpecialConfigValue("session-picker", "inner_color", k.c_str())},
|
||||
{"selected_color", m_config.getSpecialConfigValue("session-picker", "selected_color", k.c_str())},
|
||||
{"border_color", m_config.getSpecialConfigValue("session-picker", "border_color", k.c_str())},
|
||||
{"selected_border_color", m_config.getSpecialConfigValue("session-picker", "selected_border_color", k.c_str())},
|
||||
{"halign", m_config.getSpecialConfigValue("session-picker", "halign", k.c_str())},
|
||||
{"valign", m_config.getSpecialConfigValue("session-picker", "valign", k.c_str())},
|
||||
{"zindex", m_config.getSpecialConfigValue("session-picker", "zindex", k.c_str())},
|
||||
SHADOWABLE("session-picker"),
|
||||
}
|
||||
});
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<SLoginSessionConfig> CConfigManager::getLoginSessionConfigs() {
|
||||
std::vector<SLoginSessionConfig> result;
|
||||
|
||||
//
|
||||
auto keys = m_config.listKeysForSpecialCategory("login-session");
|
||||
result.reserve(keys.size());
|
||||
for (auto& k : keys) {
|
||||
result.push_back(SLoginSessionConfig{
|
||||
.name = std::any_cast<Hyprlang::STRING>(m_config.getSpecialConfigValue("login-session", "name", k.c_str())),
|
||||
.exec = std::any_cast<Hyprlang::STRING>(m_config.getSpecialConfigValue("login-session", "exec", k.c_str())),
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool CConfigManager::widgetsContainSessionPicker() {
|
||||
return !m_config.listKeysForSpecialCategory("session-picker").empty();
|
||||
}
|
||||
|
||||
std::optional<std::string> CConfigManager::handleSource(const std::string& command, const std::string& rawpath) {
|
||||
if (rawpath.length() < 2) {
|
||||
Debug::log(ERR, "source= path garbage");
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <unordered_map>
|
||||
|
||||
#include "../defines.hpp"
|
||||
#include "./ConfigDataValues.hpp"
|
||||
|
||||
class CConfigManager {
|
||||
public:
|
||||
|
@ -28,16 +29,22 @@ class CConfigManager {
|
|||
|
||||
std::vector<SWidgetConfig> getWidgetConfigs();
|
||||
|
||||
std::vector<SLoginSessionConfig> getLoginSessionConfigs();
|
||||
|
||||
bool widgetsContainSessionPicker();
|
||||
|
||||
std::optional<std::string> handleSource(const std::string&, const std::string&);
|
||||
std::optional<std::string> handleBezier(const std::string&, const std::string&);
|
||||
std::optional<std::string> handleAnimation(const std::string&, const std::string&);
|
||||
std::optional<std::string> handleLoginSession(const std::string&, const std::string&);
|
||||
|
||||
std::string configCurrentPath;
|
||||
|
||||
Hyprutils::Animation::CAnimationConfigTree m_AnimationTree;
|
||||
|
||||
private:
|
||||
Hyprlang::CConfig m_config;
|
||||
std::vector<SLoginSessionConfig> m_loginSessions;
|
||||
Hyprlang::CConfig m_config;
|
||||
};
|
||||
|
||||
inline UP<CConfigManager> g_pConfigManager;
|
||||
|
|
203
src/config/LoginSessionManager.cpp
Normal file
203
src/config/LoginSessionManager.cpp
Normal file
|
@ -0,0 +1,203 @@
|
|||
#include "./LoginSessionManager.hpp"
|
||||
#include "./ConfigManager.hpp"
|
||||
#include "../core/hyprlock.hpp"
|
||||
#include "../helpers/MiscFunctions.hpp"
|
||||
#include "../helpers/Log.hpp"
|
||||
#include "../renderer/AsyncResourceGatherer.hpp"
|
||||
#include "../renderer/Renderer.hpp"
|
||||
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
#include <hyprutils/string/VarList.hpp>
|
||||
|
||||
static bool parseDesktopFile(SLoginSessionConfig& sessionConfig) {
|
||||
RASSERT(!sessionConfig.desktopFilePath.empty(), "Desktop file path is empty");
|
||||
|
||||
// read line for line and parse the desktop file naivly
|
||||
std::ifstream fHandle(sessionConfig.desktopFilePath.c_str());
|
||||
std::string line;
|
||||
try {
|
||||
while (std::getline(fHandle, line)) {
|
||||
if (line.empty())
|
||||
continue;
|
||||
|
||||
if (line.find("Name=") != std::string::npos)
|
||||
sessionConfig.name = line.substr(5);
|
||||
else if (line.find("Exec=") != std::string::npos)
|
||||
sessionConfig.exec = line.substr(5);
|
||||
|
||||
if (!sessionConfig.name.empty() && !sessionConfig.exec.empty())
|
||||
break;
|
||||
}
|
||||
} catch (const std::ifstream::failure& e) {
|
||||
Debug::log(ERR, "Failed to read session file {}: {}", sessionConfig.desktopFilePath.c_str(), e.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sessionConfig.name.empty() || sessionConfig.exec.empty()) {
|
||||
Debug::log(ERR, "Failed to parse session file {}: missing name or exec", sessionConfig.desktopFilePath.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::vector<SLoginSessionConfig> gatherSessions(const std::vector<std::string>& searchPaths) {
|
||||
std::vector<SLoginSessionConfig> sessions;
|
||||
|
||||
for (const auto& DIR : searchPaths) {
|
||||
if (!std::filesystem::exists(DIR))
|
||||
continue;
|
||||
|
||||
for (const auto& dirEntry : std::filesystem::recursive_directory_iterator{DIR}) {
|
||||
if (!dirEntry.is_regular_file() || dirEntry.path().extension() != ".desktop")
|
||||
continue;
|
||||
|
||||
SLoginSessionConfig session;
|
||||
session.desktopFilePath = absolutePath(dirEntry.path().filename(), DIR);
|
||||
|
||||
if (!parseDesktopFile(session))
|
||||
continue;
|
||||
|
||||
sessions.emplace_back(session);
|
||||
}
|
||||
}
|
||||
|
||||
return sessions;
|
||||
}
|
||||
|
||||
CLoginSessionManager::CLoginSessionManager(const std::string& sessionDirs) {
|
||||
const auto LOGINDEFAULTSESSION = g_pConfigManager->getValue<Hyprlang::STRING>("login:default_session");
|
||||
|
||||
Debug::log(LOG, "LoginSessions: Default session: {}, Search directories: {}", std::string{*LOGINDEFAULTSESSION}, sessionDirs);
|
||||
|
||||
Hyprutils::String::CVarList sessionDirPaths{sessionDirs, 0, ':', true};
|
||||
m_loginSessions = gatherSessions(std::vector<std::string>{sessionDirPaths.begin(), sessionDirPaths.end()});
|
||||
const auto CONFIGUEDSESSIONS = g_pConfigManager->getLoginSessionConfigs();
|
||||
m_loginSessions.insert(m_loginSessions.end(), CONFIGUEDSESSIONS.begin(), CONFIGUEDSESSIONS.end());
|
||||
|
||||
if (const std::string DEFAULTSESSIONSTRING{*LOGINDEFAULTSESSION}; !DEFAULTSESSIONSTRING.empty()) {
|
||||
bool found = false;
|
||||
const auto ABSPATH = absolutePath(DEFAULTSESSIONSTRING, "/");
|
||||
// default session is a name
|
||||
for (size_t i = 0; i < m_loginSessions.size(); i++) {
|
||||
if (m_loginSessions[i].name == DEFAULTSESSIONSTRING) {
|
||||
m_selectedLoginSession = i;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
// default session is a path
|
||||
for (size_t i = 0; i < m_loginSessions.size(); i++) {
|
||||
if (m_loginSessions[i].desktopFilePath == ABSPATH) {
|
||||
m_selectedLoginSession = i;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
// default session is a path, but not contained in sessionDirs
|
||||
SLoginSessionConfig defaultSession;
|
||||
defaultSession.desktopFilePath = ABSPATH;
|
||||
if (parseDesktopFile(defaultSession)) {
|
||||
m_loginSessions.insert(m_loginSessions.begin(), defaultSession);
|
||||
m_selectedLoginSession = 0;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
Debug::log(WARN, "[LoginSessionManager] Default session {} not found", DEFAULTSESSIONSTRING);
|
||||
}
|
||||
|
||||
if (m_loginSessions.empty()) {
|
||||
Debug::log(CRIT,
|
||||
"Hyprlock did not find any wayland sessions.\n"
|
||||
"By default, hyprlock searches /usr/share/wayland-sessions and /usr/local/share/wayland-sessions.\n"
|
||||
"You can specify the directories hyprlock searches in with the --session-dirs argument followed by a comma seperated list of directories.\n"
|
||||
"Alternatively, you can specify a session with the login-session hyprlock keyword. Read the wiki for more info\n");
|
||||
|
||||
m_loginSessions.emplace_back(SLoginSessionConfig{
|
||||
.name = "Hyprland",
|
||||
.exec = "/usr/bin/Hyprland",
|
||||
});
|
||||
}
|
||||
|
||||
if (!g_pConfigManager->widgetsContainSessionPicker())
|
||||
m_fixedDefault = true;
|
||||
|
||||
requestSessionPickerAssets();
|
||||
}
|
||||
|
||||
void CLoginSessionManager::handleKeyUp() {
|
||||
if (m_fixedDefault)
|
||||
return;
|
||||
|
||||
if (m_loginSessions.size() > 1) {
|
||||
if (m_selectedLoginSession > 0)
|
||||
m_selectedLoginSession--;
|
||||
else
|
||||
m_selectedLoginSession = m_loginSessions.size() - 1;
|
||||
}
|
||||
}
|
||||
|
||||
void CLoginSessionManager::handleKeyDown() {
|
||||
if (m_fixedDefault)
|
||||
return;
|
||||
|
||||
if (m_loginSessions.size() > 1) {
|
||||
m_selectedLoginSession++;
|
||||
if (m_selectedLoginSession >= m_loginSessions.size())
|
||||
m_selectedLoginSession = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void CLoginSessionManager::onGotLoginSessionAssetCallback() {
|
||||
m_renderedSessionNames++;
|
||||
if (m_renderedSessionNames == m_loginSessionResourceIds.size())
|
||||
g_pHyprlock->renderAllOutputs();
|
||||
}
|
||||
|
||||
const SLoginSessionConfig& CLoginSessionManager::getSelectedLoginSession() const {
|
||||
return m_loginSessions[m_selectedLoginSession];
|
||||
}
|
||||
|
||||
size_t CLoginSessionManager::getSelectedLoginSessionIndex() const {
|
||||
return m_selectedLoginSession;
|
||||
}
|
||||
|
||||
const std::vector<SLoginSessionConfig>& CLoginSessionManager::getLoginSessions() const {
|
||||
return m_loginSessions;
|
||||
}
|
||||
|
||||
const std::vector<std::string>& CLoginSessionManager::getLoginSessionResourceIds() const {
|
||||
return m_loginSessionResourceIds;
|
||||
}
|
||||
|
||||
static void sessionNameAssetCallback() {
|
||||
g_pLoginSessionManager->onGotLoginSessionAssetCallback();
|
||||
}
|
||||
|
||||
void CLoginSessionManager::requestSessionPickerAssets() {
|
||||
m_loginSessionResourceIds = std::vector<std::string>{m_loginSessions.size(), ""};
|
||||
for (size_t i = 0; i < m_loginSessions.size(); ++i) {
|
||||
const auto& SESSIONCONFIG = m_loginSessions[i];
|
||||
m_loginSessionResourceIds[i] = std::format("session:{}-{}", (uintptr_t)this, SESSIONCONFIG.name);
|
||||
|
||||
// request asset preload
|
||||
CAsyncResourceGatherer::SPreloadRequest request;
|
||||
request.id = m_loginSessionResourceIds[i];
|
||||
request.asset = SESSIONCONFIG.name;
|
||||
request.type = CAsyncResourceGatherer::eTargetType::TARGET_TEXT;
|
||||
//request.props["font_family"] = fontFamily;
|
||||
//request.props["color"] = colorConfig.font;
|
||||
//request.props["font_size"] = rowHeight;
|
||||
request.callback = sessionNameAssetCallback;
|
||||
|
||||
g_pRenderer->asyncResourceGatherer->requestAsyncAssetPreload(request);
|
||||
}
|
||||
}
|
37
src/config/LoginSessionManager.hpp
Normal file
37
src/config/LoginSessionManager.hpp
Normal file
|
@ -0,0 +1,37 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "../defines.hpp"
|
||||
#include "./ConfigDataValues.hpp"
|
||||
|
||||
class CLoginSessionManager {
|
||||
public:
|
||||
CLoginSessionManager(const std::string& sessionDirs);
|
||||
~CLoginSessionManager() = default;
|
||||
|
||||
CLoginSessionManager(const CLoginSessionManager&) = delete;
|
||||
CLoginSessionManager& operator=(const CLoginSessionManager&) = delete;
|
||||
CLoginSessionManager(CLoginSessionManager&&) noexcept = delete;
|
||||
|
||||
void handleKeyUp();
|
||||
void handleKeyDown();
|
||||
void onGotLoginSessionAssetCallback();
|
||||
|
||||
const SLoginSessionConfig& getSelectedLoginSession() const;
|
||||
size_t getSelectedLoginSessionIndex() const;
|
||||
const std::vector<std::string>& getLoginSessionResourceIds() const;
|
||||
const std::vector<SLoginSessionConfig>& getLoginSessions() const;
|
||||
|
||||
private:
|
||||
std::vector<SLoginSessionConfig> m_loginSessions;
|
||||
std::vector<std::string> m_loginSessionResourceIds;
|
||||
size_t m_renderedSessionNames = 0;
|
||||
size_t m_selectedLoginSession = 0;
|
||||
|
||||
bool m_fixedDefault = false;
|
||||
|
||||
void requestSessionPickerAssets();
|
||||
};
|
||||
|
||||
inline UP<CLoginSessionManager> g_pLoginSessionManager;
|
|
@ -2,9 +2,11 @@
|
|||
#include "AnimationManager.hpp"
|
||||
#include "../helpers/Log.hpp"
|
||||
#include "../config/ConfigManager.hpp"
|
||||
#include "../config/LoginSessionManager.hpp"
|
||||
#include "../renderer/Renderer.hpp"
|
||||
#include "../auth/Auth.hpp"
|
||||
#include "../auth/Fingerprint.hpp"
|
||||
#include "../auth/GreetdLogin.hpp"
|
||||
#include "Egl.hpp"
|
||||
#include <hyprutils/memory/UniquePtr.hpp>
|
||||
#include <sys/wait.h>
|
||||
|
@ -35,7 +37,8 @@ static void setMallocThreshold() {
|
|||
#endif
|
||||
}
|
||||
|
||||
CHyprlock::CHyprlock(const std::string& wlDisplay, const bool immediate, const bool immediateRender) {
|
||||
CHyprlock::CHyprlock(const std::string& wlDisplay, const bool immediate, const bool immediateRender, const bool greetdLogin, const std::string& sessionDirs) :
|
||||
m_bGreetdLogin(greetdLogin), m_greetdSessionDirs(sessionDirs) {
|
||||
setMallocThreshold();
|
||||
|
||||
m_sWaylandState.display = wl_display_connect(wlDisplay.empty() ? nullptr : wlDisplay.c_str());
|
||||
|
@ -54,7 +57,10 @@ CHyprlock::CHyprlock(const std::string& wlDisplay, const bool immediate, const b
|
|||
|
||||
const auto CURRENTDESKTOP = getenv("XDG_CURRENT_DESKTOP");
|
||||
const auto SZCURRENTD = std::string{CURRENTDESKTOP ? CURRENTDESKTOP : ""};
|
||||
m_sCurrentDesktop = SZCURRENTD;
|
||||
m_currentDesktop = SZCURRENTD;
|
||||
|
||||
if (greetdLogin && m_greetdSessionDirs.empty())
|
||||
m_greetdSessionDirs = "/usr/share/wayland-sessions:/usr/local/share/wayland-sessions";
|
||||
}
|
||||
|
||||
CHyprlock::~CHyprlock() {
|
||||
|
@ -307,14 +313,15 @@ void CHyprlock::run() {
|
|||
// gather info about monitors
|
||||
wl_display_roundtrip(m_sWaylandState.display);
|
||||
|
||||
g_pRenderer = makeUnique<CRenderer>();
|
||||
g_pAuth = makeUnique<CAuth>();
|
||||
g_pRenderer = makeUnique<CRenderer>();
|
||||
g_pLoginSessionManager = makeUnique<CLoginSessionManager>(m_greetdSessionDirs);
|
||||
g_pAuth = makeUnique<CAuth>(m_bGreetdLogin);
|
||||
g_pAuth->start();
|
||||
|
||||
Debug::log(LOG, "Running on {}", m_sCurrentDesktop);
|
||||
Debug::log(LOG, "Running on {}", m_currentDesktop);
|
||||
|
||||
// Hyprland violates the protocol a bit to allow for this.
|
||||
if (m_sCurrentDesktop != "Hyprland") {
|
||||
if (m_currentDesktop != "Hyprland") {
|
||||
while (!g_pRenderer->asyncResourceGatherer->gathered) {
|
||||
wl_display_flush(m_sWaylandState.display);
|
||||
if (wl_display_prepare_read(m_sWaylandState.display) == 0) {
|
||||
|
@ -499,7 +506,7 @@ void CHyprlock::unlock() {
|
|||
return;
|
||||
}
|
||||
|
||||
const bool IMMEDIATE = m_sCurrentDesktop != "Hyprland";
|
||||
const bool IMMEDIATE = m_currentDesktop != "Hyprland";
|
||||
|
||||
g_pRenderer->startFadeOut(true, IMMEDIATE);
|
||||
m_bUnlockedCalled = true;
|
||||
|
@ -655,11 +662,15 @@ void CHyprlock::handleKeySym(xkb_keysym_t sym, bool composed) {
|
|||
m_sPasswordState.passBuffer.pop_back();
|
||||
m_sPasswordState.passBuffer = m_sPasswordState.passBuffer.substr(0, m_sPasswordState.passBuffer.length() - 1);
|
||||
}
|
||||
} else if (SYM == XKB_KEY_Caps_Lock) {
|
||||
} else if (SYM == XKB_KEY_Caps_Lock)
|
||||
m_bCapsLock = !m_bCapsLock;
|
||||
} else if (SYM == XKB_KEY_Num_Lock) {
|
||||
else if (SYM == XKB_KEY_Num_Lock)
|
||||
m_bNumLock = !m_bNumLock;
|
||||
} else {
|
||||
else if (SYM == XKB_KEY_Up)
|
||||
g_pLoginSessionManager->handleKeyUp();
|
||||
else if (SYM == XKB_KEY_Down)
|
||||
g_pLoginSessionManager->handleKeyDown();
|
||||
else {
|
||||
char buf[16] = {0};
|
||||
int len = (composed) ? xkb_compose_state_get_utf8(g_pSeatManager->m_pXKBComposeState, buf, sizeof(buf)) /* nullbyte */ + 1 :
|
||||
xkb_keysym_to_utf8(SYM, buf, sizeof(buf)) /* already includes a nullbyte */;
|
||||
|
|
|
@ -29,7 +29,7 @@ struct SDMABUFModifier {
|
|||
|
||||
class CHyprlock {
|
||||
public:
|
||||
CHyprlock(const std::string& wlDisplay, const bool immediate, const bool immediateRender);
|
||||
CHyprlock(const std::string& wlDisplay, const bool immediate, const bool immediateRender, const bool greetdLogin, const std::string& sessionDirs);
|
||||
~CHyprlock();
|
||||
|
||||
void run();
|
||||
|
@ -90,7 +90,7 @@ class CHyprlock {
|
|||
|
||||
bool m_bImmediateRender = false;
|
||||
|
||||
std::string m_sCurrentDesktop = "";
|
||||
bool m_bGreetdLogin = false;
|
||||
|
||||
//
|
||||
std::chrono::system_clock::time_point m_tGraceEnds;
|
||||
|
@ -119,6 +119,9 @@ class CHyprlock {
|
|||
} dma;
|
||||
gbm_device* createGBMDevice(drmDevice* dev);
|
||||
|
||||
std::string m_greetdSessionDirs = "";
|
||||
std::string m_currentDesktop = "";
|
||||
|
||||
private:
|
||||
struct {
|
||||
wl_display* display = nullptr;
|
||||
|
|
20
src/main.cpp
20
src/main.cpp
|
@ -2,6 +2,7 @@
|
|||
#include "config/ConfigManager.hpp"
|
||||
#include "core/hyprlock.hpp"
|
||||
#include "helpers/Log.hpp"
|
||||
#include "helpers/MiscFunctions.hpp"
|
||||
#include "core/AnimationManager.hpp"
|
||||
#include <cstddef>
|
||||
#include <string_view>
|
||||
|
@ -9,6 +10,7 @@
|
|||
void help() {
|
||||
std::println("Usage: hyprlock [options]\n\n"
|
||||
"Options:\n"
|
||||
" -g, --greetd - Start hyprlock for session login via greetd\n"
|
||||
" -v, --verbose - Enable verbose logging\n"
|
||||
" -q, --quiet - Disable logging\n"
|
||||
" -c FILE, --config FILE - Specify config file to use\n"
|
||||
|
@ -16,6 +18,7 @@ void help() {
|
|||
" --immediate - Lock immediately, ignoring any configured grace period\n"
|
||||
" --immediate-render - Do not wait for resources before drawing the background\n"
|
||||
" --no-fade-in - Disable the fade-in animation when the lock screen appears\n"
|
||||
" --session-dirs DIR1:DIR2 - Specify directories to search for session files\n"
|
||||
" -V, --version - Show version information\n"
|
||||
" -h, --help - Show this help message");
|
||||
}
|
||||
|
@ -43,9 +46,13 @@ int main(int argc, char** argv, char** envp) {
|
|||
bool immediate = false;
|
||||
bool immediateRender = false;
|
||||
bool noFadeIn = false;
|
||||
bool greetdLogin = false;
|
||||
|
||||
std::vector<std::string> args(argv, argv + argc);
|
||||
|
||||
// Used for greetd login
|
||||
std::string sessionDirs;
|
||||
|
||||
for (std::size_t i = 1; i < args.size(); ++i) {
|
||||
const std::string arg = argv[i];
|
||||
|
||||
|
@ -59,7 +66,16 @@ int main(int argc, char** argv, char** envp) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (arg == "--verbose" || arg == "-v")
|
||||
if (arg == "--greetd" || arg == "-g") {
|
||||
greetdLogin = true;
|
||||
immediate = true;
|
||||
immediateRender = true;
|
||||
} else if (arg == "--session-dirs" && i + 1 < (std::size_t)argc) {
|
||||
if (auto value = parseArg(args, arg, i); value)
|
||||
sessionDirs = *value;
|
||||
else
|
||||
return 1;
|
||||
} else if (arg == "--verbose" || arg == "-v")
|
||||
Debug::verbose = true;
|
||||
|
||||
else if (arg == "--quiet" || arg == "-q")
|
||||
|
@ -111,7 +127,7 @@ int main(int argc, char** argv, char** envp) {
|
|||
g_pConfigManager->m_AnimationTree.setConfigForNode("fadeIn", false, 0.f, "default");
|
||||
|
||||
try {
|
||||
g_pHyprlock = makeUnique<CHyprlock>(wlDisplay, immediate, immediateRender);
|
||||
g_pHyprlock = makeUnique<CHyprlock>(wlDisplay, immediate, immediateRender, greetdLogin, sessionDirs);
|
||||
g_pHyprlock->run();
|
||||
} catch (const std::exception& ex) {
|
||||
Debug::log(CRIT, "Hyprlock threw: {}", ex.what());
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "widgets/Label.hpp"
|
||||
#include "widgets/Image.hpp"
|
||||
#include "widgets/Shape.hpp"
|
||||
#include "widgets/SessionPicker.hpp"
|
||||
|
||||
inline const float fullVerts[] = {
|
||||
1, 0, // top right
|
||||
|
@ -420,6 +421,8 @@ std::vector<SP<IWidget>>& CRenderer::getOrCreateWidgetsFor(const CSessionLockSur
|
|||
createWidget<CShape>(widgets[surf.m_outputID]);
|
||||
} else if (c.type == "image") {
|
||||
createWidget<CImage>(widgets[surf.m_outputID]);
|
||||
} else if (c.type == "session-picker") {
|
||||
createWidget<CSessionPicker>(widgets[surf.m_outputID]);
|
||||
} else {
|
||||
Debug::log(ERR, "Unknown widget type: {}", c.type);
|
||||
continue;
|
||||
|
|
|
@ -376,7 +376,7 @@ void CPasswordInputField::updatePlaceholder() {
|
|||
request.props["font_family"] = fontFamily;
|
||||
request.props["color"] = colorState.font;
|
||||
request.props["font_size"] = (int)size->value().y / 4;
|
||||
request.callback = [REF = m_self] {
|
||||
request.callback = [REF = m_self]() {
|
||||
if (const auto SELF = REF.lock(); SELF)
|
||||
g_pHyprlock->renderOutput(SELF->outputStringPort);
|
||||
};
|
||||
|
|
123
src/renderer/widgets/SessionPicker.cpp
Normal file
123
src/renderer/widgets/SessionPicker.cpp
Normal file
|
@ -0,0 +1,123 @@
|
|||
#include "SessionPicker.hpp"
|
||||
#include "../Renderer.hpp"
|
||||
#include "../../helpers/Log.hpp"
|
||||
#include "../../helpers/Color.hpp"
|
||||
#include "../../config/ConfigDataValues.hpp"
|
||||
#include "../../config/LoginSessionManager.hpp"
|
||||
#include <algorithm>
|
||||
#include <hyprlang.hpp>
|
||||
#include <hyprutils/math/Vector2D.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
void CSessionPicker::registerSelf(const SP<CSessionPicker>& self) {
|
||||
m_self = self;
|
||||
}
|
||||
|
||||
void CSessionPicker::configure(const std::unordered_map<std::string, std::any>& props, const SP<COutput>& pOutput) {
|
||||
m_viewport = pOutput->getViewport();
|
||||
|
||||
m_shadow.configure(m_self.lock(), props, m_viewport);
|
||||
|
||||
try {
|
||||
m_configPos = CLayoutValueData::fromAnyPv(props.at("position"))->getAbsolute(m_viewport);
|
||||
m_size = CLayoutValueData::fromAnyPv(props.at("size"))->getAbsolute(m_viewport);
|
||||
m_rounding = std::any_cast<Hyprlang::INT>(props.at("rounding"));
|
||||
m_borderSize = std::any_cast<Hyprlang::INT>(props.at("border_size"));
|
||||
m_entrySpacing = std::any_cast<Hyprlang::FLOAT>(props.at("entry_spacing"));
|
||||
m_colorConfig.inner = std::any_cast<Hyprlang::INT>(props.at("inner_color"));
|
||||
m_colorConfig.selected = std::any_cast<Hyprlang::INT>(props.at("selected_color"));
|
||||
m_colorConfig.border = *CGradientValueData::fromAnyPv(props.at("border_color"));
|
||||
m_colorConfig.selectedBorder = *CGradientValueData::fromAnyPv(props.at("selected_border_color"));
|
||||
m_halign = std::any_cast<Hyprlang::STRING>(props.at("halign"));
|
||||
m_valign = std::any_cast<Hyprlang::STRING>(props.at("valign"));
|
||||
} catch (const std::bad_any_cast& e) {
|
||||
RASSERT(false, "Failed to construct CSessionPicker: {}", e.what()); //
|
||||
} catch (const std::out_of_range& e) {
|
||||
RASSERT(false, "Missing property for CSessionPicker: {}", e.what()); //
|
||||
}
|
||||
|
||||
setupSessionEntryTexts();
|
||||
}
|
||||
|
||||
bool CSessionPicker::draw(const SRenderData& data) {
|
||||
const size_t SELECTEDENTRYINDEX = g_pLoginSessionManager->getSelectedLoginSessionIndex();
|
||||
|
||||
const double PAD = std::abs((m_size.y - m_biggestEntryAssetSize.y) / 2);
|
||||
const Vector2D SIZE{std::max(m_size.x, m_biggestEntryAssetSize.x + PAD), m_size.y};
|
||||
const CBox RECTBOX{
|
||||
posFromHVAlign(m_viewport, SIZE, m_configPos, m_halign, m_valign),
|
||||
SIZE,
|
||||
};
|
||||
|
||||
const auto ENTRYHEIGHT = RECTBOX.h / (m_loginSessions.size() + 1);
|
||||
const auto TOPLEFT = RECTBOX.pos() + Vector2D{0.0, RECTBOX.h};
|
||||
|
||||
for (size_t i = 0; i < m_loginSessions.size(); ++i) {
|
||||
auto& sessionEntry = m_loginSessions[i];
|
||||
|
||||
const CBox ENTRYBOX{
|
||||
TOPLEFT.x,
|
||||
TOPLEFT.y - ENTRYHEIGHT - (i * (ENTRYHEIGHT + m_entrySpacing)),
|
||||
RECTBOX.w,
|
||||
ENTRYHEIGHT,
|
||||
};
|
||||
|
||||
const auto ENTRYROUND = roundingForBox(ENTRYBOX, m_rounding);
|
||||
const bool SELECTED = i == SELECTEDENTRYINDEX;
|
||||
|
||||
CHyprColor entryCol;
|
||||
if (SELECTED)
|
||||
entryCol = CHyprColor{m_colorConfig.selected.asRGB(), m_colorConfig.selected.a * data.opacity};
|
||||
else
|
||||
entryCol = CHyprColor{m_colorConfig.inner.asRGB(), m_colorConfig.inner.a * data.opacity};
|
||||
|
||||
g_pRenderer->renderRect(ENTRYBOX, entryCol, ENTRYROUND);
|
||||
if (m_borderSize > 0) {
|
||||
const CBox ENTRYBORDERBOX{
|
||||
ENTRYBOX.pos() - Vector2D{m_borderSize, m_borderSize},
|
||||
ENTRYBOX.size() + Vector2D{2 * m_borderSize, 2 * m_borderSize},
|
||||
};
|
||||
|
||||
const auto ENTRYBORDERROUND = roundingForBorderBox(ENTRYBORDERBOX, m_rounding, m_borderSize);
|
||||
g_pRenderer->renderBorder(ENTRYBORDERBOX, (SELECTED) ? m_colorConfig.selectedBorder : m_colorConfig.border, m_borderSize, ENTRYBORDERROUND, data.opacity);
|
||||
}
|
||||
|
||||
if (!sessionEntry.m_textAsset) {
|
||||
sessionEntry.m_textAsset = g_pRenderer->asyncResourceGatherer->getAssetByID(sessionEntry.m_textResourceID);
|
||||
if (sessionEntry.m_textAsset)
|
||||
m_biggestEntryAssetSize = Vector2D{
|
||||
std::max<double>(m_biggestEntryAssetSize.x, sessionEntry.m_textAsset->texture.m_vSize.x),
|
||||
sessionEntry.m_textAsset->texture.m_vSize.y,
|
||||
};
|
||||
}
|
||||
|
||||
if (sessionEntry.m_textAsset) {
|
||||
const CBox ASSETBOXCENTERED{
|
||||
ENTRYBOX.pos() +
|
||||
Vector2D{
|
||||
(ENTRYBOX.size().x / 2) - (sessionEntry.m_textAsset->texture.m_vSize.x / 2),
|
||||
(ENTRYBOX.size().y / 2) - (sessionEntry.m_textAsset->texture.m_vSize.y / 2),
|
||||
},
|
||||
sessionEntry.m_textAsset->texture.m_vSize,
|
||||
};
|
||||
g_pRenderer->renderTexture(ASSETBOXCENTERED, sessionEntry.m_textAsset->texture, data.opacity);
|
||||
}
|
||||
}
|
||||
|
||||
return false; // rely on the asset update callback in case m_textAsset is a nullptr
|
||||
}
|
||||
|
||||
void CSessionPicker::setupSessionEntryTexts() {
|
||||
const auto& LOGINSESSIONS = g_pLoginSessionManager->getLoginSessions();
|
||||
const auto& LOGINSESSIONRESOURCEIDS = g_pLoginSessionManager->getLoginSessionResourceIds();
|
||||
|
||||
RASSERT(LOGINSESSIONS.size() == LOGINSESSIONRESOURCEIDS.size(), "Login session resource IDs size does not match login sessions size");
|
||||
|
||||
m_loginSessions.resize(LOGINSESSIONS.size());
|
||||
for (size_t i = 0; i < LOGINSESSIONS.size(); i++) {
|
||||
m_loginSessions[i] = {
|
||||
.m_loginSession = LOGINSESSIONS[i],
|
||||
.m_textResourceID = LOGINSESSIONRESOURCEIDS[i],
|
||||
};
|
||||
};
|
||||
}
|
61
src/renderer/widgets/SessionPicker.hpp
Normal file
61
src/renderer/widgets/SessionPicker.hpp
Normal file
|
@ -0,0 +1,61 @@
|
|||
#pragma once
|
||||
|
||||
#include "IWidget.hpp"
|
||||
#include "Shadowable.hpp"
|
||||
#include "../../config/ConfigDataValues.hpp"
|
||||
#include "../../helpers/Color.hpp"
|
||||
#include <hyprutils/math/Vector2D.hpp>
|
||||
#include <string>
|
||||
#include <any>
|
||||
#include <vector>
|
||||
|
||||
struct SPreloadedAsset;
|
||||
class CSessionLockSurface;
|
||||
|
||||
class CSessionPicker : public IWidget {
|
||||
public:
|
||||
struct SSessionAsset {
|
||||
SLoginSessionConfig m_loginSession;
|
||||
std::string m_textResourceID;
|
||||
SPreloadedAsset* m_textAsset = nullptr;
|
||||
};
|
||||
|
||||
CSessionPicker() = default;
|
||||
~CSessionPicker() = default;
|
||||
|
||||
void registerSelf(const SP<CSessionPicker>& self);
|
||||
|
||||
virtual void configure(const std::unordered_map<std::string, std::any>& props, const SP<COutput>& pOutput);
|
||||
virtual bool draw(const SRenderData& data);
|
||||
|
||||
void onGotSessionEntryAsset(const std::string& sessionName);
|
||||
|
||||
private:
|
||||
void setupSessionEntryTexts();
|
||||
|
||||
WP<CSessionPicker> m_self;
|
||||
std::vector<SSessionAsset> m_loginSessions;
|
||||
|
||||
Vector2D m_viewport;
|
||||
Vector2D m_configPos;
|
||||
Vector2D m_size;
|
||||
std::string m_halign = "";
|
||||
std::string m_valign = "";
|
||||
int m_rounding = -1;
|
||||
int m_borderSize = -1;
|
||||
int m_entryHeight = -1;
|
||||
int m_entrySpacing = -1;
|
||||
|
||||
Vector2D m_biggestEntryAssetSize;
|
||||
|
||||
struct {
|
||||
CHyprColor inner;
|
||||
CHyprColor selected;
|
||||
|
||||
CGradientValueData border;
|
||||
CGradientValueData selectedBorder;
|
||||
} m_colorConfig;
|
||||
|
||||
CShadowable m_shadow;
|
||||
bool m_updateShadow = true;
|
||||
};
|
Loading…
Reference in a new issue