Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
- name: Install Dependencies
run: |
apt update
apt install -y desktop-file-utils libgdk-pixbuf-2.0-dev libgnome-desktop-3-dev libgranite-dev libgtk-3-dev libhandy-1-dev libjson-glib-dev liblightdm-gobject-1-dev ${{ matrix.mutter_pkg }} libsoup-3.0-dev libx11-dev ${{ matrix.lightdm_vala }} meson valac
apt install -y desktop-file-utils libaccountsservice-dev libgdk-pixbuf-2.0-dev libgnome-desktop-3-dev libgranite-dev libgtk-3-dev libhandy-1-dev libjson-glib-dev liblightdm-gobject-1-dev ${{ matrix.mutter_pkg }} libsoup-3.0-dev libx11-dev ${{ matrix.lightdm_vala }} meson valac
- name: Build
env:
DESTDIR: out
Expand All @@ -50,7 +50,7 @@ jobs:
- uses: actions/checkout@v7
- name: Install Dependencies
run: |
dnf install -y desktop-file-utils gdk-pixbuf2-devel gnome-desktop3-devel granite-devel gtk3-devel libhandy-devel lightdm-gobject-devel mutter-devel libX11-devel meson valac
dnf install -y accountsservice-devel desktop-file-utils gdk-pixbuf2-devel gnome-desktop3-devel granite-devel gtk3-devel libhandy-devel lightdm-gobject-devel mutter-devel libX11-devel meson valac
- name: Build
env:
DESTDIR: out
Expand All @@ -69,7 +69,7 @@ jobs:
run: |
zypper addrepo https://download.opensuse.org/repositories/X11:Pantheon/16.0/X11:Pantheon.repo
zypper --gpg-auto-import-keys refresh
zypper --non-interactive install tar git desktop-file-utils gdk-pixbuf-devel libgnome-desktop-3-devel granite6-devel gtk3-devel libhandy-devel lightdm-gobject-devel mutter-devel meson vala gcc
zypper --non-interactive install accountsservice-devel accountsservice-vala tar git desktop-file-utils gdk-pixbuf-devel libgnome-desktop-3-devel granite6-devel gtk3-devel libhandy-devel lightdm-gobject-devel mutter-devel meson vala gcc
- uses: actions/checkout@v7
- name: Build
env:
Expand Down
5 changes: 4 additions & 1 deletion src/Application.vala
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2018-2024 elementary, Inc. (https://elementary.io)
* Copyright 2018-2026 elementary, Inc. (https://elementary.io)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
Expand Down Expand Up @@ -111,6 +111,9 @@ public class Greeter.Application : Gtk.Application {
}

public static int main (string[] args) {
// Connect to UserManager as early as possible
Act.UserManager.get_default ();

return new Greeter.Application ().run (args);
}
}
9 changes: 6 additions & 3 deletions src/Cards/BaseCard.vala
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
/*
* SPDX-License-Identifier: GPL-2.0-or-later
* SPDX-FileCopyrightText: 2018-2025 elementary, Inc. (https://elementary.io)
* SPDX-FileCopyrightText: 2018-2026 elementary, Inc. (https://elementary.io)
*
* Authors: Corentin Noël <corentin@elementary.io>
*/

public abstract class Greeter.BaseCard : Gtk.Bin {
public signal void do_connect (string? credential = null);
public signal void start_authentication (string username);
public signal void provide_credential (string credential);
public signal void go_left ();
public signal void go_right ();

protected const int ERROR_SHAKE_DURATION = 450;

public bool connecting { get; set; default = false; }
public bool need_password { get; set; default = false; }
public bool use_fingerprint { get; set; default = false; }

construct {
Expand All @@ -34,5 +34,8 @@ public abstract class Greeter.BaseCard : Gtk.Bin {
return base.focus (direction);
}

public virtual void on_selected () {}
public virtual void on_deselected () {}

public abstract void wrong_credentials ();
}
8 changes: 3 additions & 5 deletions src/Cards/ManualCard.vala
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
/*
* Copyright 2018-2023 elementary, Inc. (https://elementary.io)
* Copyright 2018-2026 elementary, Inc. (https://elementary.io)
* SPDX-License-Identifier: GPL-2.0-or-later
*/

public class Greeter.ManualCard : Greeter.BaseCard {
public signal void do_connect_username (string username);

private Greeter.PasswordEntry password_entry;
private Gtk.Entry username_entry;
private Gtk.Box main_box;
Expand Down Expand Up @@ -70,7 +68,7 @@ public class Greeter.ManualCard : Greeter.BaseCard {

username_entry.focus_out_event.connect (() => {
if (username_entry.text != "") {
do_connect_username (username_entry.text);
start_authentication (username_entry.text);
}
});

Expand All @@ -83,7 +81,7 @@ public class Greeter.ManualCard : Greeter.BaseCard {
}

connecting = true;
do_connect (password_entry.text);
provide_credential (password_entry.text);
}

public override void wrong_credentials () {
Expand Down
128 changes: 71 additions & 57 deletions src/Cards/UserCard.vala
Original file line number Diff line number Diff line change
@@ -1,43 +1,48 @@
/*
* Copyright 2018-2025 elementary, Inc. (https://elementary.io)
* Copyright 2018-2026 elementary, Inc. (https://elementary.io)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Authors: Corentin Noël <corentin@elementary.io>
*/

public class Greeter.UserCard : Greeter.BaseCard {
public LightDM.User lightdm_user { get; construct; }
public bool show_input { get; set; default = false; }
private const int PASSWORD_MODE_REGULAR = 0;
private const int PASSWORD_MODE_SET_AT_NEXT_LOGIN = 1;
private const int PASSWORD_MODE_NO_PASSWORD = 2;

/**
* We use Act.User instead of LightDM.User because lightdm is unmaintained
* and lacks some fields from Act.User such as `password_mode`.
*/
public Act.User user { get; construct; }
public bool is_24h { get; set; default = true; }
// TODO: In Gtk4 remove this gesture and move it to MainWindow
public Gtk.GestureMultiPress click_gesture { get; private set; }

private Pantheon.AccountsService greeter_act;
private Pantheon.SettingsDaemon.AccountsService settings_act;

private Gtk.Label username_label;
private Gtk.Revealer form_revealer;
private Gtk.Stack login_stack;
private Greeter.PasswordEntry password_entry;
private Gtk.Box main_box;

private SelectionCheck logged_in;

public UserCard (LightDM.User lightdm_user) {
Object (lightdm_user: lightdm_user);
public UserCard (Act.User user) requires (user.is_loaded) {
Object (user: user);
}

construct {
need_password = true;

username_label = new Gtk.Label (lightdm_user.display_name) {
var username_label = new Gtk.Label (user.real_name) {
hexpand = true,
margin_top = 24,
margin_bottom = 12,
margin_start = 24,
margin_end = 24,
};
username_label.get_style_context ().add_class (Granite.STYLE_CLASS_H2_LABEL);
user.bind_property ("locked", username_label, "sensitive", SYNC_CREATE);

password_entry = new Greeter.PasswordEntry ();
bind_property ("connecting", password_entry, "sensitive", INVERT_BOOLEAN);
Expand All @@ -59,7 +64,9 @@ public class Greeter.UserCard : Greeter.BaseCard {
password_grid.attach (password_session_button, 2, 0);
password_grid.attach (new Greeter.CapsLockRevealer (), 0, 1, 3);

var login_button = new Gtk.Button.with_label (_("Log In"));
var login_button = new Gtk.Button.with_label (_("Log In")) {
hexpand = true
};
login_button.get_style_context ().add_class (Gtk.STYLE_CLASS_SUGGESTED_ACTION);
bind_property ("connecting", login_button, "sensitive", INVERT_BOOLEAN);

Expand All @@ -86,16 +93,15 @@ public class Greeter.UserCard : Greeter.BaseCard {
margin_end = 24
};
login_stack.add_named (password_grid, "password");
login_stack.add_named (login_button, "button");
login_stack.add_named (login_box, "button");
login_stack.add_named (disabled_box, "disabled");

form_revealer = new Gtk.Revealer () {
margin_bottom = 12,
reveal_child = true,
reveal_child = false,
transition_type = SLIDE_DOWN,
child = login_stack
};
bind_property ("show-input", form_revealer, "reveal-child", SYNC_CREATE);

main_box = new Gtk.Box (Gtk.Orientation.VERTICAL, 0) {
margin_bottom = 48
Expand All @@ -105,15 +111,14 @@ public class Greeter.UserCard : Greeter.BaseCard {
main_box.pack_end (username_label);
main_box.get_style_context ().add_class (Granite.STYLE_CLASS_CARD);
main_box.get_style_context ().add_class (Granite.STYLE_CLASS_ROUNDED);
main_box.get_style_context ().add_class ("collapsed");

update_collapsed_class ();

var avatar = new Hdy.Avatar (64, lightdm_user.display_name, true) {
var avatar = new Hdy.Avatar (64, user.real_name, true) {
margin_top = 6,
margin_bottom = 6,
margin_start = 6,
margin_end = 6,
loadable_icon = new FileIcon (File.new_for_path (lightdm_user.image))
loadable_icon = new FileIcon (File.new_for_path (user.icon_file))
};

var avatar_overlay = new Gtk.Overlay () {
Expand All @@ -128,7 +133,7 @@ public class Greeter.UserCard : Greeter.BaseCard {
valign = END
};

if (lightdm_user.logged_in) {
if (user.is_logged_in ()) {
avatar_overlay.add_overlay (logged_in);

password_session_button.sensitive = false;
Expand All @@ -149,43 +154,48 @@ public class Greeter.UserCard : Greeter.BaseCard {

child = card_overlay;

connect_to_dbus_interfaces ();
lightdm_user.changed.connect (update_is_locked_ui);
notify["need-password"].connect (update_is_locked_ui);
show_all ();

click_gesture = new Gtk.GestureMultiPress (this);
user.notify["locked"].connect (update_is_locked_ui);
user.notify["password-mode"].connect (update_is_locked_ui);
update_is_locked_ui ();

notify["show-input"].connect (update_collapsed_class);
click_gesture = new Gtk.GestureMultiPress (this);

password_entry.activate.connect (on_login);
login_button.clicked.connect (on_login);

grab_focus.connect (password_entry.grab_focus_without_selecting);

connect_to_dbus_interfaces ();
}

private void set_check_style () {
// Override check's accent_color so that it *always* uses user's preferred color
logged_in.get_style_context ().add_class (accent_to_string (settings_act.accent_color));
}

private string generate_background_image_path () {
if (settings_act.picture_options == 0) {
return "";
}

var path = Path.build_filename ("/", "var", "lib", "lightdm-data", user.user_name, "wallpaper");
if (FileUtils.test (path, EXISTS) && FileUtils.test (path, IS_REGULAR)) {
return path;
}

return "/usr/share/backgrounds/elementaryos-default";
}

private void set_background_image () {
Greeter.BackgroundImage background_image;

var background_path = lightdm_user.background;
var background_exists = (
background_path != null &&
FileUtils.test (background_path, EXISTS) &&
FileUtils.test (background_path, IS_REGULAR)
);

if (!background_exists) {
background_path = Path.build_filename ("/", "var", "lib", "lightdm-data", lightdm_user.name, "wallpaper");
background_exists = FileUtils.test (background_path, EXISTS) && FileUtils.test (background_path, IS_REGULAR);
}
var background_path = generate_background_image_path ();

if (settings_act.picture_options != 0 && background_exists) {
if (settings_act.picture_options != 0) {
background_image = new Greeter.BackgroundImage.from_path (background_path);
} else if (settings_act.picture_options == 0 && settings_act.primary_color != null) {
} else if (settings_act.primary_color != null) {
background_image = new Greeter.BackgroundImage.from_color (settings_act.primary_color);
} else {
background_image = new Greeter.BackgroundImage.from_path (null);
Expand Down Expand Up @@ -223,7 +233,7 @@ public class Greeter.UserCard : Greeter.BaseCard {
}

private void connect_to_dbus_interfaces () {
var account_path = "/org/freedesktop/Accounts/User%d".printf ((int )lightdm_user.uid);
var account_path = "/org/freedesktop/Accounts/User%d".printf (user.uid);
try {
greeter_act = Bus.get_proxy_sync (
SYSTEM,
Expand All @@ -246,18 +256,12 @@ public class Greeter.UserCard : Greeter.BaseCard {

set_background_image ();
set_check_style ();
update_is_locked_ui ();
}

private void update_is_locked_ui () {
// lightdm_user.is_locked prints warnings so let's use a getter method here
var is_locked = lightdm_user.get_is_locked ();

username_label.sensitive = !is_locked;

if (is_locked) {
if (user.locked) {
login_stack.visible_child_name = "disabled";
} else if (need_password) {
} else if (user.password_mode != PASSWORD_MODE_NO_PASSWORD) {
login_stack.visible_child_name = "password";
} else {
login_stack.visible_child_name = "button";
Expand All @@ -270,22 +274,15 @@ public class Greeter.UserCard : Greeter.BaseCard {
}

connecting = true;
if (need_password) {
do_connect (password_entry.text);
} else {
do_connect ();
}
}

private void update_collapsed_class () {
if (show_input) {
main_box.get_style_context ().remove_class ("collapsed");
if (user.password_mode != PASSWORD_MODE_NO_PASSWORD) {
provide_credential (password_entry.text);
} else {
main_box.get_style_context ().add_class ("collapsed");
start_authentication (user.user_name);
}
}

public void set_settings () {
private void set_settings () {
set_keyboard_layouts ();
set_mouse_touchpad_settings ();
set_interface_settings ();
Expand Down Expand Up @@ -378,7 +375,7 @@ public class Greeter.UserCard : Greeter.BaseCard {

var background_settings = new GLib.Settings ("org.gnome.desktop.background");
background_settings.set_enum ("picture-options", settings_act.picture_options);
set_or_reset_settings_key (background_settings, "picture-uri", lightdm_user.background);
set_or_reset_settings_key (background_settings, "picture-uri", generate_background_image_path ());
set_or_reset_settings_key (background_settings, "primary-color", settings_act.primary_color);
}

Expand Down Expand Up @@ -426,6 +423,23 @@ public class Greeter.UserCard : Greeter.BaseCard {
SettingsPortal.get_default ().prefers_color_scheme = greeter_act.prefers_color_scheme;
}

public override void on_selected () {
form_revealer.reveal_child = true;
main_box.get_style_context ().remove_class ("collapsed");

set_settings ();
grab_focus ();

if (user.password_mode != PASSWORD_MODE_NO_PASSWORD) {
start_authentication (user.user_name);
}
}

public override void on_deselected () {
form_revealer.reveal_child = false;
main_box.get_style_context ().add_class ("collapsed");
}

public override void wrong_credentials () {
password_entry.get_style_context ().add_class (Gtk.STYLE_CLASS_ERROR);
main_box.get_style_context ().add_class ("shake");
Expand Down
Loading