Add standalone login shell for diya-session-manager backend

This commit is contained in:
DanyLE 2025-03-08 00:45:13 +01:00
parent f722acdd20
commit dd8f2b3011
9 changed files with 385 additions and 10 deletions

View File

@ -48,18 +48,38 @@ foreach proto : wl_protocols
command: [ wayland_scanner, 'public-code', '@INPUT@', '@OUTPUT@' ] ) command: [ wayland_scanner, 'public-code', '@INPUT@', '@OUTPUT@' ] )
endforeach endforeach
src = [ gnome=import('gnome')
'src/base.c',
base = [
'src/base.c'
]
dm_src = [
base,
'src/launcher.c', 'src/launcher.c',
'src/background.c', 'src/background.c',
'src/wayland.c',
'src/shell.c', 'src/shell.c',
'src/foreign.c', 'src/foreign.c',
'src/session.c', 'src/session.c',
'src/main.c', 'src/wayland.c',
'src/dm.c',
wayland_targets] wayland_targets]
executable( executable(
'diya-shell', 'diya-shell',
src, dm_src,
dependencies: [gtk, gtk_layer_shell, wayland_client])
login_src = [
base,
'src/login-shell.c',
'src/login.c'
]
login_resource = gnome.compile_resources('resources','resources/login-shell/gresource.xml')
executable(
'diya-login-shell',
login_src,
login_resource,
dependencies: [gtk, gtk_layer_shell, wayland_client]) dependencies: [gtk, gtk_layer_shell, wayland_client])

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<gresources>
<gresource prefix="/dev/iohub/diya/shell">
<file alias="login-shell.css">resources/login-shell/login-shell.css</file>
</gresource>
</gresources>

View File

@ -0,0 +1,9 @@
.header {
font-size: 16px;
font-weight: bold;
}
.status {
font-size: 12px;
color: red;
}

View File

@ -33,7 +33,7 @@ static GParamSpec *g_so_prop[SO_N_PROPERTIES] = {0};
typedef struct _DiyaShellObjectPrivate typedef struct _DiyaShellObjectPrivate
{ {
DiyaObject * parent; DiyaObject parent;
DiyaShell * shell; DiyaShell * shell;
} DiyaShellObjectPrivate; } DiyaShellObjectPrivate;
G_DEFINE_TYPE_WITH_PRIVATE(DiyaShellObject, diya_shell_object, DIYA_TYPE_OBJECT); G_DEFINE_TYPE_WITH_PRIVATE(DiyaShellObject, diya_shell_object, DIYA_TYPE_OBJECT);

273
src/login-shell.c Normal file
View File

@ -0,0 +1,273 @@
#include "login-shell.h"
#include <gtk4-layer-shell.h>
#include <gtk4-session-lock.h>
#include <gio/gio.h>
#define DBUS_SERVER_NAME "dev.iohub.diya.SessionManager"
#define DBUS_SERVER_PATH "/dev/iohub/diya/SessionManager"
#define DBUS_SERVER_ERROR_NAME "dev.iohub.diya.SessionManager.Error"
struct _DiyaLoginShell
{
DiyaObject parent_object;
GtkApplication *app;
GtkSessionLockInstance *lock;
GtkWidget *username;
GtkWidget *password;
GtkWidget *status;
guint bus_watch_id;
};
G_DEFINE_FINAL_TYPE(DiyaLoginShell, diya_login_shell, DIYA_TYPE_OBJECT);
static void diya_login_shell_dispose(GObject *object)
{
DiyaLoginShell *self = DIYA_LOGIN_SHELL(object);
g_debug("diya_login_shell_dispose: %s", diya_object_to_string(self));
if (self->lock)
{
if (gtk_session_lock_instance_is_locked(self->lock))
{
gtk_session_lock_instance_unlock(self->lock);
}
g_object_unref(self->lock);
}
if (self->app)
{
g_object_unref(self->app);
}
if (self->bus_watch_id > 0)
{
g_bus_unwatch_name(self->bus_watch_id);
}
G_OBJECT_CLASS(diya_login_shell_parent_class)->dispose(object);
}
static void diya_login_shell_init(DiyaLoginShell *self)
{
self->app = NULL;
self->lock = gtk_session_lock_instance_new();
self->username = NULL;
self->password = NULL;
self->bus_watch_id = 0;
}
static const gchar *diya_login_shell_to_string(DiyaObject *object)
{
(void)object;
return "Diya Login Shell";
}
static void diya_login_shell_class_init(DiyaLoginShellClass *class)
{
GObjectClass *gobject_class = G_OBJECT_CLASS(class);
DiyaObjectClass *base_class = DIYA_OBJECT_CLASS(class);
gobject_class->dispose = diya_login_shell_dispose;
// gobject_class->set_property = diya_lock_session_set_property;
// gobject_class->get_property = diya_lock_session_get_property;
base_class->to_string = diya_login_shell_to_string;
}
static void on_method_call_return(GObject *source_object, GAsyncResult *res, gpointer user_data)
{
(void)source_object;
DiyaLoginShell *self = DIYA_LOGIN_SHELL(user_data);
GError *error = NULL;
GDBusMessage *reply = g_dbus_connection_send_message_with_reply_finish(G_DBUS_CONNECTION(source_object), res, &error);
if (error)
{
g_warning("Method call error: %s", error->message);
gtk_label_set_text(GTK_LABEL(self->status), "Unable send request to session manager");
g_error_free(error);
return;
}
if (g_dbus_message_get_message_type(reply) == G_DBUS_MESSAGE_TYPE_ERROR)
{
g_dbus_message_to_gerror(reply, &error);
g_warning("Server return error: %s", error->message);
gtk_label_set_text(GTK_LABEL(self->status), "Session manager return with error");
g_error_free(error);
g_object_unref(reply);
return;
}
GVariant *result = g_dbus_message_get_body(reply);
gboolean success;
g_variant_get(result, "(b)", &success);
if (success)
{
gtk_label_set_text(GTK_LABEL(self->status), "Login successful");
g_application_quit(G_APPLICATION(self->app));
}
else
{
gtk_label_set_text(GTK_LABEL(self->status), "Login failed! Please try again.");
}
g_variant_unref(result);
//g_object_unref(reply);
}
static void on_name_appeared(GDBusConnection *connection, const gchar *name, const gchar *name_owner, gpointer user_data)
{
(void)connection;
DiyaLoginShell *self = DIYA_LOGIN_SHELL(user_data);
g_debug("Name appeared: %s %s", name, name_owner);
/**
* send dbus message to SessionManager to request login
*/
GDBusMessage *request = NULL;
const gchar *username = gtk_editable_get_text(GTK_EDITABLE(self->username));
const gchar *password = gtk_editable_get_text(GTK_EDITABLE(self->password));
g_debug("Login request for user: %s", username);
request = g_dbus_message_new_method_call(name_owner, DBUS_SERVER_PATH, DBUS_SERVER_NAME, "login");
g_dbus_message_set_body(request, g_variant_new("(ss)", username, password));
g_dbus_connection_send_message_with_reply(connection, request, G_DBUS_SEND_MESSAGE_FLAGS_NONE, -1, NULL, NULL, on_method_call_return, self);
g_object_unref(request);
g_bus_unwatch_name (self->bus_watch_id);
self->bus_watch_id = 0;
}
static void on_name_vanished(GDBusConnection *connection, const gchar *name, gpointer user_data)
{
(void)connection;
DiyaLoginShell *self = DIYA_LOGIN_SHELL(user_data);
g_warning("Name vanished: %s. Unwatch it", name);
gtk_label_set_text(GTK_LABEL(self->status), "Unable to connect to session manager");
g_bus_unwatch_name(self->bus_watch_id);
self->bus_watch_id = 0;
}
static void do_login(GtkButton *button, DiyaLoginShell *self)
{
(void)button;
const gchar *username = gtk_editable_get_text(GTK_EDITABLE(self->username));
const gchar *password = gtk_editable_get_text(GTK_EDITABLE(self->password));
if (!username || !password || strlen(username) == 0 || strlen(password) == 0)
{
gtk_label_set_text(GTK_LABEL(self->status), "Please enter username and password");
return;
}
if (self->bus_watch_id > 0)
{
g_warning("A login operation is already in progress");
gtk_label_set_text(GTK_LABEL(self->status), "A login operation is already in progress");
return;
}
/**
* watch the bus name
*/
self->bus_watch_id = g_bus_watch_name(G_BUS_TYPE_SYSTEM,
DBUS_SERVER_NAME,
G_BUS_NAME_WATCHER_FLAGS_NONE,
on_name_appeared,
on_name_vanished,
self,
NULL);
// g_application_quit(G_APPLICATION(self->app));
}
DiyaLoginShell *diya_login_shell_new()
{
return g_object_new(DIYA_TYPE_LOGIN_SHELL, NULL);
}
void diya_login_shell_attach(DiyaLoginShell *self, GtkApplication *app)
{
self->app = app;
if (!gtk_session_lock_instance_lock(self->lock))
{
g_error("gtk_session_lock_instance_lock: Unable to lock the display");
g_object_unref(self);
return;
}
GdkDisplay *display = gdk_display_get_default();
GListModel *monitors = gdk_display_get_monitors(display);
guint n_monitors = g_list_model_get_n_items(monitors);
for (guint i = 0; i < n_monitors; ++i)
{
GdkMonitor *monitor = g_list_model_get_item(monitors, i);
GtkWindow *gtk_window = GTK_WINDOW(gtk_application_window_new(app));
gtk_session_lock_instance_assign_window_to_monitor(self->lock, gtk_window, monitor);
GtkWidget *box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
gtk_widget_set_halign(box, GTK_ALIGN_CENTER);
gtk_widget_set_valign(box, GTK_ALIGN_CENTER);
gtk_box_set_spacing(GTK_BOX(box), 10);
GtkWidget *label;
label = gtk_label_new("Please login");
gtk_box_append(GTK_BOX(box), label);
gtk_widget_add_css_class(label, "header");
GtkWidget *user_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
gtk_widget_set_valign(user_box, GTK_ALIGN_CENTER);
gtk_widget_set_halign(user_box, GTK_ALIGN_END);
gtk_box_set_spacing(GTK_BOX(user_box), 10);
label = gtk_label_new("Username");
gtk_box_append(GTK_BOX(user_box), label);
self->username = gtk_entry_new();
gtk_box_append(GTK_BOX(user_box), GTK_WIDGET(self->username));
gtk_box_append(GTK_BOX(box), user_box);
GtkWidget *password_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
gtk_widget_set_valign(password_box, GTK_ALIGN_CENTER);
gtk_widget_set_halign(password_box, GTK_ALIGN_END);
gtk_box_set_spacing(GTK_BOX(password_box), 10);
label = gtk_label_new("Password");
gtk_box_append(GTK_BOX(password_box), label);
self->password = gtk_password_entry_new();
gtk_box_append(GTK_BOX(password_box), GTK_WIDGET(self->password));
gtk_box_append(GTK_BOX(box), password_box);
GtkWidget *button = gtk_button_new_with_label("Login");
g_signal_connect(self->username, "activate", G_CALLBACK(do_login), self);
g_signal_connect(self->password, "activate", G_CALLBACK(do_login), self);
g_signal_connect(button, "clicked", G_CALLBACK(do_login), self);
gtk_box_append(GTK_BOX(box), button);
// Not displayed, but allows testing that creating popups doesn't crash GTK
gtk_widget_set_tooltip_text(button, "Login");
self->status = gtk_label_new(NULL);
gtk_box_append(GTK_BOX(box), self->status);
gtk_widget_add_css_class(self->status, "status");
gtk_window_set_child(GTK_WINDOW(gtk_window), box);
gtk_window_present(gtk_window);
}
// load xml content from gresource
GError *err = NULL;
GBytes *bytes = g_resources_lookup_data("/dev/iohub/diya/shell/login-shell.css", 0, &err);
if (err != NULL)
{
g_critical("Unable to load CSS resource: %s", err->message);
g_error_free(err);
return;
}
else
{
const char *data = g_bytes_get_data(bytes, NULL);
g_debug("CSS resource loaded:\n %s", data);
GtkCssProvider *provider = gtk_css_provider_new();
gtk_css_provider_load_from_string(provider, data);
gtk_style_context_add_provider_for_display(
gdk_display_get_default(),
GTK_STYLE_PROVIDER(provider),
GTK_STYLE_PROVIDER_PRIORITY_USER);
g_bytes_unref(bytes);
}
}

14
src/login-shell.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef LOGIN_SHELL_H
#define LOGIN_SHELL_H
#include <gtk/gtk.h>
#include "base.h"
#define DIYA_TYPE_LOGIN_SHELL (diya_login_shell_get_type())
G_DECLARE_FINAL_TYPE(DiyaLoginShell, diya_login_shell, DIYA, LOGIN_SHELL, DiyaObject);
DiyaLoginShell *diya_login_shell_new();
void diya_login_shell_attach(DiyaLoginShell *self, GtkApplication *app);
#endif

57
src/login.c Normal file
View File

@ -0,0 +1,57 @@
#include <glib-unix.h>
#include <assert.h>
#include "login-shell.h"
static gchar **g_shell_argv;
static void activate(GtkApplication *app, void *data)
{
(void)app;
(void)data;
}
static gboolean restart(gpointer d)
{
(void)d;
gint i, fdlimit;
fdlimit = (int)sysconf(_SC_OPEN_MAX);
g_debug("reload: closing fd's %d to %d", STDERR_FILENO + 1, fdlimit);
for (i = STDERR_FILENO + 1; i < fdlimit; i++)
{
fcntl(i, F_SETFD, FD_CLOEXEC);
}
g_debug("reload: exec: %s", g_shell_argv[0]);
execvp(g_shell_argv[0], g_shell_argv);
exit(1);
return FALSE;
}
static void startup(GtkApplication *app, DiyaLoginShell* shell)
{
g_unix_signal_add(SIGHUP, (GSourceFunc)restart, NULL);
g_unix_signal_add(SIGINT,(GSourceFunc)g_application_quit,(void*)app);
diya_login_shell_attach(shell, app);
}
int main(int argc, char *argv[])
{
g_shell_argv = g_malloc0(sizeof(gchar *) * (argc + 1));
for (int i = 0; i < argc; i++)
{
g_shell_argv[i] = argv[i];
}
GtkApplication* app = gtk_application_new("dev.iohub.diya.login-shell", G_APPLICATION_DEFAULT_FLAGS);
DiyaLoginShell* shell = diya_login_shell_new();
g_signal_connect(app, "startup", G_CALLBACK(startup), shell);
g_signal_connect(app, "activate", G_CALLBACK(activate), shell);
// g_signal_connect(app, "shutdown", G_CALLBACK(shutdown), (void*)shell);
int status = g_application_run(G_APPLICATION(app), argc, argv);
g_warning("Exiting SHELL");
g_object_unref(shell);
return status;
}

View File

@ -21,10 +21,6 @@ G_DECLARE_FINAL_TYPE (DiyaWayland, diya_wayland, DIYA, WAYLAND, DiyaShellObject)
#define DIYA_TYPE_FOREIGN_WINDOW (diya_foreign_window_get_type ()) #define DIYA_TYPE_FOREIGN_WINDOW (diya_foreign_window_get_type ())
G_DECLARE_FINAL_TYPE (DiyaForeignWindow, diya_foreign_window, DIYA, FOREIGN_WINDOW, DiyaShellObject) G_DECLARE_FINAL_TYPE (DiyaForeignWindow, diya_foreign_window, DIYA, FOREIGN_WINDOW, DiyaShellObject)
#define DIYA_TYPE_LOCK_SESSION (diya_lock_session_get_type ())
G_DECLARE_FINAL_TYPE (DiyaLockSession, diya_lock_session, DIYA, LOCK_SESSION, DiyaShellObject)
DiyaForeignWindow* diya_shell_get_window(DiyaShell * shell, gpointer handle); DiyaForeignWindow* diya_shell_get_window(DiyaShell * shell, gpointer handle);
gboolean diya_shell_add_window(DiyaShell * shell, DiyaForeignWindow * win); gboolean diya_shell_add_window(DiyaShell * shell, DiyaForeignWindow * win);
gboolean diya_shell_remove_window(DiyaShell * shell, DiyaForeignWindow * win); gboolean diya_shell_remove_window(DiyaShell * shell, DiyaForeignWindow * win);