update libcosmic
this uses a subsurface for the greeter menu and a text input that manages its own input string state
This commit is contained in:
parent
5527c9b866
commit
7918485651
9 changed files with 2440 additions and 1464 deletions
123
src/locker.rs
123
src/locker.rs
|
|
@ -1,7 +1,8 @@
|
|||
// Copyright 2023 System76 <info@system76.com>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use cosmic::app::{message, Command, Core, Settings};
|
||||
use cosmic::app::{message, Core, Settings, Task};
|
||||
use cosmic::surface_message::{MessageWrapper, SurfaceMessage, SurfaceMessageHandler};
|
||||
use cosmic::{
|
||||
executor,
|
||||
iced::{
|
||||
|
|
@ -11,8 +12,9 @@ use cosmic::{
|
|||
wayland::{Event as WaylandEvent, OutputEvent, SessionLockEvent},
|
||||
},
|
||||
futures::{self, SinkExt},
|
||||
subscription,
|
||||
wayland::session_lock::{destroy_lock_surface, get_lock_surface, lock, unlock},
|
||||
platform_specific::shell::wayland::commands::session_lock::{
|
||||
destroy_lock_surface, get_lock_surface, lock, unlock,
|
||||
},
|
||||
Length, Subscription,
|
||||
},
|
||||
iced_runtime::core::window::Id as SurfaceId,
|
||||
|
|
@ -48,7 +50,7 @@ pub fn main(current_user: pwd::Passwd) -> Result<(), Box<dyn std::error::Error>>
|
|||
let icon_path = Path::new("/var/lib/AccountsService/icons").join(¤t_user.name);
|
||||
let icon_opt = if icon_path.is_file() {
|
||||
match fs::read(&icon_path) {
|
||||
Ok(icon_data) => Some(widget::image::Handle::from_memory(icon_data)),
|
||||
Ok(icon_data) => Some(widget::image::Handle::from_bytes(icon_data)),
|
||||
Err(err) => {
|
||||
log::error!("failed to read {:?}: {:?}", icon_path, err);
|
||||
None
|
||||
|
|
@ -119,20 +121,24 @@ impl Conversation {
|
|||
log::error!("failed to convert prompt to UTF-8: {:?}", err);
|
||||
pam_client::ErrorCode::CONV_ERR
|
||||
})?;
|
||||
|
||||
futures::executor::block_on(async {
|
||||
self.msg_tx
|
||||
.send(Message::Prompt(
|
||||
prompt.to_string(),
|
||||
secret,
|
||||
Some(String::new()),
|
||||
))
|
||||
.await
|
||||
})
|
||||
.map_err(|err| {
|
||||
log::error!("failed to send prompt: {:?}", err);
|
||||
pam_client::ErrorCode::CONV_ERR
|
||||
})?;
|
||||
let runtime = tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap();
|
||||
runtime
|
||||
.block_on(async {
|
||||
self.msg_tx
|
||||
.send(Message::Prompt(
|
||||
prompt.to_string(),
|
||||
secret,
|
||||
Some(String::new()),
|
||||
))
|
||||
.await
|
||||
})
|
||||
.map_err(|err| {
|
||||
log::error!("failed to send prompt: {:?}", err);
|
||||
pam_client::ErrorCode::CONV_ERR
|
||||
})?;
|
||||
|
||||
let value = self.value_rx.blocking_recv().ok_or_else(|| {
|
||||
log::error!("failed to receive value: channel closed");
|
||||
|
|
@ -201,6 +207,21 @@ pub struct Flags {
|
|||
wallpapers: Vec<(String, cosmic_bg_config::Source)>,
|
||||
}
|
||||
|
||||
impl SurfaceMessageHandler for Message {
|
||||
fn to_surface_message(self) -> MessageWrapper<Self> {
|
||||
match self {
|
||||
Message::Surface(msg) => MessageWrapper::Surface(msg),
|
||||
msg => MessageWrapper::Message(msg),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SurfaceMessage> for Message {
|
||||
fn from(value: SurfaceMessage) -> Self {
|
||||
Message::Surface(value)
|
||||
}
|
||||
}
|
||||
|
||||
/// Messages that are used specifically by our [`App`].
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Message {
|
||||
|
|
@ -214,6 +235,7 @@ pub enum Message {
|
|||
PowerInfo(Option<(String, f64)>),
|
||||
Prompt(String, bool, Option<String>),
|
||||
Submit,
|
||||
Surface(SurfaceMessage),
|
||||
Suspend,
|
||||
Error(String),
|
||||
Lock,
|
||||
|
|
@ -267,7 +289,7 @@ impl App {
|
|||
cosmic_bg_config::Source::Path(path) => {
|
||||
match fs::read(path) {
|
||||
Ok(bytes) => {
|
||||
let image = widget::image::Handle::from_memory(bytes);
|
||||
let image = widget::image::Handle::from_bytes(bytes);
|
||||
self.surface_images.insert(*surface_id, image);
|
||||
//TODO: what to do about duplicates?
|
||||
break;
|
||||
|
|
@ -316,7 +338,7 @@ impl cosmic::Application for App {
|
|||
}
|
||||
|
||||
/// Creates the application, and optionally emits command on initialize.
|
||||
fn init(mut core: Core, flags: Self::Flags) -> (Self, Command<Self::Message>) {
|
||||
fn init(mut core: Core, flags: Self::Flags) -> (Self, Task<Self::Message>) {
|
||||
core.window.show_window_menu = false;
|
||||
core.window.show_headerbar = false;
|
||||
core.window.sharp_corners = true;
|
||||
|
|
@ -354,7 +376,7 @@ impl cosmic::Application for App {
|
|||
lock()
|
||||
} else {
|
||||
// When logind feature is used, wait for lock signal
|
||||
Command::none()
|
||||
Task::none()
|
||||
}
|
||||
} else {
|
||||
// When logind feature not used, lock immediately
|
||||
|
|
@ -367,7 +389,7 @@ impl cosmic::Application for App {
|
|||
}
|
||||
|
||||
/// Handle application events here.
|
||||
fn update(&mut self, message: Self::Message) -> Command<Self::Message> {
|
||||
fn update(&mut self, message: Self::Message) -> Task<Self::Message> {
|
||||
match message {
|
||||
Message::None => {}
|
||||
Message::OutputEvent(output_event, output) => {
|
||||
|
|
@ -376,6 +398,7 @@ impl cosmic::Application for App {
|
|||
log::info!("output {}: created", output.id());
|
||||
|
||||
let surface_id = SurfaceId::unique();
|
||||
|
||||
match self.surface_ids.insert(output.clone(), surface_id) {
|
||||
Some(old_surface_id) => {
|
||||
//TODO: remove old surface?
|
||||
|
|
@ -384,6 +407,7 @@ impl cosmic::Application for App {
|
|||
output.id(),
|
||||
old_surface_id
|
||||
);
|
||||
return Task::none();
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
|
|
@ -409,7 +433,7 @@ impl cosmic::Application for App {
|
|||
.insert(surface_id, text_input_id.clone());
|
||||
|
||||
if matches!(self.state, State::Locked) {
|
||||
return Command::batch([
|
||||
return Task::batch([
|
||||
get_lock_surface(surface_id, output),
|
||||
widget::text_input::focus(text_input_id),
|
||||
]);
|
||||
|
|
@ -454,7 +478,7 @@ impl cosmic::Application for App {
|
|||
for (output, surface_id) in self.surface_ids.iter() {
|
||||
commands.push(get_lock_surface(*surface_id, output.clone()));
|
||||
}
|
||||
return Command::batch(commands);
|
||||
return Task::batch(commands);
|
||||
}
|
||||
SessionLockEvent::Unlocked => {
|
||||
log::info!("session unlocked");
|
||||
|
|
@ -504,7 +528,7 @@ impl cosmic::Application for App {
|
|||
Some(value_tx) => {
|
||||
// Clear errors
|
||||
self.error_opt = None;
|
||||
return cosmic::command::future(async move {
|
||||
return cosmic::task::future(async move {
|
||||
value_tx.send(value).await.unwrap();
|
||||
Message::Channel(value_tx)
|
||||
});
|
||||
|
|
@ -517,7 +541,7 @@ impl cosmic::Application for App {
|
|||
},
|
||||
Message::Suspend => {
|
||||
#[cfg(feature = "logind")]
|
||||
return cosmic::command::future(async move {
|
||||
return cosmic::task::future(async move {
|
||||
match crate::logind::suspend().await {
|
||||
Ok(()) => message::none(),
|
||||
Err(err) => {
|
||||
|
|
@ -577,7 +601,7 @@ impl cosmic::Application for App {
|
|||
// Tell compositor to unlock
|
||||
commands.push(unlock());
|
||||
// Wait to exit until `Unlocked` event, when server has processed unlock
|
||||
return Command::batch(commands);
|
||||
return Task::batch(commands);
|
||||
}
|
||||
State::Locking => {
|
||||
log::info!("session still locking");
|
||||
|
|
@ -587,8 +611,9 @@ impl cosmic::Application for App {
|
|||
}
|
||||
}
|
||||
}
|
||||
Message::Surface(_) => {}
|
||||
}
|
||||
Command::none()
|
||||
Task::none()
|
||||
}
|
||||
|
||||
// Not used for layer surface window
|
||||
|
|
@ -607,13 +632,13 @@ impl cosmic::Application for App {
|
|||
|
||||
let date = dt.format_localized("%A, %B %-d", locale);
|
||||
column = column
|
||||
.push(widget::text::title2(format!("{}", date)).style(style::Text::Accent));
|
||||
.push(widget::text::title2(format!("{}", date)).class(style::Text::Accent));
|
||||
|
||||
let time = dt.format_localized("%R", locale);
|
||||
column = column.push(
|
||||
widget::text(format!("{}", time))
|
||||
.size(112.0)
|
||||
.style(style::Text::Accent),
|
||||
.class(style::Text::Accent),
|
||||
);
|
||||
|
||||
column
|
||||
|
|
@ -634,18 +659,18 @@ impl cosmic::Application for App {
|
|||
|
||||
//TODO: implement these buttons
|
||||
let button_row = iced::widget::row![
|
||||
widget::button(widget::icon::from_name(
|
||||
widget::button::custom(widget::icon::from_name(
|
||||
"applications-accessibility-symbolic"
|
||||
))
|
||||
.padding(12.0)
|
||||
.on_press(Message::None),
|
||||
widget::button(widget::icon::from_name("input-keyboard-symbolic"))
|
||||
widget::button::custom(widget::icon::from_name("input-keyboard-symbolic"))
|
||||
.padding(12.0)
|
||||
.on_press(Message::None),
|
||||
widget::button(widget::icon::from_name("system-users-symbolic"))
|
||||
widget::button::custom(widget::icon::from_name("system-users-symbolic"))
|
||||
.padding(12.0)
|
||||
.on_press(Message::None),
|
||||
widget::button(widget::icon::from_name("system-suspend-symbolic"))
|
||||
widget::button::custom(widget::icon::from_name("system-suspend-symbolic"))
|
||||
.padding(12.0)
|
||||
.on_press(Message::Suspend),
|
||||
]
|
||||
|
|
@ -714,7 +739,7 @@ impl cosmic::Application for App {
|
|||
*secret,
|
||||
)
|
||||
.on_input(|value| Message::Prompt(prompt.clone(), *secret, Some(value)))
|
||||
.on_submit(Message::Submit);
|
||||
.on_submit(|_| Message::Submit);
|
||||
|
||||
if let Some(text_input_id) = self.text_input_ids.get(&surface_id) {
|
||||
text_input = text_input.id(text_input_id.clone());
|
||||
|
|
@ -746,18 +771,18 @@ impl cosmic::Application for App {
|
|||
widget::container(
|
||||
widget::layer_container(
|
||||
iced::widget::row![left_element, right_element]
|
||||
.align_items(alignment::Alignment::Center),
|
||||
.align_y(alignment::Alignment::Center),
|
||||
)
|
||||
.layer(cosmic::cosmic_theme::Layer::Background)
|
||||
.padding(16)
|
||||
.style(cosmic::theme::Container::Custom(Box::new(
|
||||
.class(cosmic::theme::Container::Custom(Box::new(
|
||||
|theme: &cosmic::Theme| {
|
||||
// Use background appearance as the base
|
||||
let mut appearance = widget::container::StyleSheet::appearance(
|
||||
let mut appearance = widget::container::Catalog::style(
|
||||
theme,
|
||||
&cosmic::theme::Container::Background,
|
||||
);
|
||||
appearance.border = iced::Border::with_radius(16.0);
|
||||
appearance.border = iced::Border::default().rounded(16.0);
|
||||
appearance
|
||||
},
|
||||
)))
|
||||
|
|
@ -768,12 +793,12 @@ impl cosmic::Application for App {
|
|||
.height(Length::Fill)
|
||||
.align_x(alignment::Horizontal::Center)
|
||||
.align_y(alignment::Vertical::Top)
|
||||
.style(cosmic::theme::Container::Transparent),
|
||||
.class(cosmic::theme::Container::Transparent),
|
||||
)
|
||||
.image(match self.surface_images.get(&surface_id) {
|
||||
Some(some) => some.clone(),
|
||||
//TODO: default image
|
||||
None => widget::image::Handle::from_pixels(1, 1, vec![0x00, 0x00, 0x00, 0xFF]),
|
||||
None => widget::image::Handle::from_rgba(1, 1, vec![0x00, 0x00, 0x00, 0xFF]),
|
||||
})
|
||||
.content_fit(iced::ContentFit::Cover)
|
||||
.into()
|
||||
|
|
@ -782,7 +807,7 @@ impl cosmic::Application for App {
|
|||
fn subscription(&self) -> Subscription<Self::Message> {
|
||||
let mut subscriptions = Vec::with_capacity(7);
|
||||
|
||||
subscriptions.push(event::listen_with(|event, _| match event {
|
||||
subscriptions.push(event::listen_with(|event, _, _| match event {
|
||||
iced::Event::PlatformSpecific(iced::event::PlatformSpecific::Wayland(
|
||||
wayland_event,
|
||||
)) => match wayland_event {
|
||||
|
|
@ -812,26 +837,24 @@ impl cosmic::Application for App {
|
|||
|
||||
if matches!(self.state, State::Locked) {
|
||||
struct HeartbeatSubscription;
|
||||
subscriptions.push(subscription::channel(
|
||||
subscriptions.push(Subscription::run_with_id(
|
||||
TypeId::of::<HeartbeatSubscription>(),
|
||||
16,
|
||||
|mut msg_tx| async move {
|
||||
cosmic::iced_futures::stream::channel(16, |mut msg_tx| async move {
|
||||
loop {
|
||||
// Send heartbeat once a second to update time
|
||||
//TODO: only send this when needed
|
||||
msg_tx.send(Message::None).await.unwrap();
|
||||
time::sleep(time::Duration::new(1, 0)).await;
|
||||
}
|
||||
},
|
||||
}),
|
||||
));
|
||||
|
||||
struct PamSubscription;
|
||||
//TODO: how to avoid cloning this on every time subscription is called?
|
||||
let username = self.flags.current_user.name.clone();
|
||||
subscriptions.push(subscription::channel(
|
||||
subscriptions.push(Subscription::run_with_id(
|
||||
TypeId::of::<PamSubscription>(),
|
||||
16,
|
||||
|mut msg_tx| async move {
|
||||
cosmic::iced_futures::stream::channel(16, |mut msg_tx| async move {
|
||||
loop {
|
||||
let (value_tx, value_rx) = mpsc::channel(16);
|
||||
msg_tx.send(Message::Channel(value_tx)).await.unwrap();
|
||||
|
|
@ -862,7 +885,7 @@ impl cosmic::Application for App {
|
|||
loop {
|
||||
time::sleep(time::Duration::new(60, 0)).await;
|
||||
}
|
||||
},
|
||||
}),
|
||||
));
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue