service/greetd: add greetd service
This commit is contained in:
parent
72956185bd
commit
3573663ab6
|
@ -24,6 +24,7 @@ option(SERVICE_STATUS_NOTIFIER "StatusNotifierItem service" ON)
|
||||||
option(SERVICE_PIPEWIRE "PipeWire service" ON)
|
option(SERVICE_PIPEWIRE "PipeWire service" ON)
|
||||||
option(SERVICE_MPRIS "Mpris service" ON)
|
option(SERVICE_MPRIS "Mpris service" ON)
|
||||||
option(SERVICE_PAM "Pam service" ON)
|
option(SERVICE_PAM "Pam service" ON)
|
||||||
|
option(SERVICE_GREETD "Greet service" ON)
|
||||||
|
|
||||||
message(STATUS "Quickshell configuration")
|
message(STATUS "Quickshell configuration")
|
||||||
message(STATUS " Jemalloc: ${USE_JEMALLOC}")
|
message(STATUS " Jemalloc: ${USE_JEMALLOC}")
|
||||||
|
@ -41,6 +42,7 @@ message(STATUS " StatusNotifier: ${SERVICE_STATUS_NOTIFIER}")
|
||||||
message(STATUS " PipeWire: ${SERVICE_PIPEWIRE}")
|
message(STATUS " PipeWire: ${SERVICE_PIPEWIRE}")
|
||||||
message(STATUS " Mpris: ${SERVICE_MPRIS}")
|
message(STATUS " Mpris: ${SERVICE_MPRIS}")
|
||||||
message(STATUS " Pam: ${SERVICE_PAM}")
|
message(STATUS " Pam: ${SERVICE_PAM}")
|
||||||
|
message(STATUS " Greetd: ${SERVICE_GREETD}")
|
||||||
message(STATUS " Hyprland: ${HYPRLAND}")
|
message(STATUS " Hyprland: ${HYPRLAND}")
|
||||||
if (HYPRLAND)
|
if (HYPRLAND)
|
||||||
message(STATUS " IPC: ${HYPRLAND_IPC}")
|
message(STATUS " IPC: ${HYPRLAND_IPC}")
|
||||||
|
|
|
@ -302,6 +302,12 @@ void EngineGeneration::assignIncubationController() {
|
||||||
this->engine->setIncubationController(controller);
|
this->engine->setIncubationController(controller);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EngineGeneration* EngineGeneration::currentGeneration() {
|
||||||
|
if (g_generations.size() == 1) {
|
||||||
|
return *g_generations.begin();
|
||||||
|
} else return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
EngineGeneration* EngineGeneration::findEngineGeneration(QQmlEngine* engine) {
|
EngineGeneration* EngineGeneration::findEngineGeneration(QQmlEngine* engine) {
|
||||||
return g_generations.value(engine);
|
return g_generations.value(engine);
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,10 @@ public:
|
||||||
static EngineGeneration* findEngineGeneration(QQmlEngine* engine);
|
static EngineGeneration* findEngineGeneration(QQmlEngine* engine);
|
||||||
static EngineGeneration* findObjectGeneration(QObject* object);
|
static EngineGeneration* findObjectGeneration(QObject* object);
|
||||||
|
|
||||||
|
// Returns the current generation if there is only one generation,
|
||||||
|
// otherwise null.
|
||||||
|
static EngineGeneration* currentGeneration();
|
||||||
|
|
||||||
RootWrapper* wrapper = nullptr;
|
RootWrapper* wrapper = nullptr;
|
||||||
QDir rootPath;
|
QDir rootPath;
|
||||||
QmlScanner scanner;
|
QmlScanner scanner;
|
||||||
|
@ -57,12 +61,14 @@ signals:
|
||||||
void filesChanged();
|
void filesChanged();
|
||||||
void reloadFinished();
|
void reloadFinished();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void quit();
|
||||||
|
void exit(int code);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onFileChanged(const QString& name);
|
void onFileChanged(const QString& name);
|
||||||
void onDirectoryChanged();
|
void onDirectoryChanged();
|
||||||
void incubationControllerDestroyed();
|
void incubationControllerDestroyed();
|
||||||
void quit();
|
|
||||||
void exit(int code);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void postReload();
|
void postReload();
|
||||||
|
|
|
@ -13,3 +13,7 @@ endif()
|
||||||
if (SERVICE_PAM)
|
if (SERVICE_PAM)
|
||||||
add_subdirectory(pam)
|
add_subdirectory(pam)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (SERVICE_GREETD)
|
||||||
|
add_subdirectory(greetd)
|
||||||
|
endif()
|
||||||
|
|
16
src/services/greetd/CMakeLists.txt
Normal file
16
src/services/greetd/CMakeLists.txt
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
qt_add_library(quickshell-service-greetd STATIC
|
||||||
|
qml.cpp
|
||||||
|
connection.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
qt_add_qml_module(quickshell-service-greetd
|
||||||
|
URI Quickshell.Services.Greetd
|
||||||
|
VERSION 0.1
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(quickshell-service-greetd PRIVATE ${QT_DEPS})
|
||||||
|
|
||||||
|
qs_pch(quickshell-service-greetd)
|
||||||
|
qs_pch(quickshell-service-greetdplugin)
|
||||||
|
|
||||||
|
target_link_libraries(quickshell PRIVATE quickshell-service-greetdplugin)
|
263
src/services/greetd/connection.cpp
Normal file
263
src/services/greetd/connection.cpp
Normal file
|
@ -0,0 +1,263 @@
|
||||||
|
#include "connection.hpp"
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include <qcontainerfwd.h>
|
||||||
|
#include <qjsonarray.h>
|
||||||
|
#include <qjsondocument.h>
|
||||||
|
#include <qjsonobject.h>
|
||||||
|
#include <qlocalsocket.h>
|
||||||
|
#include <qlogging.h>
|
||||||
|
#include <qloggingcategory.h>
|
||||||
|
#include <qobject.h>
|
||||||
|
#include <qtenvironmentvariables.h>
|
||||||
|
#include <qtmetamacros.h>
|
||||||
|
#include <qtypes.h>
|
||||||
|
|
||||||
|
#include "../../core/generation.hpp"
|
||||||
|
|
||||||
|
Q_LOGGING_CATEGORY(logGreetd, "quickshell.service.greetd");
|
||||||
|
|
||||||
|
QString GreetdState::toString(GreetdState::Enum value) {
|
||||||
|
switch (value) {
|
||||||
|
case GreetdState::Inactive: return "Inactive";
|
||||||
|
case GreetdState::Authenticating: return "Authenticating";
|
||||||
|
case GreetdState::ReadyToLaunch: return "Ready to Launch";
|
||||||
|
case GreetdState::Launching: return "Launching";
|
||||||
|
case GreetdState::Launched: return "Launched";
|
||||||
|
default: return "Invalid";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GreetdConnection::GreetdConnection() {
|
||||||
|
auto socket = qEnvironmentVariable("GREETD_SOCK");
|
||||||
|
|
||||||
|
if (socket.isEmpty()) {
|
||||||
|
this->mAvailable = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->mAvailable = true;
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
QObject::connect(&this->socket, &QLocalSocket::connected, this, &GreetdConnection::onSocketConnected);
|
||||||
|
QObject::connect(&this->socket, &QLocalSocket::readyRead, this, &GreetdConnection::onSocketReady);
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
this->socket.connectToServer(socket, QLocalSocket::ReadWrite);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GreetdConnection::createSession(QString user) {
|
||||||
|
if (!this->mAvailable) {
|
||||||
|
qCCritical(logGreetd) << "Greetd is not available.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user != this->mUser) {
|
||||||
|
this->mUser = std::move(user);
|
||||||
|
emit this->userChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
this->setActive(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GreetdConnection::cancelSession() { this->setActive(false); }
|
||||||
|
|
||||||
|
void GreetdConnection::respond(QString response) {
|
||||||
|
if (!this->mResponseRequired) {
|
||||||
|
qCCritical(logGreetd) << "Cannot respond to greetd as a response is not currently required.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->sendRequest({
|
||||||
|
{"type", "post_auth_message_response"},
|
||||||
|
{"response", response},
|
||||||
|
});
|
||||||
|
|
||||||
|
this->mResponseRequired = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GreetdConnection::launch(
|
||||||
|
const QList<QString>& command,
|
||||||
|
const QList<QString>& environment,
|
||||||
|
bool exit
|
||||||
|
) {
|
||||||
|
if (this->mState != GreetdState::ReadyToLaunch) {
|
||||||
|
qCCritical(logGreetd) << "Cannot call launch() as state is not currently ReadyToLaunch.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->mState = GreetdState::Launching;
|
||||||
|
this->mExitAfterLaunch = exit;
|
||||||
|
|
||||||
|
this->sendRequest({
|
||||||
|
{"type", "start_session"},
|
||||||
|
{"cmd", QJsonArray::fromStringList(command)},
|
||||||
|
{"env", QJsonArray::fromStringList(environment)},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GreetdConnection::isAvailable() const { return this->mAvailable; }
|
||||||
|
GreetdState::Enum GreetdConnection::state() const { return this->mState; }
|
||||||
|
|
||||||
|
void GreetdConnection::setActive(bool active) {
|
||||||
|
if (this->socket.state() == QLocalSocket::ConnectedState) {
|
||||||
|
this->mTargetActive = active;
|
||||||
|
if (active == (this->mState != GreetdState::Inactive)) return;
|
||||||
|
|
||||||
|
if (active) {
|
||||||
|
if (this->mUser.isEmpty()) {
|
||||||
|
qCCritical(logGreetd) << "Cannot activate greetd with unset user.";
|
||||||
|
this->setActive(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->sendRequest({
|
||||||
|
{"type", "create_session"},
|
||||||
|
{"username", this->mUser},
|
||||||
|
});
|
||||||
|
|
||||||
|
this->mState = GreetdState::Authenticating;
|
||||||
|
emit this->stateChanged();
|
||||||
|
} else {
|
||||||
|
this->sendRequest({
|
||||||
|
{"type", "cancel_session"},
|
||||||
|
});
|
||||||
|
|
||||||
|
this->setInactive();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (active != this->mTargetActive) {
|
||||||
|
this->mTargetActive = active;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GreetdConnection::setInactive() {
|
||||||
|
this->mTargetActive = false;
|
||||||
|
this->mResponseRequired = false;
|
||||||
|
this->mState = GreetdState::Inactive;
|
||||||
|
emit this->stateChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString GreetdConnection::user() const { return this->mUser; }
|
||||||
|
|
||||||
|
void GreetdConnection::onSocketConnected() {
|
||||||
|
qCDebug(logGreetd) << "Connected to greetd socket.";
|
||||||
|
|
||||||
|
if (this->mTargetActive) {
|
||||||
|
this->setActive(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GreetdConnection::onSocketError(QLocalSocket::LocalSocketError error) {
|
||||||
|
qCCritical(logGreetd) << "Greetd socket encountered an error and cannot continue:" << error;
|
||||||
|
|
||||||
|
this->mAvailable = false;
|
||||||
|
this->setActive(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GreetdConnection::onSocketReady() {
|
||||||
|
qint32 length = 0;
|
||||||
|
|
||||||
|
this->socket.read(
|
||||||
|
reinterpret_cast<char*>(&length), // NOLINT
|
||||||
|
sizeof(qint32)
|
||||||
|
);
|
||||||
|
|
||||||
|
auto text = this->socket.read(length);
|
||||||
|
auto json = QJsonDocument::fromJson(text).object();
|
||||||
|
auto type = json.value("type").toString();
|
||||||
|
|
||||||
|
qCDebug(logGreetd).noquote() << "Received greetd response:" << text;
|
||||||
|
|
||||||
|
if (type == "success") {
|
||||||
|
switch (this->mState) {
|
||||||
|
case GreetdState::Authenticating:
|
||||||
|
qCDebug(logGreetd) << "Authentication complete.";
|
||||||
|
this->mState = GreetdState::ReadyToLaunch;
|
||||||
|
emit this->stateChanged();
|
||||||
|
emit this->readyToLaunch();
|
||||||
|
break;
|
||||||
|
case GreetdState::Launching:
|
||||||
|
qCDebug(logGreetd) << "Target session set successfully.";
|
||||||
|
this->mState = GreetdState::Launched;
|
||||||
|
emit this->stateChanged();
|
||||||
|
emit this->launched();
|
||||||
|
|
||||||
|
if (this->mExitAfterLaunch) {
|
||||||
|
qCDebug(logGreetd) << "Quitting.";
|
||||||
|
EngineGeneration::currentGeneration()->quit();
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
default: goto unexpected;
|
||||||
|
}
|
||||||
|
} else if (type == "error") {
|
||||||
|
auto errorType = json.value("error_type").toString();
|
||||||
|
auto desc = json.value("description").toString();
|
||||||
|
|
||||||
|
// Special case this error in case a session was already running.
|
||||||
|
// This cancels and restarts the session.
|
||||||
|
if (errorType == "error" && desc == "a session is already being configured") {
|
||||||
|
qCDebug(logGreetd
|
||||||
|
) << "A session was already in progress, cancelling it and starting a new one.";
|
||||||
|
this->setActive(false);
|
||||||
|
this->setActive(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errorType == "auth_error") {
|
||||||
|
emit this->authFailure(desc);
|
||||||
|
this->setActive(false);
|
||||||
|
} else if (errorType == "error") {
|
||||||
|
qCWarning(logGreetd) << "Greetd error occurred" << desc;
|
||||||
|
emit this->error(desc);
|
||||||
|
} else goto unexpected;
|
||||||
|
|
||||||
|
// errors terminate the session
|
||||||
|
this->setInactive();
|
||||||
|
} else if (type == "auth_message") {
|
||||||
|
auto message = json.value("auth_message").toString();
|
||||||
|
auto type = json.value("auth_message_type").toString();
|
||||||
|
auto error = type == "error";
|
||||||
|
auto responseRequired = type == "visible" || type == "secret";
|
||||||
|
auto echoResponse = type != "secret";
|
||||||
|
|
||||||
|
this->mResponseRequired = responseRequired;
|
||||||
|
emit this->authMessage(message, error, responseRequired, echoResponse);
|
||||||
|
} else goto unexpected;
|
||||||
|
|
||||||
|
return;
|
||||||
|
unexpected:
|
||||||
|
qCCritical(logGreetd) << "Received unexpected greetd response" << text;
|
||||||
|
this->setActive(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GreetdConnection::sendRequest(const QJsonObject& json) {
|
||||||
|
auto text = QJsonDocument(json).toJson(QJsonDocument::Compact);
|
||||||
|
auto length = static_cast<qint32>(text.length());
|
||||||
|
|
||||||
|
if (logGreetd().isDebugEnabled()) {
|
||||||
|
auto debugJson = json;
|
||||||
|
|
||||||
|
if (json.value("type").toString() == "post_auth_message_response") {
|
||||||
|
debugJson["response"] = "<CENSORED>";
|
||||||
|
}
|
||||||
|
|
||||||
|
qCDebug(logGreetd).noquote() << "Sending greetd request:"
|
||||||
|
<< QJsonDocument(debugJson).toJson(QJsonDocument::Compact);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->socket.write(
|
||||||
|
reinterpret_cast<char*>(&length), // NOLINT
|
||||||
|
sizeof(qint32)
|
||||||
|
);
|
||||||
|
|
||||||
|
this->socket.write(text);
|
||||||
|
this->socket.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
GreetdConnection* GreetdConnection::instance() {
|
||||||
|
static auto* instance = new GreetdConnection(); // NOLINT
|
||||||
|
return instance;
|
||||||
|
}
|
74
src/services/greetd/connection.hpp
Normal file
74
src/services/greetd/connection.hpp
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <qcontainerfwd.h>
|
||||||
|
#include <qjsonobject.h>
|
||||||
|
#include <qlocalsocket.h>
|
||||||
|
#include <qobject.h>
|
||||||
|
#include <qqmlintegration.h>
|
||||||
|
#include <qtmetamacros.h>
|
||||||
|
|
||||||
|
class GreetdState: public QObject {
|
||||||
|
Q_OBJECT;
|
||||||
|
QML_ELEMENT;
|
||||||
|
QML_SINGLETON;
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum Enum {
|
||||||
|
Inactive = 0,
|
||||||
|
Authenticating = 1,
|
||||||
|
ReadyToLaunch = 2,
|
||||||
|
Launching = 3,
|
||||||
|
Launched = 4,
|
||||||
|
};
|
||||||
|
Q_ENUM(Enum);
|
||||||
|
|
||||||
|
Q_INVOKABLE static QString toString(GreetdState::Enum value);
|
||||||
|
};
|
||||||
|
|
||||||
|
class GreetdConnection: public QObject {
|
||||||
|
Q_OBJECT;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void createSession(QString user);
|
||||||
|
void cancelSession();
|
||||||
|
void respond(QString response);
|
||||||
|
void launch(const QList<QString>& command, const QList<QString>& environment, bool exit);
|
||||||
|
|
||||||
|
[[nodiscard]] bool isAvailable() const;
|
||||||
|
[[nodiscard]] GreetdState::Enum state() const;
|
||||||
|
|
||||||
|
[[nodiscard]] QString user() const;
|
||||||
|
|
||||||
|
static GreetdConnection* instance();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void authMessage(QString message, bool error, bool responseRequired, bool echoResponse);
|
||||||
|
void authFailure(QString message);
|
||||||
|
void readyToLaunch();
|
||||||
|
void launched();
|
||||||
|
void error(QString error);
|
||||||
|
|
||||||
|
void stateChanged();
|
||||||
|
void userChanged();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onSocketConnected();
|
||||||
|
void onSocketError(QLocalSocket::LocalSocketError error);
|
||||||
|
void onSocketReady();
|
||||||
|
|
||||||
|
private:
|
||||||
|
explicit GreetdConnection();
|
||||||
|
|
||||||
|
void sendRequest(const QJsonObject& json);
|
||||||
|
void setActive(bool active);
|
||||||
|
void setInactive();
|
||||||
|
|
||||||
|
bool mAvailable = false;
|
||||||
|
GreetdState::Enum mState = GreetdState::Inactive;
|
||||||
|
bool mTargetActive = false;
|
||||||
|
bool mExitAfterLaunch = false;
|
||||||
|
QString mMessage;
|
||||||
|
bool mResponseRequired = false;
|
||||||
|
QString mUser;
|
||||||
|
QLocalSocket socket;
|
||||||
|
};
|
7
src/services/greetd/module.md
Normal file
7
src/services/greetd/module.md
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
name = "Quickshell.Services.Greetd"
|
||||||
|
description = "Greetd integration"
|
||||||
|
headers = [
|
||||||
|
"qml.hpp",
|
||||||
|
"connection.hpp",
|
||||||
|
]
|
||||||
|
-----
|
46
src/services/greetd/qml.cpp
Normal file
46
src/services/greetd/qml.cpp
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
#include "qml.hpp"
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include <qcontainerfwd.h>
|
||||||
|
#include <qobject.h>
|
||||||
|
|
||||||
|
#include "connection.hpp"
|
||||||
|
|
||||||
|
Greetd::Greetd(QObject* parent): QObject(parent) {
|
||||||
|
auto* connection = GreetdConnection::instance();
|
||||||
|
|
||||||
|
QObject::connect(connection, &GreetdConnection::authMessage, this, &Greetd::authMessage);
|
||||||
|
QObject::connect(connection, &GreetdConnection::authFailure, this, &Greetd::authFailure);
|
||||||
|
QObject::connect(connection, &GreetdConnection::readyToLaunch, this, &Greetd::readyToLaunch);
|
||||||
|
QObject::connect(connection, &GreetdConnection::launched, this, &Greetd::launched);
|
||||||
|
QObject::connect(connection, &GreetdConnection::error, this, &Greetd::error);
|
||||||
|
|
||||||
|
QObject::connect(connection, &GreetdConnection::stateChanged, this, &Greetd::stateChanged);
|
||||||
|
QObject::connect(connection, &GreetdConnection::userChanged, this, &Greetd::userChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Greetd::createSession(QString user) {
|
||||||
|
GreetdConnection::instance()->createSession(std::move(user));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Greetd::cancelSession() { GreetdConnection::instance()->cancelSession(); }
|
||||||
|
|
||||||
|
void Greetd::respond(QString response) {
|
||||||
|
GreetdConnection::instance()->respond(std::move(response));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Greetd::launch(const QList<QString>& command) {
|
||||||
|
GreetdConnection::instance()->launch(command, {}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Greetd::launch(const QList<QString>& command, const QList<QString>& environment) {
|
||||||
|
GreetdConnection::instance()->launch(command, environment, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Greetd::launch(const QList<QString>& command, const QList<QString>& environment, bool quit) {
|
||||||
|
GreetdConnection::instance()->launch(command, environment, quit);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Greetd::isAvailable() { return GreetdConnection::instance()->isAvailable(); }
|
||||||
|
GreetdState::Enum Greetd::state() { return GreetdConnection::instance()->state(); }
|
||||||
|
QString Greetd::user() { return GreetdConnection::instance()->user(); }
|
95
src/services/greetd/qml.hpp
Normal file
95
src/services/greetd/qml.hpp
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <qcontainerfwd.h>
|
||||||
|
#include <qobject.h>
|
||||||
|
#include <qqmlintegration.h>
|
||||||
|
#include <qtmetamacros.h>
|
||||||
|
|
||||||
|
#include "connection.hpp"
|
||||||
|
|
||||||
|
/// This object provides access to a running greetd instance if present.
|
||||||
|
/// With it you can authenticate a user and launch a session.
|
||||||
|
///
|
||||||
|
/// See [the greetd wiki] for instructions on how to set up a graphical greeter.
|
||||||
|
///
|
||||||
|
/// [the greetd wiki]: https://man.sr.ht/~kennylevinsen/greetd/#setting-up-greetd-with-gtkgreet
|
||||||
|
class Greetd: public QObject {
|
||||||
|
Q_OBJECT;
|
||||||
|
/// If the greetd socket is available.
|
||||||
|
Q_PROPERTY(bool available READ isAvailable CONSTANT);
|
||||||
|
/// The current state of the greetd connection.
|
||||||
|
Q_PROPERTY(GreetdState::Enum state READ state NOTIFY stateChanged);
|
||||||
|
/// The currently authenticating user.
|
||||||
|
Q_PROPERTY(QString user READ user NOTIFY userChanged);
|
||||||
|
QML_ELEMENT;
|
||||||
|
QML_SINGLETON;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit Greetd(QObject* parent = nullptr);
|
||||||
|
|
||||||
|
/// Create a greetd session for the given user.
|
||||||
|
Q_INVOKABLE static void createSession(QString user);
|
||||||
|
/// Cancel the active greetd session.
|
||||||
|
Q_INVOKABLE static void cancelSession();
|
||||||
|
/// Respond to an authentication message.
|
||||||
|
///
|
||||||
|
/// May only be called in response to an `authMessage` with responseRequired set to true.
|
||||||
|
Q_INVOKABLE static void respond(QString response);
|
||||||
|
|
||||||
|
// docgen currently can't handle default params
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
/// Launch the session, exiting quickshell.
|
||||||
|
/// `readyToLaunch` must be true to call this function.
|
||||||
|
Q_INVOKABLE static void launch(const QList<QString>& command);
|
||||||
|
/// Launch the session, exiting quickshell.
|
||||||
|
/// `readyToLaunch` must be true to call this function.
|
||||||
|
Q_INVOKABLE static void launch(const QList<QString>& command, const QList<QString>& environment);
|
||||||
|
/// Launch the session, exiting quickshell if `quit` is true.
|
||||||
|
/// `readyToLaunch` must be true to call this function.
|
||||||
|
///
|
||||||
|
/// The `launched` signal can be used to perform an action after greetd has acknowledged
|
||||||
|
/// the desired session.
|
||||||
|
///
|
||||||
|
/// > [!WARNING] Note that greetd expects the greeter to terminate as soon as possible
|
||||||
|
/// > after setting a target session, and waiting too long may lead to unexpected behavior
|
||||||
|
/// > such as the greeter restarting.
|
||||||
|
/// >
|
||||||
|
/// > Performing animations and such should be done *before* calling `launch`.
|
||||||
|
Q_INVOKABLE static void launch(const QList<QString>& command, const QList<QString>& environment, bool quit);
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
[[nodiscard]] static bool isAvailable();
|
||||||
|
[[nodiscard]] static GreetdState::Enum state();
|
||||||
|
[[nodiscard]] static QString user();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
/// An authentication message has been sent by greetd.
|
||||||
|
/// - `message` - the text of the message
|
||||||
|
/// - `error` - if the message should be displayed as an error
|
||||||
|
/// - `responseRequired` - if a response via `respond()` is required for this message
|
||||||
|
/// - `echoResponse` - if the response should be displayed in clear text to the user
|
||||||
|
///
|
||||||
|
/// Note that `error` and `responseRequired` are mutually exclusive.
|
||||||
|
///
|
||||||
|
/// Errors are sent through `authMessage` when they are recoverable, such as a fingerprint scanner
|
||||||
|
/// not being able to read a finger correctly, while definite failures such as a bad password are
|
||||||
|
/// sent through `authFailure`.
|
||||||
|
void authMessage(QString message, bool error, bool responseRequired, bool echoResponse);
|
||||||
|
/// Authentication has failed an the session has terminated.
|
||||||
|
///
|
||||||
|
/// Usually this is something like a timeout or a failed password entry.
|
||||||
|
void authFailure(QString message);
|
||||||
|
/// Authentication has finished successfully and greetd can now launch a session.
|
||||||
|
void readyToLaunch();
|
||||||
|
/// Greetd has acknowledged the launch request and the greeter should quit as soon as possible.
|
||||||
|
///
|
||||||
|
/// This signal is sent right before quickshell exits automatically if the launch was not specifically
|
||||||
|
/// requested not to exit. You usually don't need to use this signal.
|
||||||
|
void launched();
|
||||||
|
/// Greetd has encountered an error.
|
||||||
|
void error(QString error);
|
||||||
|
|
||||||
|
void stateChanged();
|
||||||
|
void userChanged();
|
||||||
|
};
|
|
@ -1,11 +1,10 @@
|
||||||
#find_package(PAM REQUIRED)
|
|
||||||
|
|
||||||
qt_add_library(quickshell-service-pam STATIC
|
qt_add_library(quickshell-service-pam STATIC
|
||||||
qml.cpp
|
qml.cpp
|
||||||
conversation.cpp
|
conversation.cpp
|
||||||
ipc.cpp
|
ipc.cpp
|
||||||
subprocess.cpp
|
subprocess.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
qt_add_qml_module(quickshell-service-pam
|
qt_add_qml_module(quickshell-service-pam
|
||||||
URI Quickshell.Services.Pam
|
URI Quickshell.Services.Pam
|
||||||
VERSION 0.1
|
VERSION 0.1
|
||||||
|
|
Loading…
Reference in a new issue