From 912017615ccce599617efa1109f283240472895a Mon Sep 17 00:00:00 2001 From: Josh Megnauth Date: Thu, 29 Aug 2024 03:23:40 -0400 Subject: [PATCH 1/4] Respect user config for military time Closes: #96 --- daemon/src/lib.rs | 2 ++ daemon/src/main.rs | 19 ++++++++++++++++++- src/greeter.rs | 17 ++++++++++++++++- 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/daemon/src/lib.rs b/daemon/src/lib.rs index 489ffe0..1272358 100644 --- a/daemon/src/lib.rs +++ b/daemon/src/lib.rs @@ -11,6 +11,8 @@ pub struct UserData { pub theme_opt: Option, pub wallpapers_opt: Option>, pub xkb_config_opt: Option, + pub clock_military_time: bool, + // pub clock_show_seconds: bool, } #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] diff --git a/daemon/src/main.rs b/daemon/src/main.rs index a0ab282..b15e6f0 100644 --- a/daemon/src/main.rs +++ b/daemon/src/main.rs @@ -1,6 +1,6 @@ use cosmic_bg_config::Source; use cosmic_comp_config::CosmicCompConfig; -use cosmic_config::CosmicConfigEntry; +use cosmic_config::{ConfigGet, CosmicConfigEntry}; use cosmic_greeter_daemon::{UserData, WallpaperData}; use std::{env, error::Error, fs, future::pending, io, path::Path}; use zbus::{ConnectionBuilder, DBusError}; @@ -115,6 +115,8 @@ impl GreeterProxy { //TODO: should wallpapers come from a per-user call? wallpapers_opt: None, xkb_config_opt: None, + clock_military_time: false, + // clock_show_seconds: false, }; //IMPORTANT: Assume the identity of the user to ensure we don't read wallpaper file data as root @@ -208,6 +210,21 @@ impl GreeterProxy { log::error!("failed to create cosmic-comp config handler: {}", err); } }; + + match cosmic_config::Config::new("com.system76.CosmicAppletTime", 1) { + Ok(config_handler) => { + user_data.clock_military_time = + config_handler.get("military_time").unwrap_or_default(); + // user_data.clock_show_seconds = + // config_handler.get("show_seconds").unwrap_or_default(); + } + Err(err) => { + log::error!( + "failed to create CosmicAppletTime config handler: {:?}", + err + ); + } + }; }) .map_err(|err| GreeterError::RunAsUser(err.to_string()))?; diff --git a/src/greeter.rs b/src/greeter.rs index 48f1922..f828ecd 100644 --- a/src/greeter.rs +++ b/src/greeter.rs @@ -110,6 +110,8 @@ fn user_data_fallback() -> Vec { theme_opt: None, wallpapers_opt: None, xkb_config_opt: None, + clock_military_time: false, + // clock_show_seconds: false, } }) .collect() @@ -1025,7 +1027,20 @@ impl cosmic::Application for App { column = column .push(widget::text::title2(format!("{}", date)).style(style::Text::Accent)); - let time = dt.format_localized("%R", locale); + // xxx It may be prudent to store the index of the selected user to avoid + // searches. This would also simplify logic elsewhere. + let time = if self + .flags + .user_datas + .binary_search_by(|probe| probe.name.cmp(&self.selected_username)) + .ok() + .map(|i| self.flags.user_datas[i].clock_military_time) + .unwrap_or_default() + { + dt.format_localized("%R", locale) + } else { + dt.format_localized("%I:%M %P", locale) + }; column = column.push( widget::text(format!("{}", time)) .size(112.0) From 5a7f5dc3b56442d505908ef498f8a0543b92f134 Mon Sep 17 00:00:00 2001 From: Josh Megnauth Date: Sat, 31 Aug 2024 01:34:31 -0400 Subject: [PATCH 2/4] Reduce searches by caching UserData index --- src/greeter.rs | 70 +++++++++++++++++++++++++++++++------------------- 1 file changed, 44 insertions(+), 26 deletions(-) diff --git a/src/greeter.rs b/src/greeter.rs index f828ecd..cb55f97 100644 --- a/src/greeter.rs +++ b/src/greeter.rs @@ -374,6 +374,13 @@ pub enum Dropdown { Session, } +struct NameIndexPair { + /// Selected username + username: String, + /// Index of the [`UserData`] for the selected username + data_idx: Option, +} + /// Messages that are used specifically by our [`App`]. #[derive(Clone, Debug)] pub enum Message { @@ -418,7 +425,7 @@ pub struct App { power_info_opt: Option<(String, f64)>, socket_state: SocketState, usernames: Vec<(String, String)>, - selected_username: String, + selected_username: NameIndexPair, prompt_opt: Option<(String, bool, Option)>, session_names: Vec, selected_session: String, @@ -444,10 +451,9 @@ impl App { fn set_xkb_config(&self) { let user_data = match self - .flags - .user_datas - .iter() - .find(|x| &x.name == &self.selected_username) + .selected_username + .data_idx + .and_then(|i| self.flags.user_datas.get(i)) { Some(some) => some, None => return, @@ -475,10 +481,9 @@ impl App { fn update_user_config(&mut self) -> Command { let user_data = match self - .flags - .user_datas - .iter() - .find(|x| &x.name == &self.selected_username) + .selected_username + .data_idx + .and_then(|i| self.flags.user_datas.get(i)) { Some(some) => some, None => return Command::none(), @@ -574,6 +579,12 @@ impl App { None => Command::none(), } } + + fn user_data_index(user_datas: &[UserData], username: &str) -> Option { + user_datas + .binary_search_by(|probe| probe.name.as_str().cmp(username)) + .ok() + } } /// Implement [`cosmic::Application`] to integrate with COSMIC. @@ -620,11 +631,11 @@ impl cosmic::Application for App { usernames.sort_by(|a, b| a.1.cmp(&b.1)); //TODO: use last selected user - let (selected_username, uid) = flags + let (username, uid) = flags .user_datas .first() .map(|x| (x.name.clone(), NonZeroU32::new(x.uid))) - .unwrap_or((String::new(), None)); + .unwrap_or_default(); let mut session_names: Vec<_> = flags.sessions.keys().map(|x| x.to_string()).collect(); session_names.sort(); @@ -639,6 +650,8 @@ impl cosmic::Application for App { }) .or_else(|| session_names.first().cloned()) .unwrap_or_default(); + let data_idx = Some(0); + let selected_username = NameIndexPair { username, data_idx }; let app = App { core, @@ -764,7 +777,7 @@ impl cosmic::Application for App { SocketState::Open => { // When socket is opened, send create session return self.send_request(Request::CreateSession { - username: self.selected_username.clone(), + username: self.selected_username.username.clone(), }); } _ => {} @@ -801,14 +814,16 @@ impl cosmic::Application for App { if self.dropdown_opt == Some(Dropdown::User) { self.dropdown_opt = None; } - if username != self.selected_username { - self.selected_username = username.clone(); + if username != self.selected_username.username { + let data_idx = Self::user_data_index(&self.flags.user_datas, &username); + self.selected_username = NameIndexPair { username, data_idx }; self.surface_images.clear(); + // TODO: Remove search if let Some(session) = self .flags .user_datas .iter() - .find(|user| user.name == username) + .find(|user| user.name == self.selected_username.username) .and_then(|UserData { uid, .. }| { NonZeroU32::new(*uid).and_then(|uid| { self.flags @@ -835,19 +850,19 @@ impl cosmic::Application for App { .flags .user_datas .iter() - .find(|user| user.name == self.selected_username) + .find(|user| user.name == self.selected_username.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); + log::error!("Couldn't find user: {:?}", self.selected_username.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, + self.selected_username.username, user_entry.key() ); return Command::none(); @@ -888,7 +903,7 @@ impl cosmic::Application for App { log::error!( "Failed to set {} as last selected session for {} (UID: {}): {:?}", self.selected_session, - self.selected_username, + self.selected_username.username, uid, err ); @@ -1030,11 +1045,14 @@ impl cosmic::Application for App { // xxx It may be prudent to store the index of the selected user to avoid // searches. This would also simplify logic elsewhere. let time = if self - .flags - .user_datas - .binary_search_by(|probe| probe.name.cmp(&self.selected_username)) - .ok() - .map(|i| self.flags.user_datas[i].clock_military_time) + .selected_username + .data_idx + .and_then(|i| { + self.flags + .user_datas + .get(i) + .map(|user| user.clock_military_time) + }) .unwrap_or_default() { dt.format_localized("%R", locale) @@ -1135,7 +1153,7 @@ impl cosmic::Application for App { for (name, full_name) in self.usernames.iter() { items.push(menu_checklist( full_name, - name == &self.selected_username, + name == &self.selected_username.username, Message::Username(name.clone()), )); } @@ -1226,7 +1244,7 @@ impl cosmic::Application for App { } SocketState::Open => { for user_data in &self.flags.user_datas { - if &user_data.name == &self.selected_username { + if &user_data.name == &self.selected_username.username { match &user_data.icon_opt { Some(icon) => { column = column.push( From 70b8cae87e5efd5f664d9d5dd87e8beabd5a514e Mon Sep 17 00:00:00 2001 From: Josh Megnauth Date: Sun, 1 Sep 2024 00:49:42 -0400 Subject: [PATCH 3/4] Reduce time text's size for 12 hour clock --- src/greeter.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/greeter.rs b/src/greeter.rs index cb55f97..619376f 100644 --- a/src/greeter.rs +++ b/src/greeter.rs @@ -1042,9 +1042,7 @@ impl cosmic::Application for App { column = column .push(widget::text::title2(format!("{}", date)).style(style::Text::Accent)); - // xxx It may be prudent to store the index of the selected user to avoid - // searches. This would also simplify logic elsewhere. - let time = if self + let (time, time_size) = if self .selected_username .data_idx .and_then(|i| { @@ -1055,13 +1053,16 @@ impl cosmic::Application for App { }) .unwrap_or_default() { - dt.format_localized("%R", locale) + (dt.format_localized("%R", locale), 112.0) } else { - dt.format_localized("%I:%M %P", locale) + // xxx format_localized doesn't seem to show am/pm for some languages, such as + // French or Hungarian. This is apparently correct + // Also, time size needs to be reduced a bit here so that it fits on one line + (dt.format_localized("%I:%M %p", locale), 75.0) }; column = column.push( widget::text(format!("{}", time)) - .size(112.0) + .size(time_size) .style(style::Text::Accent), ); From 047e393eb877c12c3ebb6cef1cf03ad0e43d1b20 Mon Sep 17 00:00:00 2001 From: Josh Megnauth Date: Sat, 7 Sep 2024 02:17:27 -0400 Subject: [PATCH 4/4] Remove unneeded searches in `ConfigUpdateUser` --- src/greeter.rs | 46 ++++++++++++++++++++++------------------------ 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/src/greeter.rs b/src/greeter.rs index 619376f..5448fb9 100644 --- a/src/greeter.rs +++ b/src/greeter.rs @@ -818,22 +818,20 @@ impl cosmic::Application for App { let data_idx = Self::user_data_index(&self.flags.user_datas, &username); self.selected_username = NameIndexPair { username, data_idx }; self.surface_images.clear(); - // TODO: Remove search - if let Some(session) = self - .flags - .user_datas - .iter() - .find(|user| user.name == self.selected_username.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()) + if let Some(session) = data_idx.and_then(|i| { + self.flags + .user_datas + .get(i) + .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 { @@ -846,15 +844,15 @@ impl cosmic::Application for App { } } Message::ConfigUpdateUser => { - let Some(user_entry) = self - .flags - .user_datas - .iter() - .find(|user| user.name == self.selected_username.username) - .and_then(|UserData { uid, .. }| { - NonZeroU32::new(*uid).map(|uid| self.flags.greeter_config.users.entry(uid)) - }) - else { + let Some(user_entry) = self.selected_username.data_idx.and_then(|i| { + self.flags + .user_datas + .get(i) + .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.username); return Command::none(); };