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-comp-config",
|
||||||
"cosmic-config",
|
"cosmic-config",
|
||||||
"cosmic-dbus-networkmanager",
|
"cosmic-dbus-networkmanager",
|
||||||
|
"cosmic-greeter-config",
|
||||||
"cosmic-greeter-daemon",
|
"cosmic-greeter-daemon",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"freedesktop_entry_parser",
|
"freedesktop_entry_parser",
|
||||||
|
|
@ -1018,6 +1019,15 @@ dependencies = [
|
||||||
"zbus 4.4.0",
|
"zbus 4.4.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cosmic-greeter-config"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"cosmic-config",
|
||||||
|
"log",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cosmic-greeter-daemon"
|
name = "cosmic-greeter-daemon"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ chrono = { version = "0.4", features = ["unstable-locales"] }
|
||||||
cosmic-bg-config.workspace = true
|
cosmic-bg-config.workspace = true
|
||||||
cosmic-comp-config.workspace = true
|
cosmic-comp-config.workspace = true
|
||||||
cosmic-config = { workspace = true, features = ["calloop", "macro"] }
|
cosmic-config = { workspace = true, features = ["calloop", "macro"] }
|
||||||
|
cosmic-greeter-config.workspace = true
|
||||||
cosmic-greeter-daemon = { path = "daemon" }
|
cosmic-greeter-daemon = { path = "daemon" }
|
||||||
env_logger.workspace = true
|
env_logger.workspace = true
|
||||||
freedesktop_entry_parser = "1.3.0"
|
freedesktop_entry_parser = "1.3.0"
|
||||||
|
|
@ -62,7 +63,7 @@ opt-level = 2
|
||||||
opt-level = 2
|
opt-level = 2
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = ["daemon"]
|
members = ["cosmic-greeter-config", "daemon"]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
|
|
@ -87,6 +88,9 @@ default-features = false
|
||||||
git = "https://github.com/pop-os/cosmic-comp"
|
git = "https://github.com/pop-os/cosmic-comp"
|
||||||
default-features = false
|
default-features = false
|
||||||
|
|
||||||
|
[workspace.dependencies.cosmic-greeter-config]
|
||||||
|
path = "cosmic-greeter-config"
|
||||||
|
|
||||||
[workspace.dependencies.cosmic-config]
|
[workspace.dependencies.cosmic-config]
|
||||||
git = "https://github.com/pop-os/libcosmic"
|
git = "https://github.com/pop-os/libcosmic"
|
||||||
default-features = false
|
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,
|
style, theme, widget, Element,
|
||||||
};
|
};
|
||||||
use cosmic_comp_config::CosmicCompConfig;
|
use cosmic_comp_config::CosmicCompConfig;
|
||||||
|
use cosmic_greeter_config::Config as CosmicGreeterConfig;
|
||||||
use cosmic_greeter_daemon::{UserData, WallpaperData};
|
use cosmic_greeter_daemon::{UserData, WallpaperData};
|
||||||
use greetd_ipc::Request;
|
use greetd_ipc::Request;
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::{hash_map, HashMap},
|
||||||
error::Error,
|
error::Error,
|
||||||
fs, io,
|
fs, io,
|
||||||
|
num::NonZeroU32,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
process,
|
process,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
|
|
@ -130,6 +132,14 @@ pub fn main() -> Result<(), Box<dyn Error>> {
|
||||||
// Sort user data by uid
|
// Sort user data by uid
|
||||||
user_datas.sort_by(|a, b| a.uid.cmp(&b.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 {
|
enum SessionType {
|
||||||
X11,
|
X11,
|
||||||
Wayland,
|
Wayland,
|
||||||
|
|
@ -294,6 +304,8 @@ pub fn main() -> Result<(), Box<dyn Error>> {
|
||||||
sessions,
|
sessions,
|
||||||
layouts_opt,
|
layouts_opt,
|
||||||
comp_config_handler,
|
comp_config_handler,
|
||||||
|
greeter_config,
|
||||||
|
greeter_config_handler,
|
||||||
fallback_background,
|
fallback_background,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -310,6 +322,8 @@ pub struct Flags {
|
||||||
sessions: HashMap<String, (Vec<String>, Vec<String>)>,
|
sessions: HashMap<String, (Vec<String>, Vec<String>)>,
|
||||||
layouts_opt: Option<Arc<xkb_data::KeyboardLayouts>>,
|
layouts_opt: Option<Arc<xkb_data::KeyboardLayouts>>,
|
||||||
comp_config_handler: Option<cosmic_config::Config>,
|
comp_config_handler: Option<cosmic_config::Config>,
|
||||||
|
greeter_config: CosmicGreeterConfig,
|
||||||
|
greeter_config_handler: Option<cosmic_config::Config>,
|
||||||
fallback_background: widget::image::Handle,
|
fallback_background: widget::image::Handle,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -362,6 +376,7 @@ pub enum Dropdown {
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum Message {
|
pub enum Message {
|
||||||
Auth(Option<String>),
|
Auth(Option<String>),
|
||||||
|
ConfigUpdateUser,
|
||||||
DialogCancel,
|
DialogCancel,
|
||||||
DialogConfirm,
|
DialogConfirm,
|
||||||
DropdownToggle(Dropdown),
|
DropdownToggle(Dropdown),
|
||||||
|
|
@ -590,12 +605,6 @@ impl cosmic::Application for App {
|
||||||
core.window.show_minimize = false;
|
core.window.show_minimize = false;
|
||||||
core.window.use_template = 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
|
//TODO: use full_name_opt
|
||||||
let mut usernames: Vec<_> = flags
|
let mut usernames: Vec<_> = flags
|
||||||
.user_datas
|
.user_datas
|
||||||
|
|
@ -609,11 +618,25 @@ impl cosmic::Application for App {
|
||||||
usernames.sort_by(|a, b| a.1.cmp(&b.1));
|
usernames.sort_by(|a, b| a.1.cmp(&b.1));
|
||||||
|
|
||||||
//TODO: use last selected user
|
//TODO: use last selected user
|
||||||
let selected_username = flags
|
let (selected_username, uid) = flags
|
||||||
.user_datas
|
.user_datas
|
||||||
.first()
|
.first()
|
||||||
.map(|x| x.name.clone())
|
.map(|x| (x.name.clone(), NonZeroU32::new(x.uid)))
|
||||||
.unwrap_or(String::new());
|
.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 {
|
let app = App {
|
||||||
core,
|
core,
|
||||||
|
|
@ -779,6 +802,23 @@ impl cosmic::Application for App {
|
||||||
if username != self.selected_username {
|
if username != self.selected_username {
|
||||||
self.selected_username = username.clone();
|
self.selected_username = username.clone();
|
||||||
self.surface_images.clear();
|
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 {
|
match &self.socket_state {
|
||||||
SocketState::Open => {
|
SocketState::Open => {
|
||||||
self.prompt_opt = None;
|
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) => {
|
Message::Auth(response) => {
|
||||||
self.prompt_opt = None;
|
self.prompt_opt = None;
|
||||||
self.error_opt = None;
|
self.error_opt = None;
|
||||||
|
|
@ -798,7 +902,10 @@ impl cosmic::Application for App {
|
||||||
self.error_opt = None;
|
self.error_opt = None;
|
||||||
match self.flags.sessions.get(&self.selected_session).cloned() {
|
match self.flags.sessions.get(&self.selected_session).cloned() {
|
||||||
Some((cmd, env)) => {
|
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),
|
None => todo!("session {:?} not found", self.selected_session),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue