From 9a72c09fed6b459b42fce040228c52b9ae2856c7 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Thu, 7 Aug 2025 19:05:31 -0400 Subject: [PATCH] better syncing --- Cargo.lock | 1 + daemon/src/lib.rs | 26 +++++- debian/cosmic-greeter.tmpfiles | 3 +- src/greeter.rs | 149 +++++++++++++++++++++++++-------- src/lib.rs | 2 - src/state.rs | 8 -- 6 files changed, 144 insertions(+), 45 deletions(-) delete mode 100644 src/state.rs diff --git a/Cargo.lock b/Cargo.lock index 3c36d2e..820d95f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1308,6 +1308,7 @@ name = "cosmic-settings-daemon-config" version = "0.1.0" dependencies = [ "cosmic-config", + "cosmic-theme", "ron 0.8.1", "serde", ] diff --git a/daemon/src/lib.rs b/daemon/src/lib.rs index ac0b92f..ca9d200 100644 --- a/daemon/src/lib.rs +++ b/daemon/src/lib.rs @@ -8,7 +8,7 @@ use std::{ pub use cosmic_applets_config::time::TimeAppletConfig; pub use cosmic_bg_config::{state::State as BgState, Color, Source as BgSource}; pub use cosmic_comp_config::{CosmicCompConfig, XkbConfig}; -pub use cosmic_theme::Theme; +pub use cosmic_theme::{Theme, ThemeBuilder}; #[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize)] pub struct UserData { @@ -17,6 +17,7 @@ pub struct UserData { pub full_name: String, pub icon_opt: Option>, pub theme_opt: Option, + pub theme_builder_opt: Option, pub bg_state: BgState, pub bg_path_data: BTreeMap>, pub xkb_config_opt: Option, @@ -59,6 +60,7 @@ impl UserData { pub fn load_config_as_user(&mut self) { self.icon_opt = None; self.theme_opt = None; + self.theme_builder_opt = None; self.bg_state = Default::default(); self.xkb_config_opt = None; self.time_applet_config = Default::default(); @@ -114,6 +116,28 @@ impl UserData { } } + match if is_dark { + cosmic_theme::ThemeBuilder::dark_config() + } else { + cosmic_theme::ThemeBuilder::light_config() + } { + Ok(helper) => match cosmic_theme::ThemeBuilder::get_entry(&helper) { + Ok(theme) => { + self.theme_builder_opt = Some(theme); + } + Err((errs, theme)) => { + log::error!("failed to load cosmic-theme builder config: {:?}", errs); + self.theme_builder_opt = Some(theme); + } + }, + Err(err) => { + log::error!( + "failed to create cosmic-theme builder config helper: {:?}", + err + ); + } + } + //TODO: fallback to background config if background state is not set? match cosmic_bg_config::state::State::state() { Ok(helper) => match cosmic_bg_config::state::State::get_entry(&helper) { diff --git a/debian/cosmic-greeter.tmpfiles b/debian/cosmic-greeter.tmpfiles index a7dc7a8..c4fd74a 100644 --- a/debian/cosmic-greeter.tmpfiles +++ b/debian/cosmic-greeter.tmpfiles @@ -1,2 +1,3 @@ # Home directory of cosmic-greeter -d /var/lib/cosmic-greeter 0750 cosmic-greeter cosmic-greeter \ No newline at end of file +d /var/lib/cosmic-greeter 0750 cosmic-greeter cosmic-greeter +d /run/cosmic-greeter 0755 cosmic-greeter cosmic-greeter - diff --git a/src/greeter.rs b/src/greeter.rs index b91e48b..7941abd 100644 --- a/src/greeter.rs +++ b/src/greeter.rs @@ -9,7 +9,6 @@ use cosmic::app::{Core, Settings, Task}; use cosmic::cctk::wayland_protocols::xdg::shell::client::xdg_positioner::Gravity; use cosmic::iced::{Point, Size}; use cosmic::iced_runtime::platform_specific::wayland::subsurface::SctkSubsurfaceSettings; -use cosmic::surface; use cosmic::widget::text; use cosmic::{ Element, @@ -29,11 +28,13 @@ use cosmic::{ iced_runtime::core::window::Id as SurfaceId, theme, widget, }; +use cosmic::{cosmic_theme::{self, CosmicPalette}, surface}; +use cosmic_config::CosmicConfigEntry; use cosmic_greeter_config::Config as CosmicGreeterConfig; use cosmic_greeter_daemon::UserData; -use cosmic_settings_subscriptions::{ - accessibility::{self, DBusRequest, DBusUpdate}, - cosmic_a11y_manager::{AccessibilityEvent, AccessibilityRequest, ColorFilter}, +use cosmic_settings_daemon_config::greeter::GreeterAccessibilityState; +use cosmic_settings_subscriptions::cosmic_a11y_manager::{ + AccessibilityEvent, AccessibilityRequest, }; use greetd_ipc::Request; use std::sync::LazyLock; @@ -47,7 +48,8 @@ use std::{ sync::Arc, time::{Duration, Instant}, }; -use tokio::{sync::mpsc::UnboundedSender, time}; +use tokio::process::Child; +use tokio::time; use wayland_client::{Proxy, protocol::wl_output::WlOutput}; use zbus::{Connection, proxy}; @@ -339,7 +341,6 @@ struct NameIndexPair { #[derive(Clone, Debug)] pub enum Message { Common(common::Message), - DBusUpdate(DBusUpdate), OutputEvent(OutputEvent, WlOutput), Auth(Option), ConfigUpdateUser, @@ -354,6 +355,7 @@ pub enum Message { KeyboardLayout(usize), Login, Reconnect, + Reload(cosmic::Theme), Restart, Session(String), Shutdown, @@ -389,20 +391,20 @@ pub struct App { dropdown_opt: Option, heartbeat_handle: Option, entering_name: bool, + theme_builder: cosmic_theme::ThemeBuilder, accessibility: Accessibility, } #[derive(Default)] struct Accessibility { - pub dbus_sender: Option>, pub wayland_sender: Option>, pub wayland_protocol_version: Option, pub state: cosmic_settings_daemon_config::greeter::GreeterAccessibilityState, pub helper: Option, - pub screen_reader: bool, + pub screen_reader: Option, pub magnifier: bool, pub high_contrast: bool, pub invert_colors: bool, @@ -592,8 +594,8 @@ impl App { let mut items = Vec::new(); items.push(menu_checklist( fl!("accessibility", "screen-reader"), - self.accessibility.screen_reader, - Message::ScreenReader(!self.accessibility.screen_reader), + self.accessibility.screen_reader.is_some(), + Message::ScreenReader(!self.accessibility.screen_reader.is_some()), )); items.push(menu_checklist( fl!("accessibility", "magnifier"), @@ -907,8 +909,13 @@ impl App { // Ensure that user's xkb config is used self.common.set_xkb_config(&user_data); + if let Some(builder) = &user_data.theme_builder_opt { + self.theme_builder = builder.clone(); + } + match &user_data.theme_opt { Some(theme) => { + self.accessibility.high_contrast = theme.is_high_contrast; cosmic::command::set_theme(cosmic::Theme::custom(Arc::new(theme.clone()))) } None => Task::none(), @@ -940,10 +947,6 @@ impl cosmic::Application for App { /// Creates the application, and optionally emits command on initialize. fn init(core: Core, flags: Self::Flags) -> (Self, Task) { - // init state that is communicated to cosmic session - if let Err(err) = crate::state::init() { - log::error!("{err:?}"); - } let (mut common, common_task) = Common::init(core); common.on_output_event = Some(Box::new(|output_event, output| { Message::OutputEvent(output_event, output) @@ -993,6 +996,10 @@ impl cosmic::Application for App { let mut accessibility = Accessibility::default(); accessibility.helper = cosmic_settings_daemon_config::greeter::GreeterAccessibilityState::config().ok(); + // Reset the state so that only new changes are applied. + if let Some(helper) = accessibility.helper.as_ref() { + _ = GreeterAccessibilityState::write_entry(&Default::default(), helper); + } let app = App { common, @@ -1008,6 +1015,7 @@ impl cosmic::Application for App { heartbeat_handle: None, entering_name: false, accessibility, + theme_builder: Default::default(), }; (app, common_task) } @@ -1018,20 +1026,7 @@ impl cosmic::Application for App { Message::Common(common_message) => { return self.common.update(common_message); } - Message::DBusUpdate(update) => match update { - DBusUpdate::Error(err) => { - log::error!("{err}"); - let _ = self.accessibility.dbus_sender.take(); - self.accessibility.screen_reader = false; - } - DBusUpdate::Status(enabled) => { - self.accessibility.screen_reader = enabled; - } - DBusUpdate::Init(enabled, tx) => { - self.accessibility.screen_reader = enabled; - self.accessibility.dbus_sender = Some(tx); - } - }, + Message::OutputEvent(output_event, output) => { match output_event { OutputEvent::Created(output_info_opt) => { @@ -1176,6 +1171,12 @@ impl cosmic::Application for App { _ => {} } } + Message::Reload(new) => { + + return cosmic::command::set_theme( + new.clone(), + ); + } Message::Session(selected_session) => { self.selected_session = selected_session; if self.dropdown_opt == Some(Dropdown::Session) { @@ -1458,23 +1459,78 @@ impl cosmic::Application for App { )); } Message::ScreenReader(enabled) => { - if let Some(tx) = &self.accessibility.dbus_sender.as_ref() { - self.accessibility.screen_reader = enabled; - let _ = tx.send(DBusRequest::Status(enabled)); + if enabled + && self + .accessibility + .screen_reader + .as_mut() + .is_none_or(|c| c.try_wait().is_ok()) + { + self.accessibility.screen_reader = + tokio::process::Command::new("/usr/bin/orca").spawn().ok(); } else { - self.accessibility.screen_reader = false; + if let Some(mut c) = self.accessibility.screen_reader.take() { + return cosmic::task::future::<(), ()>(async move { + if let Err(err) = c.kill().await { + log::error!("Failed to stop screen reader: {err:?}"); + } + }) + .discard(); + } + } + + if let Some(helper) = self.accessibility.helper.as_ref() { + _ = self + .accessibility + .state + .set_screen_reader(&helper, Some(enabled)); } } Message::Magnifier(enabled) => { if let Some(tx) = &self.accessibility.wayland_sender { self.accessibility.magnifier = enabled; let _ = tx.send(AccessibilityRequest::Magnifier(enabled)); + if let Some(helper) = self.accessibility.helper.as_ref() { + _ = self + .accessibility + .state + .set_magnifier(&helper, Some(enabled)); + } } else { self.accessibility.magnifier = false; } } Message::HighContrast(enabled) => { self.accessibility.high_contrast = enabled; + + if let Some(helper) = self.accessibility.helper.as_ref() { + _ = self + .accessibility + .state + .set_high_contrast(&helper, Some(enabled)); + } + let builder = self.theme_builder.clone(); + + return cosmic::task::future::<_, _>(async move { + let builder = builder.clone(); + let (tx, rx) = tokio::sync::oneshot::channel(); + std::thread::spawn(move || { + match apply_hc_theme(builder, enabled) { + Ok(t) => { + _ = tx.send(Some(t)); + } + Err(err) => { + log::error!("{err:?}"); + _ = tx.send(None); + } + } + }); + if let Ok(Some(theme)) = rx.await { + cosmic::Action::App(Message::Reload(cosmic::Theme::custom(std::sync::Arc::new(theme)))) + } else { + cosmic::Action::None + } + }); } Message::InvertColors(enabled) => { if let Some(tx) = &self.accessibility.wayland_sender { @@ -1483,6 +1539,12 @@ impl cosmic::Application for App { inverted: enabled, filter: None, }); + if let Some(helper) = self.accessibility.helper.as_ref() { + _ = self + .accessibility + .state + .set_invert_colors(&helper, Some(enabled)); + } } else { self.accessibility.invert_colors = false; } @@ -1539,7 +1601,28 @@ impl cosmic::Application for App { self.common.subscription().map(Message::from), ipc::subscription(), wayland::a11y_subscription().map(Message::WaylandUpdate), - accessibility::subscription().map(Message::DBusUpdate), ]) } } + + +pub fn apply_hc_theme(builder: cosmic_theme::ThemeBuilder, enabled: bool) -> Result { + let is_dark = builder.palette.is_dark(); + let mut builder = builder.clone(); + + builder.palette = if is_dark { + if enabled { + CosmicPalette::HighContrastDark(builder.palette.inner()) + } else { + CosmicPalette::Dark(builder.palette.inner()) + } + } else if enabled { + CosmicPalette::HighContrastLight(builder.palette.inner()) + } else { + CosmicPalette::Light(builder.palette.inner()) + }; + + let new_theme = builder.build(); + + Ok(new_theme) +} diff --git a/src/lib.rs b/src/lib.rs index 34a8b5b..8535a1e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,6 +19,4 @@ mod networkmanager; #[cfg(feature = "upower")] mod upower; -mod state; - mod time; diff --git a/src/state.rs b/src/state.rs deleted file mode 100644 index 3348acc..0000000 --- a/src/state.rs +++ /dev/null @@ -1,8 +0,0 @@ -use std::os::unix::fs::PermissionsExt as _; - -pub fn init() -> anyhow::Result<()> { - let path = cosmic_settings_daemon_config::greeter::GreeterAccessibilityState::path(); - std::fs::create_dir_all(&path)?; - std::fs::set_permissions(path, std::fs::Permissions::from_mode(0o755))?; - Ok(()) -}