diff --git a/README.md b/README.md index bc182e0..4953506 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,8 @@ the Roboto font. All screenshots are provided under the - Allows manual entry of username and session command - Remembers the last authenticated user - Automatically selects the last used session per user +- Can also skip selecting the user/session and choose the last user and their + last used session. - Allows setting environment variables for created sessions - Supports customizing: - Background image diff --git a/regreet.sample.toml b/regreet.sample.toml index 205af59..bb77777 100644 --- a/regreet.sample.toml +++ b/regreet.sample.toml @@ -2,6 +2,9 @@ # # SPDX-License-Identifier: GPL-3.0-or-later +# Whether to skip asking for username and session, and use the last used ones. +skip_selection = false + [background] # Path to the background image/video path = "/usr/share/backgrounds/greeter.jpg" diff --git a/src/config.rs b/src/config.rs index d07a52b..b60dcc8 100644 --- a/src/config.rs +++ b/src/config.rs @@ -130,6 +130,9 @@ fn default_greeting_msg() -> String { /// The configuration struct #[derive(Default, Deserialize)] pub struct Config { + #[serde(default)] + skip_selection: bool, + #[serde(default)] appearance: AppearanceSettings, @@ -184,4 +187,8 @@ impl Config { pub fn get_default_message(&self) -> &str { &self.appearance.greeting_msg } + + pub fn skip_selection(&self) -> bool { + self.skip_selection + } } diff --git a/src/gui/component.rs b/src/gui/component.rs index 2a05fa7..26164d4 100644 --- a/src/gui/component.rs +++ b/src/gui/component.rs @@ -9,7 +9,7 @@ use std::path::PathBuf; use relm4::{ AsyncComponentSender, component::{AsyncComponent, AsyncComponentParts}, - gtk::prelude::*, + gtk::{glib::GString, prelude::*}, prelude::*, }; use tracing::{debug, info, warn}; @@ -420,6 +420,26 @@ impl AsyncComponent for Greeter { // Set the default behaviour of pressing the Return key to act like the login button. root.set_default_widget(Some(&widgets.ui.login_button)); + if let Some(user) = model.cache.get_last_user() { + if model.config.skip_selection() && model.cache.has_last_session(user) { + debug!("Skipping user & session selection and using those from the cache"); + // Queue up a login event after the Self::Input::UserChanged that will be fired by + // setup_users_sessions (as sender.input queues). This way we avoid any race + // conditions. Use cached values directly instead of extracting from widgets. + let user = user.to_string(); + let last_session = model.cache.get_last_session(&user).unwrap().to_string(); + sender.input(Self::Input::Login { + input: String::new(), + info: UserSessInfo { + user_id: Some(user.into()), + user_text: GString::new(), + sess_id: Some(last_session.into()), + sess_text: GString::new(), + }, + }); + } + } + AsyncComponentParts { model, widgets } } diff --git a/src/gui/model.rs b/src/gui/model.rs index 9110101..c43d412 100644 --- a/src/gui/model.rs +++ b/src/gui/model.rs @@ -98,8 +98,10 @@ pub struct Greeter { pub(super) updates: Updates, /// Is it run as demo pub(super) demo: bool, - + /// The clock widget pub(super) clock: Controller, + /// Has the first click of the login button been handled previously? + first_login_handled: bool, } impl Greeter { @@ -141,6 +143,7 @@ impl Greeter { updates, demo, clock, + first_login_handled: false, } } @@ -300,17 +303,34 @@ impl Greeter { ) { match response { Response::Success => { - // Authentication was successful and the session may be started. - // This may happen on the first request, in which case logging in - // as the given user requires no authentication. - info!("Successfully logged in; starting session"); - self.start_session(sender).await; + if self.config.skip_selection() && !self.first_login_handled { + // This happens when the last logged-in user has no password or any other auth + // set. Since the login button is auto-clicked, it would've otherwise logged + // them in without any possibility of changing the user or session! + warn!("Preventing auto-login for user without auth: cancelling session"); + self.cancel_click_handler().await + } else { + // Authentication was successful and the session may be started. + // This may happen on the first request, in which case logging in + // as the given user requires no authentication. + info!("Successfully logged in; starting session"); + self.start_session(sender).await + } return; } Response::AuthMessage { auth_message, auth_message_type, } => { + let auto_select_msg = if self.config.skip_selection() && !self.first_login_handled { + self.sess_info + .as_ref() + .and_then(|s| s.sess_id.as_ref()) + .map(|s| format!("Auto-selecting session \"{s}\"")) + } else { + None + }; + match auth_message_type { AuthMessageType::Secret => { // Greetd has requested input that should be hidden @@ -320,6 +340,9 @@ impl Greeter { self.updates.set_input(String::new()); self.updates .set_input_prompt(auth_message.trim_end().to_string()); + if let Some(msg) = auto_select_msg { + self.display_info(sender, &msg, "Skipped user/session selection"); + } return; } AuthMessageType::Visible => { @@ -329,6 +352,9 @@ impl Greeter { self.updates.set_input(String::new()); self.updates .set_input_prompt(auth_message.trim_end().to_string()); + if let Some(msg) = auto_select_msg { + self.display_info(sender, &msg, "Skipped user/session selection"); + } return; } AuthMessageType::Info => { @@ -435,6 +461,7 @@ impl Greeter { self.create_session(sender).await; } }; + self.first_login_handled = true; } /// Send the entered input for logging in.