Merge pull request #112 from joshuamegnauth54/issue-109-restore-user-session-choice
Default to user's previously selected session
This commit is contained in:
commit
c8477a97a9
6 changed files with 213 additions and 12 deletions
10
Cargo.lock
generated
10
Cargo.lock
generated
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
12
cosmic-greeter-config/Cargo.toml
Normal file
12
cosmic-greeter-config/Cargo.toml
Normal 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
|
||||
48
cosmic-greeter-config/src/lib.rs
Normal file
48
cosmic-greeter-config/src/lib.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
20
cosmic-greeter-config/src/user.rs
Normal file
20
cosmic-greeter-config/src/user.rs
Normal 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
|
||||
}
|
||||
129
src/greeter.rs
129
src/greeter.rs
|
|
@ -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),
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue