Add greeter keyboard and user selection (#55)

* Add greeter keyboard and user selection

Fixes #37
Fixes #38

* Support switching users and add tooltips to icon buttons

* Implement switching users

* Implement keyboard layout switching

* Ensure that user's xkb_config is used
This commit is contained in:
Jeremy Soller 2024-06-04 22:17:44 -06:00 committed by GitHub
parent 4653bb1de9
commit f6ccf0146e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 481 additions and 148 deletions

77
Cargo.lock generated
View file

@ -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"

View file

@ -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

View file

@ -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

View file

@ -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<Vec<u8>>,
pub theme_opt: Option<Theme>,
pub wallpapers_opt: Option<Vec<(String, WallpaperData)>>,
pub xkb_config_opt: Option<XkbConfig>,
}
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]

View file

@ -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()))?;

1
debian/control vendored
View file

@ -7,6 +7,7 @@ Build-Depends:
git,
just (>= 1.13.0),
libclang-dev,
libinput-dev,
libpam-dev,
libwayland-dev,
libxkbcommon-dev,

View file

@ -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

View file

@ -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<UserData> {
icon_opt,
theme_opt: None,
wallpapers_opt: None,
xkb_config_opt: None,
}
})
.collect()
@ -112,6 +115,8 @@ fn user_data_fallback() -> Vec<UserData> {
}
pub fn main() -> Result<(), Box<dyn Error>> {
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<dyn Error>> {
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<Mutex<UnixStream>>, request: Request) -> Command<
pub struct Flags {
user_datas: Vec<UserData>,
sessions: HashMap<String, Vec<String>>,
layouts_opt: Option<Arc<xkb_data::KeyboardLayouts>>,
comp_config_handler: Option<cosmic_config::Config>,
fallback_background: widget::image::Handle,
}
@ -360,6 +386,13 @@ pub enum SocketState {
Error(Arc<io::Error>),
}
#[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<String>),
Session(String),
Username(Arc<Mutex<UnixStream>>, String),
Username(String),
Auth(Arc<Mutex<UnixStream>>, Option<String>),
Login(Arc<Mutex<UnixStream>>),
Error(Arc<Mutex<UnixStream>>, 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<String>,
usernames: Vec<(String, String)>,
selected_username: String,
prompt_opt: Option<(String, bool, Option<String>)>,
session_names: Vec<String>,
selected_session: String,
active_layouts: Vec<ActiveLayout>,
error_opt: Option<String>,
dialog_page_opt: Option<DialogPage>,
dropdown_opt: Option<Dropdown>,
}
impl App {
fn update_user_config(&mut self) -> Command<Message> {
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<Message> {
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"))

View file

@ -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<dyn std::error::Error>> {
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("warn")).init();
crate::localize::localize();
//TODO: use accountsservice

View file

@ -4,8 +4,6 @@
use cosmic_greeter::{greeter, locker};
fn main() -> Result<(), Box<dyn std::error::Error>> {
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(),