diff --git a/Cargo.lock b/Cargo.lock index 7f6c84a..3ba0798 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -915,6 +915,16 @@ dependencies = [ "wayland-client", ] +[[package]] +name = "cosmic-comp-config" +version = "0.1.0" +source = "git+https://github.com/pop-os/cosmic-comp#cbca13803cb499786b080ea9e4ee74cd4f704556" +dependencies = [ + "cosmic-config", + "input", + "serde", +] + [[package]] name = "cosmic-config" version = "0.1.0" @@ -964,6 +974,7 @@ version = "0.1.0" dependencies = [ "chrono", "cosmic-bg-config", + "cosmic-comp-config", "cosmic-config", "cosmic-dbus-networkmanager", "cosmic-greeter-daemon", @@ -985,6 +996,7 @@ dependencies = [ "upower_dbus", "wayland-client", "xdg", + "xkb-data", "zbus 4.2.2", ] @@ -993,6 +1005,7 @@ name = "cosmic-greeter-daemon" version = "0.1.0" dependencies = [ "cosmic-bg-config", + "cosmic-comp-config", "cosmic-config", "cosmic-theme", "env_logger", @@ -2583,6 +2596,26 @@ dependencies = [ "libc", ] +[[package]] +name = "input" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7911ce3db9c10c5ab4a35c49af778a5f9a827bd0f7371d9be56175d8dd2740d0" +dependencies = [ + "bitflags 2.5.0", + "input-sys", + "io-lifetimes 1.0.11", + "libc", + "log", + "udev", +] + +[[package]] +name = "input-sys" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd4f5b4d1c00331c5245163aacfe5f20be75b564c7112d45893d4ae038119eb0" + [[package]] name = "instant" version = "0.1.13" @@ -2834,6 +2867,16 @@ dependencies = [ "libc", ] +[[package]] +name = "libudev-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c8469b4a23b962c1396b9b451dda50ef5b283e8dd309d69033475fa9b334324" +dependencies = [ + "libc", + "pkg-config", +] + [[package]] name = "linux-raw-sys" version = "0.3.8" @@ -4100,6 +4143,18 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-xml-rs" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb3aa78ecda1ebc9ec9847d5d3aba7d618823446a049ba2491940506da6e2782" +dependencies = [ + "log", + "serde", + "thiserror", + "xml-rs", +] + [[package]] name = "serde_derive" version = "1.0.203" @@ -4738,6 +4793,18 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +[[package]] +name = "udev" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50051c6e22be28ee6f217d50014f3bc29e81c20dc66ff7ca0d5c5226e1dcc5a1" +dependencies = [ + "io-lifetimes 1.0.11", + "libc", + "libudev-sys", + "pkg-config", +] + [[package]] name = "uds_windows" version = "1.1.0" @@ -5551,6 +5618,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "xkb-data" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "294a599fc9e6a43c9f44f5d6c560b89fd751be413717442b31c17fa367d3c764" +dependencies = [ + "serde", + "serde-xml-rs", +] + [[package]] name = "xkbcommon" version = "0.7.0" diff --git a/Cargo.toml b/Cargo.toml index 6b30284..21ce062 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] chrono = "0.4.31" cosmic-bg-config.workspace = true +cosmic-comp-config.workspace = true cosmic-config = { workspace = true, features = ["calloop", "macro"] } cosmic-greeter-daemon = { path = "daemon" } env_logger.workspace = true @@ -16,6 +17,7 @@ pam-client = "0.5.0" pwd.workspace = true ron.workspace = true shlex = "1.2.0" +xkb-data = "0.1" xdg = "2.5.2" #TODO: reduce features tokio = { workspace = true, features = ["full"] } @@ -64,6 +66,10 @@ zbus = "4" git = "https://github.com/pop-os/cosmic-bg" default-features = false +[workspace.dependencies.cosmic-comp-config] +git = "https://github.com/pop-os/cosmic-comp" +default-features = false + [workspace.dependencies.cosmic-config] git = "https://github.com/pop-os/libcosmic" default-features = false diff --git a/daemon/Cargo.toml b/daemon/Cargo.toml index 8aa8e1a..7e827f5 100644 --- a/daemon/Cargo.toml +++ b/daemon/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" [dependencies] cosmic-bg-config.workspace = true +cosmic-comp-config.workspace = true cosmic-config.workspace = true cosmic-theme.workspace = true env_logger.workspace = true diff --git a/daemon/src/lib.rs b/daemon/src/lib.rs index ce0c4b3..489ffe0 100644 --- a/daemon/src/lib.rs +++ b/daemon/src/lib.rs @@ -1,4 +1,5 @@ pub use cosmic_bg_config::Color; +pub use cosmic_comp_config::XkbConfig; pub use cosmic_theme::Theme; #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] @@ -9,6 +10,7 @@ pub struct UserData { pub icon_opt: Option>, pub theme_opt: Option, pub wallpapers_opt: Option>, + pub xkb_config_opt: Option, } #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] diff --git a/daemon/src/main.rs b/daemon/src/main.rs index 14147fc..48b8c2b 100644 --- a/daemon/src/main.rs +++ b/daemon/src/main.rs @@ -1,4 +1,5 @@ use cosmic_bg_config::Source; +use cosmic_comp_config::CosmicCompConfig; use cosmic_config::CosmicConfigEntry; use cosmic_greeter_daemon::{UserData, WallpaperData}; use std::{env, error::Error, fs, future::pending, io, path::Path}; @@ -113,6 +114,7 @@ impl GreeterProxy { theme_opt: None, //TODO: should wallpapers come from a per-user call? wallpapers_opt: None, + xkb_config_opt: None, }; //IMPORTANT: Assume the identity of the user to ensure we don't read wallpaper file data as root @@ -188,6 +190,24 @@ impl GreeterProxy { } user_data.wallpapers_opt = Some(wallpaper_datas); } + + match cosmic_config::Config::new( + "com.system76.CosmicComp", + CosmicCompConfig::VERSION, + ) { + Ok(config_handler) => match CosmicCompConfig::get_entry(&config_handler) { + Ok(config) => { + user_data.xkb_config_opt = Some(config.xkb_config); + } + Err((errs, config)) => { + log::error!("errors loading cosmic-comp config: {:?}", errs); + user_data.xkb_config_opt = Some(config.xkb_config); + } + }, + Err(err) => { + log::error!("failed to create cosmic-comp config handler: {}", err); + } + }; }) .map_err(|err| GreeterError::RunAsUser(err.to_string()))?; diff --git a/debian/control b/debian/control index da33fe8..313f3e7 100644 --- a/debian/control +++ b/debian/control @@ -7,6 +7,7 @@ Build-Depends: git, just (>= 1.13.0), libclang-dev, + libinput-dev, libpam-dev, libwayland-dev, libxkbcommon-dev, diff --git a/i18n/en/cosmic_greeter.ftl b/i18n/en/cosmic_greeter.ftl index 69385a4..357034e 100644 --- a/i18n/en/cosmic_greeter.ftl +++ b/i18n/en/cosmic_greeter.ftl @@ -1,7 +1,11 @@ cancel = Cancel +keyboard-layout = Keyboard layout restart = Restart restart-now = Restart now? restart-timeout = The system will restart automatically in {$seconds} seconds. +session = Session shutdown = Shut down shutdown-now = Shut down now? shutdown-timeout = The system will shut down automatically in {$seconds} seconds. +suspend = Suspend +user = User diff --git a/src/greeter.rs b/src/greeter.rs index 98bef15..68d96d7 100644 --- a/src/greeter.rs +++ b/src/greeter.rs @@ -3,6 +3,7 @@ use cosmic::app::{message, Command, Core, Settings}; use cosmic::{ + cosmic_config::{self, ConfigSet, CosmicConfigEntry}, executor, iced::{ self, alignment, @@ -18,11 +19,12 @@ use cosmic::{ destroy_layer_surface, get_layer_surface, Anchor, KeyboardInteractivity, Layer, }, }, - Length, Subscription, + Background, Border, Length, Subscription, }, iced_runtime::core::window::Id as SurfaceId, - style, widget, Element, + style, theme, widget, Element, }; +use cosmic_comp_config::CosmicCompConfig; use cosmic_greeter_daemon::{UserData, WallpaperData}; use greetd_ipc::{codec::TokioCodec, AuthMessageType, Request, Response}; use std::{ @@ -105,6 +107,7 @@ fn user_data_fallback() -> Vec { icon_opt, theme_opt: None, wallpapers_opt: None, + xkb_config_opt: None, } }) .collect() @@ -112,6 +115,8 @@ fn user_data_fallback() -> Vec { } pub fn main() -> Result<(), Box> { + env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("warn")).init(); + crate::localize::localize(); let mut user_datas = match futures::executor::block_on(async { user_data_dbus().await }) { @@ -257,12 +262,31 @@ pub fn main() -> Result<(), Box> { sessions }; + let layouts_opt = match xkb_data::keyboard_layouts() { + Ok(ok) => Some(Arc::new(ok)), + Err(err) => { + log::warn!("failed to load keyboard layouts: {}", err); + None + } + }; + + let comp_config_handler = + match cosmic_config::Config::new("com.system76.CosmicComp", CosmicCompConfig::VERSION) { + Ok(config_handler) => Some(config_handler), + Err(err) => { + log::error!("failed to create cosmic-comp config handler: {}", err); + None + } + }; + let fallback_background = widget::image::Handle::from_memory(include_bytes!("../res/background.png")); let flags = Flags { user_datas, sessions, + layouts_opt, + comp_config_handler, fallback_background, }; @@ -345,6 +369,8 @@ fn request_command(socket: Arc>, request: Request) -> Command< pub struct Flags { user_datas: Vec, sessions: HashMap>, + layouts_opt: Option>, + comp_config_handler: Option, fallback_background: widget::image::Handle, } @@ -360,6 +386,13 @@ pub enum SocketState { Error(Arc), } +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct ActiveLayout { + layout: String, + description: String, + variant: String, +} + #[derive(Clone, Copy, Debug)] pub enum DialogPage { Restart(Instant), @@ -378,6 +411,14 @@ impl DialogPage { } } +///TODO: this is custom code that should be better handled by libcosmic +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum Dropdown { + Keyboard, + User, + Session, +} + /// Messages that are used specifically by our [`App`]. #[derive(Clone, Debug)] pub enum Message { @@ -389,12 +430,14 @@ pub enum Message { PowerInfo(Option<(String, f64)>), Prompt(String, bool, Option), Session(String), - Username(Arc>, String), + Username(String), Auth(Arc>, Option), Login(Arc>), Error(Arc>, String), DialogCancel, DialogConfirm, + DropdownToggle(Dropdown), + KeyboardLayout(usize), Reconnect, Suspend, Restart, @@ -415,22 +458,56 @@ pub struct App { network_icon_opt: Option<&'static str>, power_info_opt: Option<(String, f64)>, socket_state: SocketState, - username_opt: Option, + usernames: Vec<(String, String)>, + selected_username: String, prompt_opt: Option<(String, bool, Option)>, session_names: Vec, selected_session: String, + active_layouts: Vec, error_opt: Option, dialog_page_opt: Option, + dropdown_opt: Option, } impl App { - fn update_user_config(&mut self) -> Command { - let username = match &self.username_opt { + fn set_xkb_config(&self) { + let user_data = match self + .flags + .user_datas + .iter() + .find(|x| &x.name == &self.selected_username) + { Some(some) => some, - None => return Command::none(), + None => return, }; - let user_data = match self.flags.user_datas.iter().find(|x| &x.name == username) { + if let Some(mut xkb_config) = user_data.xkb_config_opt.clone() { + xkb_config.layout = String::new(); + xkb_config.variant = String::new(); + for (i, layout) in self.active_layouts.iter().enumerate() { + if i > 0 { + xkb_config.layout.push(','); + xkb_config.variant.push(','); + } + xkb_config.layout.push_str(&layout.layout); + xkb_config.variant.push_str(&layout.variant); + } + if let Some(comp_config_handler) = &self.flags.comp_config_handler { + match comp_config_handler.set("xkb_config", xkb_config) { + Ok(()) => log::info!("updated cosmic-comp xkb_config"), + Err(err) => log::error!("failed to update cosmic-comp xkb_config: {}", err), + } + } + } + } + + fn update_user_config(&mut self) -> Command { + let user_data = match self + .flags + .user_datas + .iter() + .find(|x| &x.name == &self.selected_username) + { Some(some) => some, None => return Command::none(), }; @@ -471,6 +548,53 @@ impl App { } } + // From cosmic-applet-input-sources + if let Some(keyboard_layouts) = &self.flags.layouts_opt { + if let Some(xkb_config) = &user_data.xkb_config_opt { + self.active_layouts.clear(); + let config_layouts = xkb_config.layout.split_terminator(','); + let config_variants = xkb_config + .variant + .split_terminator(',') + .chain(std::iter::repeat("")); + for (config_layout, config_variant) in config_layouts.zip(config_variants) { + for xkb_layout in keyboard_layouts.layouts() { + if config_layout != xkb_layout.name() { + continue; + } + if config_variant.is_empty() { + let active_layout = ActiveLayout { + description: xkb_layout.description().to_owned(), + layout: config_layout.to_owned(), + variant: config_variant.to_owned(), + }; + self.active_layouts.push(active_layout); + continue; + } + + let Some(xkb_variants) = xkb_layout.variants() else { + continue; + }; + for xkb_variant in xkb_variants { + if config_variant != xkb_variant.name() { + continue; + } + let active_layout = ActiveLayout { + description: xkb_variant.description().to_owned(), + layout: config_layout.to_owned(), + variant: config_variant.to_owned(), + }; + self.active_layouts.push(active_layout); + } + } + } + log::info!("{:?}", self.active_layouts); + + // Ensure that user's xkb config is used + self.set_xkb_config(); + } + } + match &user_data.theme_opt { Some(theme) => { cosmic::app::command::set_theme(cosmic::Theme::custom(Arc::new(theme.clone()))) @@ -514,9 +638,28 @@ impl cosmic::Application for App { let mut session_names: Vec<_> = flags.sessions.keys().map(|x| x.to_string()).collect(); session_names.sort(); - //TODO: determine default session? + //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 + .iter() + .map(|x| { + let name = x.name.clone(); + let full_name = x.full_name_opt.clone().unwrap_or_else(|| name.clone()); + (name, full_name) + }) + .collect(); + usernames.sort_by(|a, b| a.1.cmp(&b.1)); + + //TODO: use last selected user + let selected_username = flags + .user_datas + .first() + .map(|x| x.name.clone()) + .unwrap_or(String::new()); + let mut app = App { core, flags, @@ -528,12 +671,15 @@ impl cosmic::Application for App { network_icon_opt: None, power_info_opt: None, socket_state: SocketState::Pending, - username_opt: None, + usernames, + selected_username, prompt_opt: None, session_names, selected_session, + active_layouts: Vec::new(), error_opt: None, dialog_page_opt: None, + dropdown_opt: None, }; let command = app.update(Message::Reconnect); (app, command) @@ -636,15 +782,13 @@ impl cosmic::Application for App { self.socket_state = socket_state; match &self.socket_state { SocketState::Open(socket) => { - // When socket is opened, request default user - //TODO: choose last used user - match self.flags.user_datas.first().map(|x| x.name.clone()) { - Some(username) => { - let socket = socket.clone(); - return self.update(Message::Username(socket, username)); - } - None => {} - } + // When socket is opened, send create session + return Command::batch([request_command( + socket.clone(), + Request::CreateSession { + username: self.selected_username.clone(), + }, + )]); } _ => {} } @@ -672,15 +816,25 @@ impl cosmic::Application for App { } Message::Session(selected_session) => { self.selected_session = selected_session; + if self.dropdown_opt == Some(Dropdown::Session) { + self.dropdown_opt = None; + } } - Message::Username(socket, username) => { - self.username_opt = Some(username.clone()); - self.prompt_opt = None; - self.surface_images.clear(); - return Command::batch([ - self.update_user_config(), - request_command(socket, Request::CreateSession { username }), - ]); + Message::Username(username) => { + if self.dropdown_opt == Some(Dropdown::User) { + self.dropdown_opt = None; + } + if username != self.selected_username { + self.selected_username = username.clone(); + self.surface_images.clear(); + match &self.socket_state { + SocketState::Open(socket) => { + self.prompt_opt = None; + return request_command(socket.clone(), Request::CancelSession); + } + _ => {} + } + } } Message::Auth(socket, response) => { self.prompt_opt = None; @@ -708,18 +862,23 @@ impl cosmic::Application for App { return request_command(socket, Request::CancelSession); } Message::Reconnect => { - return Command::perform( - async { - message::app(Message::Socket(match env::var_os("GREETD_SOCK") { - Some(socket_path) => match UnixStream::connect(&socket_path).await { - Ok(socket) => SocketState::Open(Arc::new(socket.into())), - Err(err) => SocketState::Error(Arc::new(err)), - }, - None => SocketState::NotSet, - })) - }, - |x| x, - ); + return Command::batch([ + self.update_user_config(), + Command::perform( + async { + message::app(Message::Socket(match env::var_os("GREETD_SOCK") { + Some(socket_path) => { + match UnixStream::connect(&socket_path).await { + Ok(socket) => SocketState::Open(Arc::new(socket.into())), + Err(err) => SocketState::Error(Arc::new(err)), + } + } + None => SocketState::NotSet, + })) + }, + |x| x, + ), + ]); } Message::DialogCancel => { self.dialog_page_opt = None; @@ -757,6 +916,22 @@ impl cosmic::Application for App { } None => {} }, + Message::DropdownToggle(dropdown) => { + if self.dropdown_opt == Some(dropdown) { + self.dropdown_opt = None; + } else { + self.dropdown_opt = Some(dropdown); + } + } + Message::KeyboardLayout(layout_i) => { + if layout_i < self.active_layouts.len() { + self.active_layouts.swap(0, layout_i); + self.set_xkb_config(); + } + if self.dropdown_opt == Some(Dropdown::Keyboard) { + self.dropdown_opt = None + } + } Message::Suspend => { #[cfg(feature = "logind")] return Command::perform( @@ -843,31 +1018,143 @@ impl cosmic::Application for App { ]); } - //TODO: implement these buttons + //TODO: move code for custom dropdowns to libcosmic + let menu_checklist = |label, value, message| { + Element::from( + widget::menu::menu_button(vec![ + if value { + widget::icon::from_name("object-select-symbolic") + .size(16) + .icon() + .width(Length::Fixed(16.0)) + .into() + } else { + widget::Space::with_width(Length::Fixed(17.0)).into() + }, + widget::Space::with_width(Length::Fixed(8.0)).into(), + widget::text(label) + .horizontal_alignment(iced::alignment::Horizontal::Left) + .into(), + ]) + .on_press(message), + ) + }; + let dropdown_menu = |items| { + widget::container(widget::column::with_children(items)) + .padding(1) + //TODO: move style to libcosmic + .style(theme::Container::custom(|theme| { + let cosmic = theme.cosmic(); + let component = &cosmic.background.component; + widget::container::Appearance { + icon_color: Some(component.on.into()), + text_color: Some(component.on.into()), + background: Some(Background::Color(component.base.into())), + border: Border { + radius: 8.0.into(), + width: 1.0, + color: component.divider.into(), + }, + ..Default::default() + } + })) + .width(Length::Fixed(240.0)) + }; + + let mut input_button = widget::popover( + widget::button(widget::icon::from_name("input-keyboard-symbolic")) + .padding(12.0) + .on_press(Message::DropdownToggle(Dropdown::Keyboard)), + ) + .position(widget::popover::Position::Bottom); + if matches!(self.dropdown_opt, Some(Dropdown::Keyboard)) { + let mut items = Vec::with_capacity(self.active_layouts.len()); + for (i, layout) in self.active_layouts.iter().enumerate() { + items.push(menu_checklist( + &layout.description, + i == 0, + Message::KeyboardLayout(i), + )); + } + input_button = input_button.popup(dropdown_menu(items)); + } + + let mut user_button = widget::popover( + widget::button(widget::icon::from_name("system-users-symbolic")) + .padding(12.0) + .on_press(Message::DropdownToggle(Dropdown::User)), + ) + .position(widget::popover::Position::Bottom); + if matches!(self.dropdown_opt, Some(Dropdown::User)) { + let mut items = Vec::with_capacity(self.usernames.len()); + for (name, full_name) in self.usernames.iter() { + items.push(menu_checklist( + full_name, + name == &self.selected_username, + Message::Username(name.clone()), + )); + } + user_button = user_button.popup(dropdown_menu(items)); + } + + let mut session_button = widget::popover( + widget::button(widget::icon::from_name("application-menu-symbolic")) + .padding(12.0) + .on_press(Message::DropdownToggle(Dropdown::Session)), + ) + .position(widget::popover::Position::Bottom); + if matches!(self.dropdown_opt, Some(Dropdown::Session)) { + let mut items = Vec::with_capacity(self.session_names.len()); + for session_name in self.session_names.iter() { + items.push(menu_checklist( + session_name, + session_name == &self.selected_session, + Message::Session(session_name.clone()), + )); + } + session_button = session_button.popup(dropdown_menu(items)); + } + let button_row = iced::widget::row![ + /*TODO: greeter accessibility options widget::button(widget::icon::from_name( "applications-accessibility-symbolic" )) .padding(12.0) .on_press(Message::None), - widget::button(widget::icon::from_name("input-keyboard-symbolic")) - .padding(12.0) - .on_press(Message::None), - widget::button(widget::icon::from_name("system-users-symbolic")) - .padding(12.0) - .on_press(Message::None), - widget::button(widget::icon::from_name("application-menu-symbolic")) - .padding(12.0) - .on_press(Message::None), - widget::button(widget::icon::from_name("system-suspend-symbolic")) - .padding(12.0) - .on_press(Message::Suspend), - widget::button(widget::icon::from_name("system-reboot-symbolic")) - .padding(12.0) - .on_press(Message::Restart), - widget::button(widget::icon::from_name("system-shutdown-symbolic")) - .padding(12.0) - .on_press(Message::Shutdown), + */ + widget::tooltip( + input_button, + fl!("keyboard-layout"), + widget::tooltip::Position::Top + ), + widget::tooltip(user_button, fl!("user"), widget::tooltip::Position::Top), + widget::tooltip( + session_button, + fl!("session"), + widget::tooltip::Position::Top + ), + widget::tooltip( + widget::button(widget::icon::from_name("system-suspend-symbolic")) + .padding(12.0) + .on_press(Message::Suspend), + fl!("suspend"), + widget::tooltip::Position::Top + ), + widget::tooltip( + widget::button(widget::icon::from_name("system-reboot-symbolic")) + .padding(12.0) + .on_press(Message::Restart), + fl!("restart"), + widget::tooltip::Position::Top + ), + widget::tooltip( + widget::button(widget::icon::from_name("system-shutdown-symbolic")) + .padding(12.0) + .on_press(Message::Shutdown), + fl!("shutdown"), + widget::tooltip::Position::Top + ) ] .padding([16.0, 0.0, 0.0, 0.0]) .spacing(8.0); @@ -893,91 +1180,35 @@ impl cosmic::Application for App { column = column.push(widget::text("Opening GREETD_SOCK")); } SocketState::Open(socket) => { - match &self.username_opt { - Some(username) => { - for user_data in &self.flags.user_datas { - if &user_data.name == username { - match &user_data.icon_opt { - Some(icon) => { - column = column.push( - widget::container( - widget::Image::new( - //TODO: cache handle - widget::image::Handle::from_memory( - icon.clone(), - ), - ) - .width(Length::Fixed(78.0)) - .height(Length::Fixed(78.0)), - ) - .width(Length::Fill) - .align_x(alignment::Horizontal::Center), + for user_data in &self.flags.user_datas { + if &user_data.name == &self.selected_username { + match &user_data.icon_opt { + Some(icon) => { + column = column.push( + widget::container( + widget::Image::new( + //TODO: cache handle + widget::image::Handle::from_memory(icon.clone()), ) - } - None => {} - } - match &user_data.full_name_opt { - Some(full_name) => { - column = column.push( - widget::container(widget::text::title4(full_name)) - .width(Length::Fill) - .align_x(alignment::Horizontal::Center), - ); - } - None => {} - } + .width(Length::Fixed(78.0)) + .height(Length::Fixed(78.0)), + ) + .width(Length::Fill) + .align_x(alignment::Horizontal::Center), + ) } + None => {} } - } - None => { - let mut row = widget::row::with_capacity(self.flags.user_datas.len()) - .spacing(12.0); - for user_data in &self.flags.user_datas { - let mut column = widget::column::with_capacity(2).spacing(12.0); - - match &user_data.icon_opt { - Some(icon) => { - column = column.push( - widget::container( - widget::Image::new( - //TODO: cache handle - widget::image::Handle::from_memory( - icon.clone(), - ), - ) - .width(Length::Fixed(78.0)) - .height(Length::Fixed(78.0)), - ) + match &user_data.full_name_opt { + Some(full_name) => { + column = column.push( + widget::container(widget::text::title4(full_name)) .width(Length::Fill) .align_x(alignment::Horizontal::Center), - ) - } - None => {} + ); } - match &user_data.full_name_opt { - Some(full_name) => { - column = column.push( - widget::container(widget::text::title4(full_name)) - .width(Length::Fill) - .align_x(alignment::Horizontal::Center), - ); - } - None => {} - } - - row = row.push( - widget::MouseArea::new( - widget::layer_container(column) - .layer(cosmic::cosmic_theme::Layer::Primary) - .padding(16) - .style(cosmic::theme::Container::Card), - ) - .on_press( - Message::Username(socket.clone(), user_data.name.clone()), - ), - ); + None => {} } - column = column.push(row); } } match &self.prompt_opt { @@ -1036,15 +1267,6 @@ impl cosmic::Application for App { column = column.push(widget::text(error)); } - column = column.push( - //TODO: use button - iced::widget::pick_list( - &self.session_names, - Some(self.selected_session.clone()), - Message::Session, - ), - ); - widget::container(column) .align_x(alignment::Horizontal::Center) .width(Length::Fill) @@ -1091,7 +1313,7 @@ impl cosmic::Application for App { popover .popup( widget::dialog(fl!("restart-now")) - .icon(widget::icon::from_name("system-restart-symbolic").size(64)) + .icon(widget::icon::from_name("system-reboot-symbolic").size(64)) .body(fl!("restart-timeout", seconds = remaining.as_secs())) .primary_action( widget::button::suggested(fl!("restart")) diff --git a/src/locker.rs b/src/locker.rs index f68b867..e717ef7 100644 --- a/src/locker.rs +++ b/src/locker.rs @@ -33,6 +33,8 @@ use tokio::{sync::mpsc, task, time}; use wayland_client::{protocol::wl_output::WlOutput, Proxy}; pub fn main(current_user: pwd::Passwd) -> Result<(), Box> { + env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("warn")).init(); + crate::localize::localize(); //TODO: use accountsservice diff --git a/src/main.rs b/src/main.rs index 0a8330f..7986e20 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,8 +4,6 @@ use cosmic_greeter::{greeter, locker}; fn main() -> Result<(), Box> { - env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("warn")).init(); - match pwd::Passwd::current_user() { Some(current_user) => match current_user.name.as_str() { "cosmic-greeter" => greeter::main(),