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:
parent
4653bb1de9
commit
f6ccf0146e
10 changed files with 481 additions and 148 deletions
77
Cargo.lock
generated
77
Cargo.lock
generated
|
|
@ -915,6 +915,16 @@ dependencies = [
|
||||||
"wayland-client",
|
"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]]
|
[[package]]
|
||||||
name = "cosmic-config"
|
name = "cosmic-config"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
@ -964,6 +974,7 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"cosmic-bg-config",
|
"cosmic-bg-config",
|
||||||
|
"cosmic-comp-config",
|
||||||
"cosmic-config",
|
"cosmic-config",
|
||||||
"cosmic-dbus-networkmanager",
|
"cosmic-dbus-networkmanager",
|
||||||
"cosmic-greeter-daemon",
|
"cosmic-greeter-daemon",
|
||||||
|
|
@ -985,6 +996,7 @@ dependencies = [
|
||||||
"upower_dbus",
|
"upower_dbus",
|
||||||
"wayland-client",
|
"wayland-client",
|
||||||
"xdg",
|
"xdg",
|
||||||
|
"xkb-data",
|
||||||
"zbus 4.2.2",
|
"zbus 4.2.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -993,6 +1005,7 @@ name = "cosmic-greeter-daemon"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cosmic-bg-config",
|
"cosmic-bg-config",
|
||||||
|
"cosmic-comp-config",
|
||||||
"cosmic-config",
|
"cosmic-config",
|
||||||
"cosmic-theme",
|
"cosmic-theme",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
|
|
@ -2583,6 +2596,26 @@ dependencies = [
|
||||||
"libc",
|
"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]]
|
[[package]]
|
||||||
name = "instant"
|
name = "instant"
|
||||||
version = "0.1.13"
|
version = "0.1.13"
|
||||||
|
|
@ -2834,6 +2867,16 @@ dependencies = [
|
||||||
"libc",
|
"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]]
|
[[package]]
|
||||||
name = "linux-raw-sys"
|
name = "linux-raw-sys"
|
||||||
version = "0.3.8"
|
version = "0.3.8"
|
||||||
|
|
@ -4100,6 +4143,18 @@ dependencies = [
|
||||||
"serde_derive",
|
"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]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.203"
|
version = "1.0.203"
|
||||||
|
|
@ -4738,6 +4793,18 @@ version = "1.17.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
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]]
|
[[package]]
|
||||||
name = "uds_windows"
|
name = "uds_windows"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
|
@ -5551,6 +5618,16 @@ dependencies = [
|
||||||
"winapi",
|
"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]]
|
[[package]]
|
||||||
name = "xkbcommon"
|
name = "xkbcommon"
|
||||||
version = "0.7.0"
|
version = "0.7.0"
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ edition = "2021"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chrono = "0.4.31"
|
chrono = "0.4.31"
|
||||||
cosmic-bg-config.workspace = true
|
cosmic-bg-config.workspace = true
|
||||||
|
cosmic-comp-config.workspace = true
|
||||||
cosmic-config = { workspace = true, features = ["calloop", "macro"] }
|
cosmic-config = { workspace = true, features = ["calloop", "macro"] }
|
||||||
cosmic-greeter-daemon = { path = "daemon" }
|
cosmic-greeter-daemon = { path = "daemon" }
|
||||||
env_logger.workspace = true
|
env_logger.workspace = true
|
||||||
|
|
@ -16,6 +17,7 @@ pam-client = "0.5.0"
|
||||||
pwd.workspace = true
|
pwd.workspace = true
|
||||||
ron.workspace = true
|
ron.workspace = true
|
||||||
shlex = "1.2.0"
|
shlex = "1.2.0"
|
||||||
|
xkb-data = "0.1"
|
||||||
xdg = "2.5.2"
|
xdg = "2.5.2"
|
||||||
#TODO: reduce features
|
#TODO: reduce features
|
||||||
tokio = { workspace = true, features = ["full"] }
|
tokio = { workspace = true, features = ["full"] }
|
||||||
|
|
@ -64,6 +66,10 @@ zbus = "4"
|
||||||
git = "https://github.com/pop-os/cosmic-bg"
|
git = "https://github.com/pop-os/cosmic-bg"
|
||||||
default-features = false
|
default-features = false
|
||||||
|
|
||||||
|
[workspace.dependencies.cosmic-comp-config]
|
||||||
|
git = "https://github.com/pop-os/cosmic-comp"
|
||||||
|
default-features = false
|
||||||
|
|
||||||
[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
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cosmic-bg-config.workspace = true
|
cosmic-bg-config.workspace = true
|
||||||
|
cosmic-comp-config.workspace = true
|
||||||
cosmic-config.workspace = true
|
cosmic-config.workspace = true
|
||||||
cosmic-theme.workspace = true
|
cosmic-theme.workspace = true
|
||||||
env_logger.workspace = true
|
env_logger.workspace = true
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
pub use cosmic_bg_config::Color;
|
pub use cosmic_bg_config::Color;
|
||||||
|
pub use cosmic_comp_config::XkbConfig;
|
||||||
pub use cosmic_theme::Theme;
|
pub use cosmic_theme::Theme;
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||||
|
|
@ -9,6 +10,7 @@ pub struct UserData {
|
||||||
pub icon_opt: Option<Vec<u8>>,
|
pub icon_opt: Option<Vec<u8>>,
|
||||||
pub theme_opt: Option<Theme>,
|
pub theme_opt: Option<Theme>,
|
||||||
pub wallpapers_opt: Option<Vec<(String, WallpaperData)>>,
|
pub wallpapers_opt: Option<Vec<(String, WallpaperData)>>,
|
||||||
|
pub xkb_config_opt: Option<XkbConfig>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
use cosmic_bg_config::Source;
|
use cosmic_bg_config::Source;
|
||||||
|
use cosmic_comp_config::CosmicCompConfig;
|
||||||
use cosmic_config::CosmicConfigEntry;
|
use cosmic_config::CosmicConfigEntry;
|
||||||
use cosmic_greeter_daemon::{UserData, WallpaperData};
|
use cosmic_greeter_daemon::{UserData, WallpaperData};
|
||||||
use std::{env, error::Error, fs, future::pending, io, path::Path};
|
use std::{env, error::Error, fs, future::pending, io, path::Path};
|
||||||
|
|
@ -113,6 +114,7 @@ impl GreeterProxy {
|
||||||
theme_opt: None,
|
theme_opt: None,
|
||||||
//TODO: should wallpapers come from a per-user call?
|
//TODO: should wallpapers come from a per-user call?
|
||||||
wallpapers_opt: None,
|
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
|
//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);
|
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()))?;
|
.map_err(|err| GreeterError::RunAsUser(err.to_string()))?;
|
||||||
|
|
||||||
|
|
|
||||||
1
debian/control
vendored
1
debian/control
vendored
|
|
@ -7,6 +7,7 @@ Build-Depends:
|
||||||
git,
|
git,
|
||||||
just (>= 1.13.0),
|
just (>= 1.13.0),
|
||||||
libclang-dev,
|
libclang-dev,
|
||||||
|
libinput-dev,
|
||||||
libpam-dev,
|
libpam-dev,
|
||||||
libwayland-dev,
|
libwayland-dev,
|
||||||
libxkbcommon-dev,
|
libxkbcommon-dev,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,11 @@
|
||||||
cancel = Cancel
|
cancel = Cancel
|
||||||
|
keyboard-layout = Keyboard layout
|
||||||
restart = Restart
|
restart = Restart
|
||||||
restart-now = Restart now?
|
restart-now = Restart now?
|
||||||
restart-timeout = The system will restart automatically in {$seconds} seconds.
|
restart-timeout = The system will restart automatically in {$seconds} seconds.
|
||||||
|
session = Session
|
||||||
shutdown = Shut down
|
shutdown = Shut down
|
||||||
shutdown-now = Shut down now?
|
shutdown-now = Shut down now?
|
||||||
shutdown-timeout = The system will shut down automatically in {$seconds} seconds.
|
shutdown-timeout = The system will shut down automatically in {$seconds} seconds.
|
||||||
|
suspend = Suspend
|
||||||
|
user = User
|
||||||
|
|
|
||||||
514
src/greeter.rs
514
src/greeter.rs
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
use cosmic::app::{message, Command, Core, Settings};
|
use cosmic::app::{message, Command, Core, Settings};
|
||||||
use cosmic::{
|
use cosmic::{
|
||||||
|
cosmic_config::{self, ConfigSet, CosmicConfigEntry},
|
||||||
executor,
|
executor,
|
||||||
iced::{
|
iced::{
|
||||||
self, alignment,
|
self, alignment,
|
||||||
|
|
@ -18,11 +19,12 @@ use cosmic::{
|
||||||
destroy_layer_surface, get_layer_surface, Anchor, KeyboardInteractivity, Layer,
|
destroy_layer_surface, get_layer_surface, Anchor, KeyboardInteractivity, Layer,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Length, Subscription,
|
Background, Border, Length, Subscription,
|
||||||
},
|
},
|
||||||
iced_runtime::core::window::Id as SurfaceId,
|
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 cosmic_greeter_daemon::{UserData, WallpaperData};
|
||||||
use greetd_ipc::{codec::TokioCodec, AuthMessageType, Request, Response};
|
use greetd_ipc::{codec::TokioCodec, AuthMessageType, Request, Response};
|
||||||
use std::{
|
use std::{
|
||||||
|
|
@ -105,6 +107,7 @@ fn user_data_fallback() -> Vec<UserData> {
|
||||||
icon_opt,
|
icon_opt,
|
||||||
theme_opt: None,
|
theme_opt: None,
|
||||||
wallpapers_opt: None,
|
wallpapers_opt: None,
|
||||||
|
xkb_config_opt: None,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
|
|
@ -112,6 +115,8 @@ fn user_data_fallback() -> Vec<UserData> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main() -> Result<(), Box<dyn Error>> {
|
pub fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("warn")).init();
|
||||||
|
|
||||||
crate::localize::localize();
|
crate::localize::localize();
|
||||||
|
|
||||||
let mut user_datas = match futures::executor::block_on(async { user_data_dbus().await }) {
|
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
|
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 =
|
let fallback_background =
|
||||||
widget::image::Handle::from_memory(include_bytes!("../res/background.png"));
|
widget::image::Handle::from_memory(include_bytes!("../res/background.png"));
|
||||||
|
|
||||||
let flags = Flags {
|
let flags = Flags {
|
||||||
user_datas,
|
user_datas,
|
||||||
sessions,
|
sessions,
|
||||||
|
layouts_opt,
|
||||||
|
comp_config_handler,
|
||||||
fallback_background,
|
fallback_background,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -345,6 +369,8 @@ fn request_command(socket: Arc<Mutex<UnixStream>>, request: Request) -> Command<
|
||||||
pub struct Flags {
|
pub struct Flags {
|
||||||
user_datas: Vec<UserData>,
|
user_datas: Vec<UserData>,
|
||||||
sessions: HashMap<String, Vec<String>>,
|
sessions: HashMap<String, Vec<String>>,
|
||||||
|
layouts_opt: Option<Arc<xkb_data::KeyboardLayouts>>,
|
||||||
|
comp_config_handler: Option<cosmic_config::Config>,
|
||||||
fallback_background: widget::image::Handle,
|
fallback_background: widget::image::Handle,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -360,6 +386,13 @@ pub enum SocketState {
|
||||||
Error(Arc<io::Error>),
|
Error(Arc<io::Error>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
|
pub struct ActiveLayout {
|
||||||
|
layout: String,
|
||||||
|
description: String,
|
||||||
|
variant: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub enum DialogPage {
|
pub enum DialogPage {
|
||||||
Restart(Instant),
|
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`].
|
/// Messages that are used specifically by our [`App`].
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum Message {
|
pub enum Message {
|
||||||
|
|
@ -389,12 +430,14 @@ pub enum Message {
|
||||||
PowerInfo(Option<(String, f64)>),
|
PowerInfo(Option<(String, f64)>),
|
||||||
Prompt(String, bool, Option<String>),
|
Prompt(String, bool, Option<String>),
|
||||||
Session(String),
|
Session(String),
|
||||||
Username(Arc<Mutex<UnixStream>>, String),
|
Username(String),
|
||||||
Auth(Arc<Mutex<UnixStream>>, Option<String>),
|
Auth(Arc<Mutex<UnixStream>>, Option<String>),
|
||||||
Login(Arc<Mutex<UnixStream>>),
|
Login(Arc<Mutex<UnixStream>>),
|
||||||
Error(Arc<Mutex<UnixStream>>, String),
|
Error(Arc<Mutex<UnixStream>>, String),
|
||||||
DialogCancel,
|
DialogCancel,
|
||||||
DialogConfirm,
|
DialogConfirm,
|
||||||
|
DropdownToggle(Dropdown),
|
||||||
|
KeyboardLayout(usize),
|
||||||
Reconnect,
|
Reconnect,
|
||||||
Suspend,
|
Suspend,
|
||||||
Restart,
|
Restart,
|
||||||
|
|
@ -415,22 +458,56 @@ pub struct App {
|
||||||
network_icon_opt: Option<&'static str>,
|
network_icon_opt: Option<&'static str>,
|
||||||
power_info_opt: Option<(String, f64)>,
|
power_info_opt: Option<(String, f64)>,
|
||||||
socket_state: SocketState,
|
socket_state: SocketState,
|
||||||
username_opt: Option<String>,
|
usernames: Vec<(String, String)>,
|
||||||
|
selected_username: String,
|
||||||
prompt_opt: Option<(String, bool, Option<String>)>,
|
prompt_opt: Option<(String, bool, Option<String>)>,
|
||||||
session_names: Vec<String>,
|
session_names: Vec<String>,
|
||||||
selected_session: String,
|
selected_session: String,
|
||||||
|
active_layouts: Vec<ActiveLayout>,
|
||||||
error_opt: Option<String>,
|
error_opt: Option<String>,
|
||||||
dialog_page_opt: Option<DialogPage>,
|
dialog_page_opt: Option<DialogPage>,
|
||||||
|
dropdown_opt: Option<Dropdown>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
fn update_user_config(&mut self) -> Command<Message> {
|
fn set_xkb_config(&self) {
|
||||||
let username = match &self.username_opt {
|
let user_data = match self
|
||||||
|
.flags
|
||||||
|
.user_datas
|
||||||
|
.iter()
|
||||||
|
.find(|x| &x.name == &self.selected_username)
|
||||||
|
{
|
||||||
Some(some) => some,
|
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,
|
Some(some) => some,
|
||||||
None => return Command::none(),
|
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 {
|
match &user_data.theme_opt {
|
||||||
Some(theme) => {
|
Some(theme) => {
|
||||||
cosmic::app::command::set_theme(cosmic::Theme::custom(Arc::new(theme.clone())))
|
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();
|
let mut session_names: Vec<_> = flags.sessions.keys().map(|x| x.to_string()).collect();
|
||||||
session_names.sort();
|
session_names.sort();
|
||||||
|
|
||||||
//TODO: determine default session?
|
//TODO: use last selected session
|
||||||
let selected_session = session_names.first().cloned().unwrap_or(String::new());
|
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 {
|
let mut app = App {
|
||||||
core,
|
core,
|
||||||
flags,
|
flags,
|
||||||
|
|
@ -528,12 +671,15 @@ impl cosmic::Application for App {
|
||||||
network_icon_opt: None,
|
network_icon_opt: None,
|
||||||
power_info_opt: None,
|
power_info_opt: None,
|
||||||
socket_state: SocketState::Pending,
|
socket_state: SocketState::Pending,
|
||||||
username_opt: None,
|
usernames,
|
||||||
|
selected_username,
|
||||||
prompt_opt: None,
|
prompt_opt: None,
|
||||||
session_names,
|
session_names,
|
||||||
selected_session,
|
selected_session,
|
||||||
|
active_layouts: Vec::new(),
|
||||||
error_opt: None,
|
error_opt: None,
|
||||||
dialog_page_opt: None,
|
dialog_page_opt: None,
|
||||||
|
dropdown_opt: None,
|
||||||
};
|
};
|
||||||
let command = app.update(Message::Reconnect);
|
let command = app.update(Message::Reconnect);
|
||||||
(app, command)
|
(app, command)
|
||||||
|
|
@ -636,15 +782,13 @@ impl cosmic::Application for App {
|
||||||
self.socket_state = socket_state;
|
self.socket_state = socket_state;
|
||||||
match &self.socket_state {
|
match &self.socket_state {
|
||||||
SocketState::Open(socket) => {
|
SocketState::Open(socket) => {
|
||||||
// When socket is opened, request default user
|
// When socket is opened, send create session
|
||||||
//TODO: choose last used user
|
return Command::batch([request_command(
|
||||||
match self.flags.user_datas.first().map(|x| x.name.clone()) {
|
socket.clone(),
|
||||||
Some(username) => {
|
Request::CreateSession {
|
||||||
let socket = socket.clone();
|
username: self.selected_username.clone(),
|
||||||
return self.update(Message::Username(socket, username));
|
},
|
||||||
}
|
)]);
|
||||||
None => {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
@ -672,15 +816,25 @@ impl cosmic::Application for App {
|
||||||
}
|
}
|
||||||
Message::Session(selected_session) => {
|
Message::Session(selected_session) => {
|
||||||
self.selected_session = selected_session;
|
self.selected_session = selected_session;
|
||||||
|
if self.dropdown_opt == Some(Dropdown::Session) {
|
||||||
|
self.dropdown_opt = None;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Message::Username(socket, username) => {
|
Message::Username(username) => {
|
||||||
self.username_opt = Some(username.clone());
|
if self.dropdown_opt == Some(Dropdown::User) {
|
||||||
self.prompt_opt = None;
|
self.dropdown_opt = None;
|
||||||
self.surface_images.clear();
|
}
|
||||||
return Command::batch([
|
if username != self.selected_username {
|
||||||
self.update_user_config(),
|
self.selected_username = username.clone();
|
||||||
request_command(socket, Request::CreateSession { username }),
|
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) => {
|
Message::Auth(socket, response) => {
|
||||||
self.prompt_opt = None;
|
self.prompt_opt = None;
|
||||||
|
|
@ -708,18 +862,23 @@ impl cosmic::Application for App {
|
||||||
return request_command(socket, Request::CancelSession);
|
return request_command(socket, Request::CancelSession);
|
||||||
}
|
}
|
||||||
Message::Reconnect => {
|
Message::Reconnect => {
|
||||||
return Command::perform(
|
return Command::batch([
|
||||||
async {
|
self.update_user_config(),
|
||||||
message::app(Message::Socket(match env::var_os("GREETD_SOCK") {
|
Command::perform(
|
||||||
Some(socket_path) => match UnixStream::connect(&socket_path).await {
|
async {
|
||||||
Ok(socket) => SocketState::Open(Arc::new(socket.into())),
|
message::app(Message::Socket(match env::var_os("GREETD_SOCK") {
|
||||||
Err(err) => SocketState::Error(Arc::new(err)),
|
Some(socket_path) => {
|
||||||
},
|
match UnixStream::connect(&socket_path).await {
|
||||||
None => SocketState::NotSet,
|
Ok(socket) => SocketState::Open(Arc::new(socket.into())),
|
||||||
}))
|
Err(err) => SocketState::Error(Arc::new(err)),
|
||||||
},
|
}
|
||||||
|x| x,
|
}
|
||||||
);
|
None => SocketState::NotSet,
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
|x| x,
|
||||||
|
),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
Message::DialogCancel => {
|
Message::DialogCancel => {
|
||||||
self.dialog_page_opt = None;
|
self.dialog_page_opt = None;
|
||||||
|
|
@ -757,6 +916,22 @@ impl cosmic::Application for App {
|
||||||
}
|
}
|
||||||
None => {}
|
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 => {
|
Message::Suspend => {
|
||||||
#[cfg(feature = "logind")]
|
#[cfg(feature = "logind")]
|
||||||
return Command::perform(
|
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![
|
let button_row = iced::widget::row![
|
||||||
|
/*TODO: greeter accessibility options
|
||||||
widget::button(widget::icon::from_name(
|
widget::button(widget::icon::from_name(
|
||||||
"applications-accessibility-symbolic"
|
"applications-accessibility-symbolic"
|
||||||
))
|
))
|
||||||
.padding(12.0)
|
.padding(12.0)
|
||||||
.on_press(Message::None),
|
.on_press(Message::None),
|
||||||
widget::button(widget::icon::from_name("input-keyboard-symbolic"))
|
*/
|
||||||
.padding(12.0)
|
widget::tooltip(
|
||||||
.on_press(Message::None),
|
input_button,
|
||||||
widget::button(widget::icon::from_name("system-users-symbolic"))
|
fl!("keyboard-layout"),
|
||||||
.padding(12.0)
|
widget::tooltip::Position::Top
|
||||||
.on_press(Message::None),
|
),
|
||||||
widget::button(widget::icon::from_name("application-menu-symbolic"))
|
widget::tooltip(user_button, fl!("user"), widget::tooltip::Position::Top),
|
||||||
.padding(12.0)
|
widget::tooltip(
|
||||||
.on_press(Message::None),
|
session_button,
|
||||||
widget::button(widget::icon::from_name("system-suspend-symbolic"))
|
fl!("session"),
|
||||||
.padding(12.0)
|
widget::tooltip::Position::Top
|
||||||
.on_press(Message::Suspend),
|
),
|
||||||
widget::button(widget::icon::from_name("system-reboot-symbolic"))
|
widget::tooltip(
|
||||||
.padding(12.0)
|
widget::button(widget::icon::from_name("system-suspend-symbolic"))
|
||||||
.on_press(Message::Restart),
|
.padding(12.0)
|
||||||
widget::button(widget::icon::from_name("system-shutdown-symbolic"))
|
.on_press(Message::Suspend),
|
||||||
.padding(12.0)
|
fl!("suspend"),
|
||||||
.on_press(Message::Shutdown),
|
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])
|
.padding([16.0, 0.0, 0.0, 0.0])
|
||||||
.spacing(8.0);
|
.spacing(8.0);
|
||||||
|
|
@ -893,91 +1180,35 @@ impl cosmic::Application for App {
|
||||||
column = column.push(widget::text("Opening GREETD_SOCK"));
|
column = column.push(widget::text("Opening GREETD_SOCK"));
|
||||||
}
|
}
|
||||||
SocketState::Open(socket) => {
|
SocketState::Open(socket) => {
|
||||||
match &self.username_opt {
|
for user_data in &self.flags.user_datas {
|
||||||
Some(username) => {
|
if &user_data.name == &self.selected_username {
|
||||||
for user_data in &self.flags.user_datas {
|
match &user_data.icon_opt {
|
||||||
if &user_data.name == username {
|
Some(icon) => {
|
||||||
match &user_data.icon_opt {
|
column = column.push(
|
||||||
Some(icon) => {
|
widget::container(
|
||||||
column = column.push(
|
widget::Image::new(
|
||||||
widget::container(
|
//TODO: cache handle
|
||||||
widget::Image::new(
|
widget::image::Handle::from_memory(icon.clone()),
|
||||||
//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),
|
|
||||||
)
|
)
|
||||||
}
|
.width(Length::Fixed(78.0))
|
||||||
None => {}
|
.height(Length::Fixed(78.0)),
|
||||||
}
|
)
|
||||||
match &user_data.full_name_opt {
|
.width(Length::Fill)
|
||||||
Some(full_name) => {
|
.align_x(alignment::Horizontal::Center),
|
||||||
column = column.push(
|
)
|
||||||
widget::container(widget::text::title4(full_name))
|
|
||||||
.width(Length::Fill)
|
|
||||||
.align_x(alignment::Horizontal::Center),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
None => {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
None => {}
|
||||||
}
|
}
|
||||||
}
|
match &user_data.full_name_opt {
|
||||||
None => {
|
Some(full_name) => {
|
||||||
let mut row = widget::row::with_capacity(self.flags.user_datas.len())
|
column = column.push(
|
||||||
.spacing(12.0);
|
widget::container(widget::text::title4(full_name))
|
||||||
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)),
|
|
||||||
)
|
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
.align_x(alignment::Horizontal::Center),
|
.align_x(alignment::Horizontal::Center),
|
||||||
)
|
);
|
||||||
}
|
|
||||||
None => {}
|
|
||||||
}
|
}
|
||||||
match &user_data.full_name_opt {
|
None => {}
|
||||||
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()),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
column = column.push(row);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
match &self.prompt_opt {
|
match &self.prompt_opt {
|
||||||
|
|
@ -1036,15 +1267,6 @@ impl cosmic::Application for App {
|
||||||
column = column.push(widget::text(error));
|
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)
|
widget::container(column)
|
||||||
.align_x(alignment::Horizontal::Center)
|
.align_x(alignment::Horizontal::Center)
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
|
|
@ -1091,7 +1313,7 @@ impl cosmic::Application for App {
|
||||||
popover
|
popover
|
||||||
.popup(
|
.popup(
|
||||||
widget::dialog(fl!("restart-now"))
|
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()))
|
.body(fl!("restart-timeout", seconds = remaining.as_secs()))
|
||||||
.primary_action(
|
.primary_action(
|
||||||
widget::button::suggested(fl!("restart"))
|
widget::button::suggested(fl!("restart"))
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,8 @@ use tokio::{sync::mpsc, task, time};
|
||||||
use wayland_client::{protocol::wl_output::WlOutput, Proxy};
|
use wayland_client::{protocol::wl_output::WlOutput, Proxy};
|
||||||
|
|
||||||
pub fn main(current_user: pwd::Passwd) -> Result<(), Box<dyn std::error::Error>> {
|
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();
|
crate::localize::localize();
|
||||||
|
|
||||||
//TODO: use accountsservice
|
//TODO: use accountsservice
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,6 @@
|
||||||
use cosmic_greeter::{greeter, locker};
|
use cosmic_greeter::{greeter, locker};
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
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() {
|
match pwd::Passwd::current_user() {
|
||||||
Some(current_user) => match current_user.name.as_str() {
|
Some(current_user) => match current_user.name.as_str() {
|
||||||
"cosmic-greeter" => greeter::main(),
|
"cosmic-greeter" => greeter::main(),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue