forked from quickshell/quickshell
		
	service/greetd: add greetd service
This commit is contained in:
		
							parent
							
								
									72956185bd
								
							
						
					
					
						commit
						3573663ab6
					
				
					 11 changed files with 522 additions and 4 deletions
				
			
		| 
						 | 
				
			
			@ -24,6 +24,7 @@ option(SERVICE_STATUS_NOTIFIER "StatusNotifierItem service" ON)
 | 
			
		|||
option(SERVICE_PIPEWIRE "PipeWire service" ON)
 | 
			
		||||
option(SERVICE_MPRIS "Mpris service" ON)
 | 
			
		||||
option(SERVICE_PAM "Pam service" ON)
 | 
			
		||||
option(SERVICE_GREETD "Greet service" ON)
 | 
			
		||||
 | 
			
		||||
message(STATUS "Quickshell configuration")
 | 
			
		||||
message(STATUS "  Jemalloc: ${USE_JEMALLOC}")
 | 
			
		||||
| 
						 | 
				
			
			@ -41,6 +42,7 @@ message(STATUS "    StatusNotifier: ${SERVICE_STATUS_NOTIFIER}")
 | 
			
		|||
message(STATUS "    PipeWire: ${SERVICE_PIPEWIRE}")
 | 
			
		||||
message(STATUS "    Mpris: ${SERVICE_MPRIS}")
 | 
			
		||||
message(STATUS "    Pam: ${SERVICE_PAM}")
 | 
			
		||||
message(STATUS "    Greetd: ${SERVICE_GREETD}")
 | 
			
		||||
message(STATUS "  Hyprland: ${HYPRLAND}")
 | 
			
		||||
if (HYPRLAND)
 | 
			
		||||
	message(STATUS "    IPC: ${HYPRLAND_IPC}")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -302,6 +302,12 @@ void EngineGeneration::assignIncubationController() {
 | 
			
		|||
	this->engine->setIncubationController(controller);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EngineGeneration* EngineGeneration::currentGeneration() {
 | 
			
		||||
	if (g_generations.size() == 1) {
 | 
			
		||||
		return *g_generations.begin();
 | 
			
		||||
	} else return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EngineGeneration* EngineGeneration::findEngineGeneration(QQmlEngine* engine) {
 | 
			
		||||
	return g_generations.value(engine);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -36,6 +36,10 @@ public:
 | 
			
		|||
	static EngineGeneration* findEngineGeneration(QQmlEngine* engine);
 | 
			
		||||
	static EngineGeneration* findObjectGeneration(QObject* object);
 | 
			
		||||
 | 
			
		||||
	// Returns the current generation if there is only one generation,
 | 
			
		||||
	// otherwise null.
 | 
			
		||||
	static EngineGeneration* currentGeneration();
 | 
			
		||||
 | 
			
		||||
	RootWrapper* wrapper = nullptr;
 | 
			
		||||
	QDir rootPath;
 | 
			
		||||
	QmlScanner scanner;
 | 
			
		||||
| 
						 | 
				
			
			@ -57,12 +61,14 @@ signals:
 | 
			
		|||
	void filesChanged();
 | 
			
		||||
	void reloadFinished();
 | 
			
		||||
 | 
			
		||||
public slots:
 | 
			
		||||
	void quit();
 | 
			
		||||
	void exit(int code);
 | 
			
		||||
 | 
			
		||||
private slots:
 | 
			
		||||
	void onFileChanged(const QString& name);
 | 
			
		||||
	void onDirectoryChanged();
 | 
			
		||||
	void incubationControllerDestroyed();
 | 
			
		||||
	void quit();
 | 
			
		||||
	void exit(int code);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	void postReload();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,3 +13,7 @@ endif()
 | 
			
		|||
if (SERVICE_PAM)
 | 
			
		||||
	add_subdirectory(pam)
 | 
			
		||||
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
 | 
			
		||||
	qml.cpp
 | 
			
		||||
	conversation.cpp
 | 
			
		||||
	ipc.cpp
 | 
			
		||||
	subprocess.cpp
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
qt_add_qml_module(quickshell-service-pam
 | 
			
		||||
	URI Quickshell.Services.Pam
 | 
			
		||||
	VERSION 0.1
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue