Merge pull request #112 from joshuamegnauth54/issue-109-restore-user-session-choice

Default to user's previously selected session
This commit is contained in:
Jeremy Soller 2024-09-06 10:50:37 -06:00 committed by GitHub
commit c8477a97a9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 213 additions and 12 deletions

10
Cargo.lock generated
View file

@ -994,6 +994,7 @@ dependencies = [
"cosmic-comp-config",
"cosmic-config",
"cosmic-dbus-networkmanager",
"cosmic-greeter-config",
"cosmic-greeter-daemon",
"env_logger",
"freedesktop_entry_parser",
@ -1018,6 +1019,15 @@ dependencies = [
"zbus 4.4.0",
]
[[package]]
name = "cosmic-greeter-config"
version = "0.1.0"
dependencies = [
"cosmic-config",
"log",
"serde",
]
[[package]]
name = "cosmic-greeter-daemon"
version = "0.1.0"

View file

@ -8,6 +8,7 @@ chrono = { version = "0.4", features = ["unstable-locales"] }
cosmic-bg-config.workspace = true
cosmic-comp-config.workspace = true
cosmic-config = { workspace = true, features = ["calloop", "macro"] }
cosmic-greeter-config.workspace = true
cosmic-greeter-daemon = { path = "daemon" }
env_logger.workspace = true
freedesktop_entry_parser = "1.3.0"
@ -62,7 +63,7 @@ opt-level = 2
opt-level = 2
[workspace]
members = ["daemon"]
members = ["cosmic-greeter-config", "daemon"]
resolver = "2"
[workspace.package]
@ -87,6 +88,9 @@ default-features = false
git = "https://github.com/pop-os/cosmic-comp"
default-features = false
[workspace.dependencies.cosmic-greeter-config]
path = "cosmic-greeter-config"
[workspace.dependencies.cosmic-config]
git = "https://github.com/pop-os/libcosmic"
default-features = false

View file

@ -0,0 +1,12 @@
[package]
name = "cosmic-greeter-config"
version = "0.1.0"
edition = "2021"
description = "Configuration for COSMIC Greeter"
repository = "https://github.com/pop-os/cosmic-greeter"
license = "GPL-3.0-only"
[dependencies]
cosmic-config.workspace = true
log.workspace = true
serde.workspace = true

View file

@ -0,0 +1,48 @@
// Copyright 2024 System76 <info@system76.com>
// SPDX-License-Identifier: GPL-3.0-only
pub mod user;
use std::{collections::HashMap, num::NonZeroU32};
use cosmic_config::{cosmic_config_derive::CosmicConfigEntry, CosmicConfigEntry};
use serde::{Deserialize, Serialize};
pub const APP_ID: &str = "com.system76.CosmicGreeter";
pub const CONFIG_VERSION: u64 = 1;
#[derive(Debug, Clone, Default, PartialEq, CosmicConfigEntry, Deserialize, Serialize)]
#[version = 1]
#[id = "com.system76.CosmicGreeter"]
pub struct Config {
#[serde(skip_serializing_if = "HashMap::is_empty")]
pub users: HashMap<NonZeroU32, user::UserState>,
}
impl Config {
pub fn load() -> (Self, Option<cosmic_config::Config>) {
crate::load()
}
}
pub(crate) fn load<C>() -> (C, Option<cosmic_config::Config>)
where
C: Default + CosmicConfigEntry,
{
match cosmic_config::Config::new(APP_ID, CONFIG_VERSION) {
Ok(handler) => {
let config = C::get_entry(&handler)
.inspect_err(|(errors, _)| {
for err in errors {
log::error!("{err}")
}
})
.unwrap_or_else(|(_, config)| config);
(config, Some(handler))
}
Err(e) => {
log::error!("Failed to get settings for `{APP_ID}` (v {CONFIG_VERSION}): {e:?}");
(C::default(), None)
}
}
}

View file

@ -0,0 +1,20 @@
// Copyright 2024 System76 <info@system76.com>
// SPDX-License-Identifier: GPL-3.0-only
use std::num::NonZeroU32;
use serde::{Deserialize, Serialize};
/// Per user state for Greeter.
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
pub struct UserState {
#[serde(skip_serializing_if = "invalid_uid")]
pub uid: NonZeroU32,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_session: Option<String>,
}
// Only serialize users not system accounts
const fn invalid_uid(uid: &NonZeroU32) -> bool {
uid.get() < 1000
}

View file

@ -27,12 +27,14 @@ use cosmic::{
style, theme, widget, Element,
};
use cosmic_comp_config::CosmicCompConfig;
use cosmic_greeter_config::Config as CosmicGreeterConfig;
use cosmic_greeter_daemon::{UserData, WallpaperData};
use greetd_ipc::Request;
use std::{
collections::HashMap,
collections::{hash_map, HashMap},
error::Error,
fs, io,
num::NonZeroU32,
path::{Path, PathBuf},
process,
sync::Arc,
@ -130,6 +132,14 @@ pub fn main() -> Result<(), Box<dyn Error>> {
// Sort user data by uid
user_datas.sort_by(|a, b| a.uid.cmp(&b.uid));
let (mut greeter_config, greeter_config_handler) = CosmicGreeterConfig::load();
// Filter out users that were removed from the system since the last time we loaded config
greeter_config.users.retain(|uid, _| {
user_datas
.binary_search_by(|probe| probe.uid.cmp(&uid.get()))
.is_ok()
});
enum SessionType {
X11,
Wayland,
@ -294,6 +304,8 @@ pub fn main() -> Result<(), Box<dyn Error>> {
sessions,
layouts_opt,
comp_config_handler,
greeter_config,
greeter_config_handler,
fallback_background,
};
@ -310,6 +322,8 @@ pub struct Flags {
sessions: HashMap<String, (Vec<String>, Vec<String>)>,
layouts_opt: Option<Arc<xkb_data::KeyboardLayouts>>,
comp_config_handler: Option<cosmic_config::Config>,
greeter_config: CosmicGreeterConfig,
greeter_config_handler: Option<cosmic_config::Config>,
fallback_background: widget::image::Handle,
}
@ -362,6 +376,7 @@ pub enum Dropdown {
#[derive(Clone, Debug)]
pub enum Message {
Auth(Option<String>),
ConfigUpdateUser,
DialogCancel,
DialogConfirm,
DropdownToggle(Dropdown),
@ -590,12 +605,6 @@ impl cosmic::Application for App {
core.window.show_minimize = false;
core.window.use_template = false;
let mut session_names: Vec<_> = flags.sessions.keys().map(|x| x.to_string()).collect();
session_names.sort();
//TODO: use last selected session
let selected_session = session_names.first().cloned().unwrap_or(String::new());
//TODO: use full_name_opt
let mut usernames: Vec<_> = flags
.user_datas
@ -609,11 +618,25 @@ impl cosmic::Application for App {
usernames.sort_by(|a, b| a.1.cmp(&b.1));
//TODO: use last selected user
let selected_username = flags
let (selected_username, uid) = flags
.user_datas
.first()
.map(|x| x.name.clone())
.unwrap_or(String::new());
.map(|x| (x.name.clone(), NonZeroU32::new(x.uid)))
.unwrap_or((String::new(), None));
let mut session_names: Vec<_> = flags.sessions.keys().map(|x| x.to_string()).collect();
session_names.sort();
let selected_session = uid
.and_then(|uid| {
flags
.greeter_config
.users
.get(&uid)
.and_then(|user| user.last_session.clone())
})
.or_else(|| session_names.first().cloned())
.unwrap_or_default();
let app = App {
core,
@ -779,6 +802,23 @@ impl cosmic::Application for App {
if username != self.selected_username {
self.selected_username = username.clone();
self.surface_images.clear();
if let Some(session) = self
.flags
.user_datas
.iter()
.find(|user| user.name == username)
.and_then(|UserData { uid, .. }| {
NonZeroU32::new(*uid).and_then(|uid| {
self.flags
.greeter_config
.users
.get(&uid)
.and_then(|conf| conf.last_session.as_deref())
})
})
{
session.clone_into(&mut self.selected_session);
};
match &self.socket_state {
SocketState::Open => {
self.prompt_opt = None;
@ -788,6 +828,70 @@ impl cosmic::Application for App {
}
}
}
Message::ConfigUpdateUser => {
let Some(user_entry) = self
.flags
.user_datas
.iter()
.find(|user| user.name == self.selected_username)
.and_then(|UserData { uid, .. }| {
NonZeroU32::new(*uid).map(|uid| self.flags.greeter_config.users.entry(uid))
})
else {
log::error!("Couldn't find user: {:?}", self.selected_username);
return Command::none();
};
let Some(handler) = self.flags.greeter_config_handler.as_mut() else {
log::error!(
"Failed to update config for {} (UID: {}): no config handler",
self.selected_username,
user_entry.key()
);
return Command::none();
};
let uid = *user_entry.key();
match user_entry {
hash_map::Entry::Vacant(entry) => {
let last_session = Some(self.selected_session.clone());
entry.insert(cosmic_greeter_config::user::UserState { uid, last_session });
}
hash_map::Entry::Occupied(mut entry) => {
let last_session = entry.get_mut().last_session.as_mut();
if last_session
.as_ref()
.is_some_and(|session| session.as_str() == self.selected_session)
{
return Command::none();
}
if let Some(session) = last_session {
self.selected_session.clone_into(session);
} else {
let last_session = Some(self.selected_session.clone());
entry.insert(cosmic_greeter_config::user::UserState {
uid,
last_session,
});
}
}
}
// xxx Not sure why this doesn't work unless the handler is used directly
// if let Err(err) = self
// .flags
// .greeter_config
// .set_users(&handler, self.flags.greeter_config.users.clone())
if let Err(err) = handler.set("users", &self.flags.greeter_config.users) {
log::error!(
"Failed to set {} as last selected session for {} (UID: {}): {:?}",
self.selected_session,
self.selected_username,
uid,
err
);
}
}
Message::Auth(response) => {
self.prompt_opt = None;
self.error_opt = None;
@ -798,7 +902,10 @@ impl cosmic::Application for App {
self.error_opt = None;
match self.flags.sessions.get(&self.selected_session).cloned() {
Some((cmd, env)) => {
return self.send_request(Request::StartSession { cmd, env });
return Command::batch([
self.update(Message::ConfigUpdateUser),
self.send_request(Request::StartSession { cmd, env }),
]);
}
None => todo!("session {:?} not found", self.selected_session),
}