Files
diya-shell/src/login-shell.c

316 lines
11 KiB
C

#include <gtk4-layer-shell.h>
#include <gtk4-session-lock.h>
#include <gio/gio.h>
#include "login-shell.h"
#include "widgets/virtual-keyboard-widgets.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"
#define NAMESPACE "diya_login_shell"
struct _DiyaLoginShell
{
DiyaShell parent_object;
GtkSessionLockInstance *lock;
GtkWidget *username;
GtkWidget *password;
GtkWidget *status;
guint bus_watch_id;
GtkWidget *vkb_widget;
GtkWidget *revealer;
GList *windows;
};
G_DEFINE_FINAL_TYPE(DiyaLoginShell, diya_login_shell, DIYA_TYPE_SHELL);
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->windows)
{
// destroyed all windows and free list
GList *it = NULL;
for (it = self->windows; it; it = it->next)
{
if (it->data)
{
gtk_window_destroy(GTK_WINDOW(it->data));
}
}
g_list_free(self->windows);
}
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)
{
g_debug("diya_login_shell_init");
self->lock = gtk_session_lock_instance_new();
self->username = NULL;
self->password = NULL;
self->status = NULL;
self->windows = NULL;
self->bus_watch_id = 0;
}
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_object_unref(self);
}
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));
}
static void add_new_monitor(DiyaLoginShell *self, GdkMonitor *monitor, gint position)
{
GtkWindow *window = GTK_WINDOW(gtk_application_window_new(diya_shell_get_application(DIYA_SHELL(self))));
gtk_session_lock_instance_assign_window_to_monitor(self->lock, window, monitor);
gtk_widget_set_name(GTK_WIDGET(window), NAMESPACE);
GtkWidget *grid = gtk_grid_new();
gtk_window_set_child(window, grid);
gtk_grid_set_row_homogeneous(GTK_GRID(grid), false);
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);
gtk_widget_set_hexpand(box, true);
gtk_widget_set_vexpand(box, true);
GtkWidget *label;
label = gtk_label_new("Please login");
gtk_box_append(GTK_BOX(box), label);
gtk_widget_add_css_class(label, "diya-login-header");
GtkWidget *child_grid = gtk_grid_new();
gtk_grid_set_row_spacing(GTK_GRID(child_grid),10);
gtk_grid_set_column_spacing(GTK_GRID(child_grid),10);
label = gtk_label_new("Username");
gtk_grid_attach(GTK_GRID(child_grid), label, 0, 0, 1, 1);
self->username = gtk_entry_new();
gtk_grid_attach(GTK_GRID(child_grid), self->username, 1, 0, 1, 1);
label = gtk_label_new("Password");
gtk_grid_attach(GTK_GRID(child_grid), label, 0, 1, 1, 1);
self->password = gtk_password_entry_new();
gtk_grid_attach(GTK_GRID(child_grid), self->password, 1, 1, 1, 1);
gtk_box_append(GTK_BOX(box), child_grid);
g_signal_connect(self->username, "activate", G_CALLBACK(do_login), self);
g_signal_connect(self->password, "activate", G_CALLBACK(do_login), self);
child_grid = gtk_grid_new();
gtk_grid_set_column_spacing(GTK_GRID(child_grid),2);
GtkWidget *button = gtk_button_new_with_label("Login");
g_signal_connect(button, "clicked", G_CALLBACK(do_login), self);
gtk_widget_set_can_focus(GTK_WIDGET(button), false);
gtk_grid_attach(GTK_GRID(child_grid), button, 0, 0, 1, 1);
// Not displayed, but allows testing that creating popups doesn't crash GTK
gtk_widget_set_tooltip_text(button, "Login");
gtk_widget_set_hexpand(button, true);
button = gtk_toggle_button_new_with_label("");
gtk_widget_add_css_class(button, "diya-btn-show-vkb");
gtk_grid_attach(GTK_GRID(child_grid), button, 1, 0, 1, 1);
gtk_box_append(GTK_BOX(box), child_grid);
gtk_widget_set_tooltip_text(button, "Show virtual keyboard");
gtk_widget_set_can_focus(GTK_WIDGET(button), false);
self->status = gtk_label_new(NULL);
gtk_box_append(GTK_BOX(box), self->status);
gtk_widget_add_css_class(self->status, "diya-login-status");
gtk_grid_attach(GTK_GRID(grid), box, 0, 0, 1, 1);
box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
gtk_widget_set_valign(box, GTK_ALIGN_CENTER);
gtk_widget_set_halign(box, GTK_ALIGN_CENTER);
gtk_box_set_spacing(GTK_BOX(box), 10);
self->revealer = gtk_revealer_new();
self->vkb_widget = diya_virtual_keyboard_widget_new(diya_shell_get_virtual_keyboard(DIYA_SHELL(self), "fr"));
gtk_revealer_set_child(GTK_REVEALER(self->revealer), self->vkb_widget);
gtk_revealer_set_reveal_child(GTK_REVEALER(self->revealer), false);
gtk_revealer_set_transition_type(GTK_REVEALER(self->revealer),GTK_REVEALER_TRANSITION_TYPE_SLIDE_UP);
gtk_box_append(GTK_BOX(box), self->revealer);
g_object_bind_property(button, "active", self->revealer, "reveal-child",G_BINDING_BIDIRECTIONAL);
//gtk_widget_set_visible(self->vkb_widget, false);
gtk_grid_attach(GTK_GRID(grid), box, 0, 1, 1, 1);
gtk_widget_set_hexpand(box, true);
gtk_widget_set_vexpand(box, false);
gtk_window_present(window);
self->windows = g_list_insert(self->windows, window, position);
}
static void on_monitor_changed(GListModel *monitor_lists, guint position, guint removed, guint added, gpointer object)
{
g_debug("Monitor list changed at %d: added %d, removed %d", position, added, removed);
DiyaLoginShell *self = DIYA_LOGIN_SHELL(object);
if (removed > 0)
{
for (guint i = 0; i < removed; i++)
{
GtkWindow *win = GTK_WINDOW(g_list_nth(self->windows, position));
if (win)
{
gtk_window_destroy(win);
self->windows = g_list_remove(self->windows, win);
}
}
}
if (added > 0)
{
for (guint i = 0; i < added; i++)
{
GdkMonitor *monitor = g_list_model_get_item(monitor_lists, position + i);
add_new_monitor(self, monitor, position + i);
}
}
}
static void diya_login_shell_startup(DiyaShell *shell)
{
DiyaLoginShell *self = DIYA_LOGIN_SHELL(shell);
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;
}
GListModel *list = gdk_display_get_monitors(gdk_display_get_default());
for (guint i = 0; i < g_list_model_get_n_items(list); i++)
{
GdkMonitor *monitor = g_list_model_get_item(list, i);
add_new_monitor(self, monitor, -1);
}
}
static void diya_login_shell_class_init(DiyaLoginShellClass *class)
{
GObjectClass *gobject_class = G_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;
DiyaShellClass *base_shell_class = DIYA_SHELL_CLASS(class);
base_shell_class->monitor_changed_handle = on_monitor_changed;
base_shell_class->startup_handle = diya_login_shell_startup;
// base_shell_class->foreign_register = diya_session_shell_foreign_toplevel_register;
}