316 lines
11 KiB
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;
|
|
} |