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_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…
	
	Add table
		Add a link
		
	
		Reference in a new issue