refactor: lock surface subsurface menu
This commit is contained in:
parent
b84ee1827c
commit
d6b94406e6
5 changed files with 573 additions and 616 deletions
30
Cargo.lock
generated
30
Cargo.lock
generated
|
|
@ -1046,7 +1046,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cosmic-config"
|
name = "cosmic-config"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/pop-os/libcosmic//?branch=drop-menu-tree-changes#4c78a5620218a67f6653b171e9136f4bd9e89bf1"
|
source = "git+https://github.com/pop-os/libcosmic//?branch=drop-menu-tree-changes#5976b48f677649067d1240f6386d153261204278"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atomicwrites",
|
"atomicwrites",
|
||||||
"calloop 0.14.2",
|
"calloop 0.14.2",
|
||||||
|
|
@ -1069,7 +1069,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cosmic-config-derive"
|
name = "cosmic-config-derive"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/pop-os/libcosmic//?branch=drop-menu-tree-changes#4c78a5620218a67f6653b171e9136f4bd9e89bf1"
|
source = "git+https://github.com/pop-os/libcosmic//?branch=drop-menu-tree-changes#5976b48f677649067d1240f6386d153261204278"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"syn 1.0.109",
|
"syn 1.0.109",
|
||||||
|
|
@ -1212,7 +1212,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cosmic-theme"
|
name = "cosmic-theme"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/pop-os/libcosmic//?branch=drop-menu-tree-changes#4c78a5620218a67f6653b171e9136f4bd9e89bf1"
|
source = "git+https://github.com/pop-os/libcosmic//?branch=drop-menu-tree-changes#5976b48f677649067d1240f6386d153261204278"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"almost",
|
"almost",
|
||||||
"cosmic-config",
|
"cosmic-config",
|
||||||
|
|
@ -2487,7 +2487,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "iced"
|
name = "iced"
|
||||||
version = "0.14.0-dev"
|
version = "0.14.0-dev"
|
||||||
source = "git+https://github.com/pop-os/libcosmic//?branch=drop-menu-tree-changes#4c78a5620218a67f6653b171e9136f4bd9e89bf1"
|
source = "git+https://github.com/pop-os/libcosmic//?branch=drop-menu-tree-changes#5976b48f677649067d1240f6386d153261204278"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dnd",
|
"dnd",
|
||||||
"iced_accessibility",
|
"iced_accessibility",
|
||||||
|
|
@ -2505,7 +2505,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "iced_accessibility"
|
name = "iced_accessibility"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/pop-os/libcosmic//?branch=drop-menu-tree-changes#4c78a5620218a67f6653b171e9136f4bd9e89bf1"
|
source = "git+https://github.com/pop-os/libcosmic//?branch=drop-menu-tree-changes#5976b48f677649067d1240f6386d153261204278"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"accesskit",
|
"accesskit",
|
||||||
"accesskit_winit",
|
"accesskit_winit",
|
||||||
|
|
@ -2514,7 +2514,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "iced_core"
|
name = "iced_core"
|
||||||
version = "0.14.0-dev"
|
version = "0.14.0-dev"
|
||||||
source = "git+https://github.com/pop-os/libcosmic//?branch=drop-menu-tree-changes#4c78a5620218a67f6653b171e9136f4bd9e89bf1"
|
source = "git+https://github.com/pop-os/libcosmic//?branch=drop-menu-tree-changes#5976b48f677649067d1240f6386d153261204278"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.8.0",
|
"bitflags 2.8.0",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
|
@ -2538,7 +2538,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "iced_futures"
|
name = "iced_futures"
|
||||||
version = "0.14.0-dev"
|
version = "0.14.0-dev"
|
||||||
source = "git+https://github.com/pop-os/libcosmic//?branch=drop-menu-tree-changes#4c78a5620218a67f6653b171e9136f4bd9e89bf1"
|
source = "git+https://github.com/pop-os/libcosmic//?branch=drop-menu-tree-changes#5976b48f677649067d1240f6386d153261204278"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures",
|
"futures",
|
||||||
"iced_core",
|
"iced_core",
|
||||||
|
|
@ -2564,7 +2564,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "iced_graphics"
|
name = "iced_graphics"
|
||||||
version = "0.14.0-dev"
|
version = "0.14.0-dev"
|
||||||
source = "git+https://github.com/pop-os/libcosmic//?branch=drop-menu-tree-changes#4c78a5620218a67f6653b171e9136f4bd9e89bf1"
|
source = "git+https://github.com/pop-os/libcosmic//?branch=drop-menu-tree-changes#5976b48f677649067d1240f6386d153261204278"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.8.0",
|
"bitflags 2.8.0",
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
|
|
@ -2586,7 +2586,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "iced_renderer"
|
name = "iced_renderer"
|
||||||
version = "0.14.0-dev"
|
version = "0.14.0-dev"
|
||||||
source = "git+https://github.com/pop-os/libcosmic//?branch=drop-menu-tree-changes#4c78a5620218a67f6653b171e9136f4bd9e89bf1"
|
source = "git+https://github.com/pop-os/libcosmic//?branch=drop-menu-tree-changes#5976b48f677649067d1240f6386d153261204278"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"iced_graphics",
|
"iced_graphics",
|
||||||
"iced_tiny_skia",
|
"iced_tiny_skia",
|
||||||
|
|
@ -2598,7 +2598,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "iced_runtime"
|
name = "iced_runtime"
|
||||||
version = "0.14.0-dev"
|
version = "0.14.0-dev"
|
||||||
source = "git+https://github.com/pop-os/libcosmic//?branch=drop-menu-tree-changes#4c78a5620218a67f6653b171e9136f4bd9e89bf1"
|
source = "git+https://github.com/pop-os/libcosmic//?branch=drop-menu-tree-changes#5976b48f677649067d1240f6386d153261204278"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"cosmic-client-toolkit",
|
"cosmic-client-toolkit",
|
||||||
|
|
@ -2613,7 +2613,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "iced_tiny_skia"
|
name = "iced_tiny_skia"
|
||||||
version = "0.14.0-dev"
|
version = "0.14.0-dev"
|
||||||
source = "git+https://github.com/pop-os/libcosmic//?branch=drop-menu-tree-changes#4c78a5620218a67f6653b171e9136f4bd9e89bf1"
|
source = "git+https://github.com/pop-os/libcosmic//?branch=drop-menu-tree-changes#5976b48f677649067d1240f6386d153261204278"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"cosmic-text",
|
"cosmic-text",
|
||||||
|
|
@ -2629,7 +2629,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "iced_wgpu"
|
name = "iced_wgpu"
|
||||||
version = "0.14.0-dev"
|
version = "0.14.0-dev"
|
||||||
source = "git+https://github.com/pop-os/libcosmic//?branch=drop-menu-tree-changes#4c78a5620218a67f6653b171e9136f4bd9e89bf1"
|
source = "git+https://github.com/pop-os/libcosmic//?branch=drop-menu-tree-changes#5976b48f677649067d1240f6386d153261204278"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"as-raw-xcb-connection",
|
"as-raw-xcb-connection",
|
||||||
"bitflags 2.8.0",
|
"bitflags 2.8.0",
|
||||||
|
|
@ -2660,7 +2660,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "iced_widget"
|
name = "iced_widget"
|
||||||
version = "0.14.0-dev"
|
version = "0.14.0-dev"
|
||||||
source = "git+https://github.com/pop-os/libcosmic//?branch=drop-menu-tree-changes#4c78a5620218a67f6653b171e9136f4bd9e89bf1"
|
source = "git+https://github.com/pop-os/libcosmic//?branch=drop-menu-tree-changes#5976b48f677649067d1240f6386d153261204278"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cosmic-client-toolkit",
|
"cosmic-client-toolkit",
|
||||||
"dnd",
|
"dnd",
|
||||||
|
|
@ -2678,7 +2678,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "iced_winit"
|
name = "iced_winit"
|
||||||
version = "0.14.0-dev"
|
version = "0.14.0-dev"
|
||||||
source = "git+https://github.com/pop-os/libcosmic//?branch=drop-menu-tree-changes#4c78a5620218a67f6653b171e9136f4bd9e89bf1"
|
source = "git+https://github.com/pop-os/libcosmic//?branch=drop-menu-tree-changes#5976b48f677649067d1240f6386d153261204278"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cosmic-client-toolkit",
|
"cosmic-client-toolkit",
|
||||||
"dnd",
|
"dnd",
|
||||||
|
|
@ -3144,7 +3144,7 @@ checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libcosmic"
|
name = "libcosmic"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/pop-os/libcosmic//?branch=drop-menu-tree-changes#4c78a5620218a67f6653b171e9136f4bd9e89bf1"
|
source = "git+https://github.com/pop-os/libcosmic//?branch=drop-menu-tree-changes#5976b48f677649067d1240f6386d153261204278"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"apply",
|
"apply",
|
||||||
"ashpd 0.9.2",
|
"ashpd 0.9.2",
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ mod ipc;
|
||||||
|
|
||||||
use cosmic::app::{Core, Settings, Task};
|
use cosmic::app::{Core, Settings, Task};
|
||||||
use cosmic::cctk::wayland_protocols::xdg::shell::client::xdg_positioner::Gravity;
|
use cosmic::cctk::wayland_protocols::xdg::shell::client::xdg_positioner::Gravity;
|
||||||
use cosmic::iced::{Point, Rectangle, Size};
|
use cosmic::iced::{Point, Size};
|
||||||
use cosmic::iced_core::{image, window};
|
use cosmic::iced_core::{image, window};
|
||||||
use cosmic::iced_runtime::platform_specific::wayland::subsurface::SctkSubsurfaceSettings;
|
use cosmic::iced_runtime::platform_specific::wayland::subsurface::SctkSubsurfaceSettings;
|
||||||
use cosmic::surface;
|
use cosmic::surface;
|
||||||
|
|
@ -400,11 +400,11 @@ pub enum Message {
|
||||||
DropdownToggle(Dropdown),
|
DropdownToggle(Dropdown),
|
||||||
Error(String),
|
Error(String),
|
||||||
Exit,
|
Exit,
|
||||||
|
Focus(SurfaceId),
|
||||||
// Sets channel used to communicate with the greetd IPC subscription.
|
// Sets channel used to communicate with the greetd IPC subscription.
|
||||||
GreetdChannel(tokio::sync::mpsc::Sender<Request>),
|
GreetdChannel(tokio::sync::mpsc::Sender<Request>),
|
||||||
Heartbeat,
|
Heartbeat,
|
||||||
KeyboardLayout(usize),
|
KeyboardLayout(usize),
|
||||||
LayerEvent(LayerEvent, SurfaceId),
|
|
||||||
Login,
|
Login,
|
||||||
NetworkIcon(Option<&'static str>),
|
NetworkIcon(Option<&'static str>),
|
||||||
None,
|
None,
|
||||||
|
|
@ -419,7 +419,6 @@ pub enum Message {
|
||||||
Surface(surface::Action),
|
Surface(surface::Action),
|
||||||
Suspend,
|
Suspend,
|
||||||
Username(String),
|
Username(String),
|
||||||
WindowOpen(SurfaceId),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The [`App`] stores application-specific state.
|
/// The [`App`] stores application-specific state.
|
||||||
|
|
@ -718,9 +717,6 @@ impl App {
|
||||||
)
|
)
|
||||||
.id(text_input_id)
|
.id(text_input_id)
|
||||||
.manage_value(true)
|
.manage_value(true)
|
||||||
// .on_input(|value| {
|
|
||||||
// Message::Prompt(prompt.clone(), *secret, Some(value))
|
|
||||||
// })
|
|
||||||
.on_submit(|v| Message::Auth(Some(v)));
|
.on_submit(|v| Message::Auth(Some(v)));
|
||||||
|
|
||||||
if let Some(text_input_id) = self
|
if let Some(text_input_id) = self
|
||||||
|
|
@ -1141,13 +1137,16 @@ impl cosmic::Application for App {
|
||||||
Size::new(800., unwrapped_size.1 as f32 - 32.),
|
Size::new(800., unwrapped_size.1 as f32 - 32.),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
(Point::ORIGIN, Size::new(1920., 1080.))
|
(
|
||||||
|
Point::new(0., 32.),
|
||||||
|
Size::new(unwrapped_size.0 as f32, unwrapped_size.1 as f32 - 32.),
|
||||||
|
)
|
||||||
};
|
};
|
||||||
self.window_size.insert(
|
self.window_size.insert(
|
||||||
surface_id,
|
surface_id,
|
||||||
Size::new(unwrapped_size.0 as f32, unwrapped_size.1 as f32),
|
Size::new(unwrapped_size.0 as f32, unwrapped_size.1 as f32),
|
||||||
);
|
);
|
||||||
dbg!(loc, size);
|
|
||||||
let msg = cosmic::surface::action::subsurface(
|
let msg = cosmic::surface::action::subsurface(
|
||||||
move |_: &mut App| SctkSubsurfaceSettings {
|
move |_: &mut App| SctkSubsurfaceSettings {
|
||||||
parent: surface_id,
|
parent: surface_id,
|
||||||
|
|
@ -1209,20 +1208,6 @@ impl cosmic::Application for App {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Message::LayerEvent(layer_event, surface_id) => match layer_event {
|
|
||||||
LayerEvent::Focused => {
|
|
||||||
log::info!("focus surface {:?}", surface_id);
|
|
||||||
self.active_surface_id_opt = Some(surface_id);
|
|
||||||
if let Some(text_input_id) = self
|
|
||||||
.surface_names
|
|
||||||
.get(&surface_id)
|
|
||||||
.and_then(|id| self.text_input_ids.get(id))
|
|
||||||
{
|
|
||||||
return widget::text_input::focus(text_input_id.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
},
|
|
||||||
Message::Socket(socket_state) => {
|
Message::Socket(socket_state) => {
|
||||||
self.socket_state = socket_state;
|
self.socket_state = socket_state;
|
||||||
match &self.socket_state {
|
match &self.socket_state {
|
||||||
|
|
@ -1481,22 +1466,17 @@ impl cosmic::Application for App {
|
||||||
cosmic::app::Action::Surface(a),
|
cosmic::app::Action::Surface(a),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
Message::WindowOpen(id) if self.surface_ids.values().any(|i| *i == id) => {
|
|
||||||
if let Some(text_input_id) = self.surface_names.get(&id).and_then(|id| {
|
Message::Focus(surface_id) => {
|
||||||
if self
|
self.active_surface_id_opt = Some(surface_id);
|
||||||
.active_surface_id_opt
|
if let Some(text_input_id) = self
|
||||||
.and_then(|active_id| self.surface_names.get(&active_id))
|
.surface_names
|
||||||
== Some(id)
|
.get(&surface_id)
|
||||||
|
.and_then(|id| self.text_input_ids.get(id))
|
||||||
{
|
{
|
||||||
self.text_input_ids.get(id)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}) {
|
|
||||||
return widget::text_input::focus(text_input_id.clone());
|
return widget::text_input::focus(text_input_id.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Message::WindowOpen(_) => {}
|
|
||||||
}
|
}
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
|
|
@ -1530,12 +1510,10 @@ impl cosmic::Application for App {
|
||||||
WaylandEvent::Output(output_event, output) => {
|
WaylandEvent::Output(output_event, output) => {
|
||||||
Some(Message::OutputEvent(output_event, output))
|
Some(Message::OutputEvent(output_event, output))
|
||||||
}
|
}
|
||||||
WaylandEvent::Layer(layer_event, _surface, surface_id) => {
|
|
||||||
Some(Message::LayerEvent(layer_event, surface_id))
|
|
||||||
}
|
|
||||||
_ => None,
|
_ => None,
|
||||||
},
|
},
|
||||||
iced::Event::Window(window::Event::Opened { .. }) => Some(Message::WindowOpen(id)),
|
iced::Event::Window(iced::window::Event::Focused) => Some(Message::Focus(id)),
|
||||||
_ => None,
|
_ => None,
|
||||||
}),
|
}),
|
||||||
Subscription::run_with_id(
|
Subscription::run_with_id(
|
||||||
|
|
|
||||||
|
|
@ -1,160 +0,0 @@
|
||||||
use cosmic::iced::ContentFit;
|
|
||||||
use cosmic::iced::{
|
|
||||||
widget::{
|
|
||||||
image::{draw, FilterMethod, Handle},
|
|
||||||
Container,
|
|
||||||
},
|
|
||||||
Rotation,
|
|
||||||
};
|
|
||||||
use cosmic::iced_core::event::{self, Event};
|
|
||||||
use cosmic::iced_core::layout;
|
|
||||||
use cosmic::iced_core::mouse;
|
|
||||||
use cosmic::iced_core::overlay;
|
|
||||||
use cosmic::iced_core::renderer;
|
|
||||||
use cosmic::iced_core::widget::{Operation, Tree};
|
|
||||||
use cosmic::iced_core::{Clipboard, Element, Layout, Length, Rectangle, Shell, Size, Widget};
|
|
||||||
use cosmic::{Renderer, Theme};
|
|
||||||
|
|
||||||
pub struct ImageContainer<'a, Message> {
|
|
||||||
container: Container<'a, Message, Theme, Renderer>,
|
|
||||||
image_opt: Option<Handle>,
|
|
||||||
content_fit: ContentFit,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, Message> ImageContainer<'a, Message> {
|
|
||||||
pub fn new(container: Container<'a, Message, Theme, Renderer>) -> Self {
|
|
||||||
Self {
|
|
||||||
container,
|
|
||||||
image_opt: None,
|
|
||||||
content_fit: ContentFit::None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn image(mut self, image: Handle) -> Self {
|
|
||||||
self.image_opt = Some(image);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn content_fit(mut self, content_fit: ContentFit) -> Self {
|
|
||||||
self.content_fit = content_fit;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, Message> Widget<Message, Theme, Renderer> for ImageContainer<'a, Message> {
|
|
||||||
fn children(&self) -> Vec<Tree> {
|
|
||||||
self.container.children()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn diff(&mut self, tree: &mut Tree) {
|
|
||||||
self.container.diff(tree)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn size(&self) -> Size<Length> {
|
|
||||||
self.container.size()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn layout(
|
|
||||||
&self,
|
|
||||||
tree: &mut Tree,
|
|
||||||
renderer: &Renderer,
|
|
||||||
limits: &layout::Limits,
|
|
||||||
) -> layout::Node {
|
|
||||||
self.container.layout(tree, renderer, limits)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn operate(
|
|
||||||
&self,
|
|
||||||
tree: &mut Tree,
|
|
||||||
layout: Layout<'_>,
|
|
||||||
renderer: &Renderer,
|
|
||||||
operation: &mut dyn Operation<()>,
|
|
||||||
) {
|
|
||||||
self.container.operate(tree, layout, renderer, operation)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn on_event(
|
|
||||||
&mut self,
|
|
||||||
tree: &mut Tree,
|
|
||||||
event: Event,
|
|
||||||
layout: Layout<'_>,
|
|
||||||
cursor: mouse::Cursor,
|
|
||||||
renderer: &Renderer,
|
|
||||||
clipboard: &mut dyn Clipboard,
|
|
||||||
shell: &mut Shell<'_, Message>,
|
|
||||||
viewport: &Rectangle,
|
|
||||||
) -> event::Status {
|
|
||||||
self.container.on_event(
|
|
||||||
tree, event, layout, cursor, renderer, clipboard, shell, viewport,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mouse_interaction(
|
|
||||||
&self,
|
|
||||||
tree: &Tree,
|
|
||||||
layout: Layout<'_>,
|
|
||||||
cursor: mouse::Cursor,
|
|
||||||
viewport: &Rectangle,
|
|
||||||
renderer: &Renderer,
|
|
||||||
) -> mouse::Interaction {
|
|
||||||
self.container
|
|
||||||
.mouse_interaction(tree, layout, cursor, viewport, renderer)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw(
|
|
||||||
&self,
|
|
||||||
tree: &Tree,
|
|
||||||
renderer: &mut Renderer,
|
|
||||||
theme: &Theme,
|
|
||||||
renderer_style: &renderer::Style,
|
|
||||||
layout: Layout<'_>,
|
|
||||||
cursor: mouse::Cursor,
|
|
||||||
viewport: &Rectangle,
|
|
||||||
) {
|
|
||||||
match &self.image_opt {
|
|
||||||
Some(image) => draw(
|
|
||||||
renderer,
|
|
||||||
layout,
|
|
||||||
image,
|
|
||||||
self.content_fit,
|
|
||||||
FilterMethod::Linear,
|
|
||||||
Rotation::default(),
|
|
||||||
1.,
|
|
||||||
[0.0, 0.0, 0.0, 0.0],
|
|
||||||
),
|
|
||||||
None => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
use cosmic::iced_renderer::core::Renderer as IcedRenderer;
|
|
||||||
renderer.with_layer(layout.bounds(), |renderer| {
|
|
||||||
self.container.draw(
|
|
||||||
tree,
|
|
||||||
renderer,
|
|
||||||
theme,
|
|
||||||
renderer_style,
|
|
||||||
layout,
|
|
||||||
cursor,
|
|
||||||
viewport,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn overlay<'b>(
|
|
||||||
&'b mut self,
|
|
||||||
state: &'b mut Tree,
|
|
||||||
layout: Layout<'_>,
|
|
||||||
renderer: &Renderer,
|
|
||||||
translation: cosmic::iced::Vector,
|
|
||||||
) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
|
|
||||||
self.container.overlay(state, layout, renderer, translation)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, Message> From<ImageContainer<'a, Message>> for Element<'a, Message, Theme, Renderer>
|
|
||||||
where
|
|
||||||
Message: 'a,
|
|
||||||
{
|
|
||||||
fn from(container: ImageContainer<'a, Message>) -> Element<'a, Message, Theme, Renderer> {
|
|
||||||
Element::new(container)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -4,7 +4,6 @@
|
||||||
pub mod greeter;
|
pub mod greeter;
|
||||||
pub mod locker;
|
pub mod locker;
|
||||||
|
|
||||||
mod image_container;
|
|
||||||
mod localize;
|
mod localize;
|
||||||
|
|
||||||
#[cfg(feature = "logind")]
|
#[cfg(feature = "logind")]
|
||||||
|
|
|
||||||
894
src/locker.rs
894
src/locker.rs
|
|
@ -1,7 +1,10 @@
|
||||||
// Copyright 2023 System76 <info@system76.com>
|
// Copyright 2023 System76 <info@system76.com>
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
use cosmic::app::{Action, Core, Settings, Task};
|
use cosmic::app::{Core, Settings, Task};
|
||||||
|
use cosmic::cctk::wayland_protocols::xdg::shell::client::xdg_positioner::Gravity;
|
||||||
|
use cosmic::iced::{Point, Rectangle, Size};
|
||||||
|
use cosmic::iced_runtime::platform_specific::wayland::subsurface::SctkSubsurfaceSettings;
|
||||||
use cosmic::surface;
|
use cosmic::surface;
|
||||||
use cosmic::{
|
use cosmic::{
|
||||||
executor,
|
executor,
|
||||||
|
|
@ -74,12 +77,15 @@ pub fn main(current_user: pwd::Passwd) -> Result<(), Box<dyn std::error::Error>>
|
||||||
log::error!("failed to create cosmic-bg state helper: {:?}", err);
|
log::error!("failed to create cosmic-bg state helper: {:?}", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let fallback_background =
|
||||||
|
widget::image::Handle::from_bytes(include_bytes!("../res/background.jpg").as_slice());
|
||||||
|
|
||||||
let flags = Flags {
|
let flags = Flags {
|
||||||
current_user,
|
current_user,
|
||||||
icon_opt,
|
icon_opt,
|
||||||
lockfile_opt: lockfile_opt(),
|
lockfile_opt: lockfile_opt(),
|
||||||
wallpapers,
|
wallpapers,
|
||||||
|
fallback_background,
|
||||||
};
|
};
|
||||||
|
|
||||||
let settings = Settings::default().no_main_window(true);
|
let settings = Settings::default().no_main_window(true);
|
||||||
|
|
@ -205,6 +211,7 @@ pub struct Flags {
|
||||||
icon_opt: Option<widget::image::Handle>,
|
icon_opt: Option<widget::image::Handle>,
|
||||||
lockfile_opt: Option<PathBuf>,
|
lockfile_opt: Option<PathBuf>,
|
||||||
wallpapers: Vec<(String, cosmic_bg_config::Source)>,
|
wallpapers: Vec<(String, cosmic_bg_config::Source)>,
|
||||||
|
fallback_background: widget::image::Handle,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Messages that are used specifically by our [`App`].
|
/// Messages that are used specifically by our [`App`].
|
||||||
|
|
@ -215,11 +222,12 @@ pub enum Message {
|
||||||
SessionLockEvent(SessionLockEvent),
|
SessionLockEvent(SessionLockEvent),
|
||||||
Channel(mpsc::Sender<String>),
|
Channel(mpsc::Sender<String>),
|
||||||
BackgroundState(cosmic_bg_config::state::State),
|
BackgroundState(cosmic_bg_config::state::State),
|
||||||
|
Focus(SurfaceId),
|
||||||
Inhibit(Arc<OwnedFd>),
|
Inhibit(Arc<OwnedFd>),
|
||||||
NetworkIcon(Option<&'static str>),
|
NetworkIcon(Option<&'static str>),
|
||||||
PowerInfo(Option<(String, f64)>),
|
PowerInfo(Option<(String, f64)>),
|
||||||
Prompt(String, bool, Option<String>),
|
Prompt(String, bool, Option<String>),
|
||||||
Submit,
|
Submit(String),
|
||||||
Surface(surface::Action),
|
Surface(surface::Action),
|
||||||
Suspend,
|
Suspend,
|
||||||
Error(String),
|
Error(String),
|
||||||
|
|
@ -240,11 +248,13 @@ pub struct App {
|
||||||
core: Core,
|
core: Core,
|
||||||
flags: Flags,
|
flags: Flags,
|
||||||
state: State,
|
state: State,
|
||||||
|
output_names: HashMap<WlOutput, String>,
|
||||||
surface_ids: HashMap<WlOutput, SurfaceId>,
|
surface_ids: HashMap<WlOutput, SurfaceId>,
|
||||||
|
subsurface_rects: HashMap<WlOutput, Rectangle>,
|
||||||
active_surface_id_opt: Option<SurfaceId>,
|
active_surface_id_opt: Option<SurfaceId>,
|
||||||
surface_images: HashMap<SurfaceId, widget::image::Handle>,
|
surface_images: HashMap<SurfaceId, widget::image::Handle>,
|
||||||
surface_names: HashMap<SurfaceId, String>,
|
surface_names: HashMap<SurfaceId, String>,
|
||||||
text_input_ids: HashMap<SurfaceId, widget::Id>,
|
text_input_ids: HashMap<String, widget::Id>,
|
||||||
inhibit_opt: Option<Arc<OwnedFd>>,
|
inhibit_opt: Option<Arc<OwnedFd>>,
|
||||||
network_icon_opt: Option<&'static str>,
|
network_icon_opt: Option<&'static str>,
|
||||||
power_info_opt: Option<(String, f64)>,
|
power_info_opt: Option<(String, f64)>,
|
||||||
|
|
@ -254,364 +264,7 @@ pub struct App {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
//TODO: cache wallpapers by source?
|
fn menu<'a>(&'a self, surface_id: SurfaceId) -> Element<'a, Message> {
|
||||||
fn update_wallpapers(&mut self) {
|
|
||||||
for (output, surface_id) in self.surface_ids.iter() {
|
|
||||||
if self.surface_images.contains_key(surface_id) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let output_name = match self.surface_names.get(surface_id) {
|
|
||||||
Some(some) => some,
|
|
||||||
None => continue,
|
|
||||||
};
|
|
||||||
|
|
||||||
log::info!("updating wallpaper for {:?}", output_name);
|
|
||||||
|
|
||||||
for (wallpaper_output_name, wallpaper_source) in self.flags.wallpapers.iter() {
|
|
||||||
if wallpaper_output_name == output_name {
|
|
||||||
match wallpaper_source {
|
|
||||||
cosmic_bg_config::Source::Path(path) => {
|
|
||||||
match fs::read(path) {
|
|
||||||
Ok(bytes) => {
|
|
||||||
let image = widget::image::Handle::from_bytes(bytes);
|
|
||||||
self.surface_images.insert(*surface_id, image);
|
|
||||||
//TODO: what to do about duplicates?
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
log::warn!(
|
|
||||||
"output {}: failed to load wallpaper {:?}: {:?}",
|
|
||||||
output.id(),
|
|
||||||
path,
|
|
||||||
err
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cosmic_bg_config::Source::Color(color) => {
|
|
||||||
//TODO: support color sources
|
|
||||||
log::warn!("output {}: unsupported source {:?}", output.id(), color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Implement [`cosmic::Application`] to integrate with COSMIC.
|
|
||||||
impl cosmic::Application for App {
|
|
||||||
/// Default async executor to use with the app.
|
|
||||||
type Executor = executor::Default;
|
|
||||||
|
|
||||||
/// Argument received [`cosmic::Application::new`].
|
|
||||||
type Flags = Flags;
|
|
||||||
|
|
||||||
/// Message type specific to our [`App`].
|
|
||||||
type Message = Message;
|
|
||||||
|
|
||||||
/// The unique application ID to supply to the window manager.
|
|
||||||
const APP_ID: &'static str = "com.system76.CosmicGreeter";
|
|
||||||
|
|
||||||
fn core(&self) -> &Core {
|
|
||||||
&self.core
|
|
||||||
}
|
|
||||||
|
|
||||||
fn core_mut(&mut self) -> &mut Core {
|
|
||||||
&mut self.core
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates the application, and optionally emits command on initialize.
|
|
||||||
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;
|
|
||||||
core.window.show_maximize = false;
|
|
||||||
core.window.show_minimize = false;
|
|
||||||
core.window.use_template = false;
|
|
||||||
|
|
||||||
let already_locked = match flags.lockfile_opt {
|
|
||||||
Some(ref lockfile) => lockfile.exists(),
|
|
||||||
None => false,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut app = App {
|
|
||||||
core,
|
|
||||||
flags,
|
|
||||||
state: State::Unlocked,
|
|
||||||
surface_ids: HashMap::new(),
|
|
||||||
active_surface_id_opt: None,
|
|
||||||
surface_images: HashMap::new(),
|
|
||||||
surface_names: HashMap::new(),
|
|
||||||
text_input_ids: HashMap::new(),
|
|
||||||
inhibit_opt: None,
|
|
||||||
network_icon_opt: None,
|
|
||||||
power_info_opt: None,
|
|
||||||
value_tx_opt: None,
|
|
||||||
prompt_opt: None,
|
|
||||||
error_opt: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let command = if cfg!(feature = "logind") {
|
|
||||||
if already_locked {
|
|
||||||
// Recover previously locked state
|
|
||||||
log::info!("recovering previous locked state");
|
|
||||||
app.state = State::Locking;
|
|
||||||
lock()
|
|
||||||
} else {
|
|
||||||
// When logind feature is used, wait for lock signal
|
|
||||||
Task::none()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// When logind feature not used, lock immediately
|
|
||||||
log::info!("locking immediately");
|
|
||||||
app.state = State::Locking;
|
|
||||||
lock()
|
|
||||||
};
|
|
||||||
|
|
||||||
(app, command)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Handle application events here.
|
|
||||||
fn update(&mut self, message: Self::Message) -> Task<Self::Message> {
|
|
||||||
match message {
|
|
||||||
Message::None => {}
|
|
||||||
Message::OutputEvent(output_event, output) => {
|
|
||||||
match output_event {
|
|
||||||
OutputEvent::Created(output_info_opt) => {
|
|
||||||
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?
|
|
||||||
log::warn!(
|
|
||||||
"output {}: already had surface ID {:?}",
|
|
||||||
output.id(),
|
|
||||||
old_surface_id
|
|
||||||
);
|
|
||||||
return Task::none();
|
|
||||||
}
|
|
||||||
None => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
match output_info_opt {
|
|
||||||
Some(output_info) => match output_info.name {
|
|
||||||
Some(output_name) => {
|
|
||||||
self.surface_names.insert(surface_id, output_name.clone());
|
|
||||||
self.surface_images.remove(&surface_id);
|
|
||||||
self.update_wallpapers();
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
log::warn!("output {}: no output name", output.id());
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None => {
|
|
||||||
log::warn!("output {}: no output info", output.id());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let text_input_id = widget::Id::unique();
|
|
||||||
self.text_input_ids
|
|
||||||
.insert(surface_id, text_input_id.clone());
|
|
||||||
|
|
||||||
if matches!(self.state, State::Locked) {
|
|
||||||
return Task::batch([
|
|
||||||
get_lock_surface(surface_id, output),
|
|
||||||
widget::text_input::focus(text_input_id),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
OutputEvent::Removed => {
|
|
||||||
log::info!("output {}: removed", output.id());
|
|
||||||
match self.surface_ids.remove(&output) {
|
|
||||||
Some(surface_id) => {
|
|
||||||
self.surface_images.remove(&surface_id);
|
|
||||||
self.surface_names.remove(&surface_id);
|
|
||||||
self.text_input_ids.remove(&surface_id);
|
|
||||||
if matches!(self.state, State::Locked) {
|
|
||||||
return destroy_lock_surface(surface_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
log::warn!("output {}: no surface found", output.id());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
OutputEvent::InfoUpdate(_output_info) => {
|
|
||||||
log::info!("output {}: info update", output.id());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Message::SessionLockEvent(session_lock_event) => match session_lock_event {
|
|
||||||
SessionLockEvent::Focused(_, surface_id) => {
|
|
||||||
log::info!("focus surface {:?}", surface_id);
|
|
||||||
self.active_surface_id_opt = Some(surface_id);
|
|
||||||
if let Some(text_input_id) = self.text_input_ids.get(&surface_id) {
|
|
||||||
return widget::text_input::focus(text_input_id.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SessionLockEvent::Locked => {
|
|
||||||
log::info!("session locked");
|
|
||||||
self.state = State::Locked;
|
|
||||||
// Allow suspend
|
|
||||||
self.inhibit_opt = None;
|
|
||||||
// Create lock surfaces
|
|
||||||
let mut commands = Vec::with_capacity(self.surface_ids.len());
|
|
||||||
for (output, surface_id) in self.surface_ids.iter() {
|
|
||||||
commands.push(get_lock_surface(*surface_id, output.clone()));
|
|
||||||
}
|
|
||||||
return Task::batch(commands);
|
|
||||||
}
|
|
||||||
SessionLockEvent::Unlocked => {
|
|
||||||
log::info!("session unlocked");
|
|
||||||
self.state = State::Unlocked;
|
|
||||||
if cfg!(feature = "logind") {
|
|
||||||
// When using logind feature, stick around for more lock signals
|
|
||||||
} else {
|
|
||||||
// When not using logind feature, exit immediately after unlocking
|
|
||||||
//TODO: cleaner method to exit?
|
|
||||||
process::exit(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//TODO: handle finished signal
|
|
||||||
_ => {}
|
|
||||||
},
|
|
||||||
Message::Channel(value_tx) => {
|
|
||||||
self.value_tx_opt = Some(value_tx);
|
|
||||||
}
|
|
||||||
Message::BackgroundState(background_state) => {
|
|
||||||
self.flags.wallpapers = background_state.wallpapers;
|
|
||||||
self.surface_images.clear();
|
|
||||||
self.update_wallpapers();
|
|
||||||
}
|
|
||||||
Message::Inhibit(inhibit) => {
|
|
||||||
self.inhibit_opt = Some(inhibit);
|
|
||||||
}
|
|
||||||
Message::NetworkIcon(network_icon_opt) => {
|
|
||||||
self.network_icon_opt = network_icon_opt;
|
|
||||||
}
|
|
||||||
Message::PowerInfo(power_info_opt) => {
|
|
||||||
self.power_info_opt = power_info_opt;
|
|
||||||
}
|
|
||||||
Message::Prompt(prompt, secret, value_opt) => {
|
|
||||||
let prompt_was_none = self.prompt_opt.is_none();
|
|
||||||
self.prompt_opt = Some((prompt, secret, value_opt));
|
|
||||||
if prompt_was_none {
|
|
||||||
if let Some(surface_id) = self.active_surface_id_opt {
|
|
||||||
if let Some(text_input_id) = self.text_input_ids.get(&surface_id) {
|
|
||||||
return widget::text_input::focus(text_input_id.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Message::Submit => match self.prompt_opt.take() {
|
|
||||||
Some((_prompt, _secret, value_opt)) => match value_opt {
|
|
||||||
Some(value) => match self.value_tx_opt.take() {
|
|
||||||
Some(value_tx) => {
|
|
||||||
// Clear errors
|
|
||||||
self.error_opt = None;
|
|
||||||
return cosmic::task::future(async move {
|
|
||||||
value_tx.send(value).await.unwrap();
|
|
||||||
Message::Channel(value_tx)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
None => log::warn!("tried to submit when value_tx_opt not set"),
|
|
||||||
},
|
|
||||||
None => log::warn!("tried to submit without value"),
|
|
||||||
},
|
|
||||||
None => log::warn!("tried to submit without prompt"),
|
|
||||||
},
|
|
||||||
Message::Suspend => {
|
|
||||||
#[cfg(feature = "logind")]
|
|
||||||
return cosmic::task::future(async move {
|
|
||||||
match crate::logind::suspend().await {
|
|
||||||
Ok(()) => cosmic::action::none(),
|
|
||||||
Err(err) => {
|
|
||||||
log::error!("failed to suspend: {:?}", err);
|
|
||||||
cosmic::Action::App(Message::Error(err.to_string()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Message::Error(error) => {
|
|
||||||
self.error_opt = Some(error);
|
|
||||||
}
|
|
||||||
Message::Lock => match self.state {
|
|
||||||
State::Unlocked => {
|
|
||||||
log::info!("session locking");
|
|
||||||
self.state = State::Locking;
|
|
||||||
// Clear errors
|
|
||||||
self.error_opt = None;
|
|
||||||
// Clear value_tx
|
|
||||||
self.value_tx_opt = None;
|
|
||||||
// Try to create lockfile when locking
|
|
||||||
if let Some(ref lockfile) = self.flags.lockfile_opt {
|
|
||||||
if let Err(err) = fs::File::create(lockfile) {
|
|
||||||
log::warn!("failed to create lockfile {:?}: {}", lockfile, err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Tell compositor to lock
|
|
||||||
return lock();
|
|
||||||
}
|
|
||||||
State::Unlocking => {
|
|
||||||
log::info!("session still unlocking");
|
|
||||||
}
|
|
||||||
State::Locking | State::Locked => {
|
|
||||||
log::info!("session already locking or locked");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Message::Unlock => {
|
|
||||||
match self.state {
|
|
||||||
State::Locked => {
|
|
||||||
log::info!("sessing unlocking");
|
|
||||||
self.state = State::Unlocking;
|
|
||||||
// Clear errors
|
|
||||||
self.error_opt = None;
|
|
||||||
// Clear value_tx
|
|
||||||
self.value_tx_opt = None;
|
|
||||||
// Try to delete lockfile when unlocking
|
|
||||||
if let Some(ref lockfile) = self.flags.lockfile_opt {
|
|
||||||
if let Err(err) = fs::remove_file(lockfile) {
|
|
||||||
log::warn!("failed to remove lockfile {:?}: {}", lockfile, err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Destroy lock surfaces
|
|
||||||
let mut commands = Vec::with_capacity(self.surface_ids.len() + 1);
|
|
||||||
for (_output, surface_id) in self.surface_ids.iter() {
|
|
||||||
commands.push(destroy_lock_surface(*surface_id));
|
|
||||||
}
|
|
||||||
// Tell compositor to unlock
|
|
||||||
commands.push(unlock());
|
|
||||||
// Wait to exit until `Unlocked` event, when server has processed unlock
|
|
||||||
return Task::batch(commands);
|
|
||||||
}
|
|
||||||
State::Locking => {
|
|
||||||
log::info!("session still locking");
|
|
||||||
}
|
|
||||||
State::Unlocking | State::Unlocked => {
|
|
||||||
log::info!("session already unlocking or unlocked");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Message::Surface(a) => {
|
|
||||||
return cosmic::task::message(cosmic::Action::Cosmic(
|
|
||||||
cosmic::app::Action::Surface(a),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Task::none()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Not used for layer surface window
|
|
||||||
fn view(&self) -> Element<Self::Message> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a view after each update.
|
|
||||||
fn view_window(&self, surface_id: SurfaceId) -> Element<Self::Message> {
|
|
||||||
let left_element = {
|
let left_element = {
|
||||||
let date_time_column = {
|
let date_time_column = {
|
||||||
let mut column = widget::column::with_capacity(2).padding(16.0);
|
let mut column = widget::column::with_capacity(2).padding(16.0);
|
||||||
|
|
@ -717,9 +370,16 @@ impl cosmic::Application for App {
|
||||||
match &self.prompt_opt {
|
match &self.prompt_opt {
|
||||||
Some((prompt, secret, value_opt)) => match value_opt {
|
Some((prompt, secret, value_opt)) => match value_opt {
|
||||||
Some(value) => {
|
Some(value) => {
|
||||||
|
let text_input_id = self
|
||||||
|
.surface_names
|
||||||
|
.get(&surface_id)
|
||||||
|
.and_then(|id| self.text_input_ids.get(id))
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or_else(|| cosmic::widget::Id::new("text_input"));
|
||||||
|
|
||||||
let mut text_input = widget::secure_input(
|
let mut text_input = widget::secure_input(
|
||||||
prompt.clone(),
|
prompt.clone(),
|
||||||
value.clone(),
|
"",
|
||||||
Some(Message::Prompt(
|
Some(Message::Prompt(
|
||||||
prompt.clone(),
|
prompt.clone(),
|
||||||
!*secret,
|
!*secret,
|
||||||
|
|
@ -727,12 +387,9 @@ impl cosmic::Application for App {
|
||||||
)),
|
)),
|
||||||
*secret,
|
*secret,
|
||||||
)
|
)
|
||||||
.on_input(|value| Message::Prompt(prompt.clone(), *secret, Some(value)))
|
.id(text_input_id)
|
||||||
.on_submit(|_| Message::Submit);
|
.manage_value(true)
|
||||||
|
.on_submit(|v| Message::Submit(v));
|
||||||
if let Some(text_input_id) = self.text_input_ids.get(&surface_id) {
|
|
||||||
text_input = text_input.id(text_input_id.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
if *secret {
|
if *secret {
|
||||||
text_input = text_input.password()
|
text_input = text_input.password()
|
||||||
|
|
@ -756,7 +413,6 @@ impl cosmic::Application for App {
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
};
|
};
|
||||||
|
|
||||||
crate::image_container::ImageContainer::new(
|
|
||||||
widget::container(
|
widget::container(
|
||||||
widget::layer_container(
|
widget::layer_container(
|
||||||
iced::widget::row![left_element, right_element]
|
iced::widget::row![left_element, right_element]
|
||||||
|
|
@ -775,28 +431,511 @@ impl cosmic::Application for App {
|
||||||
appearance
|
appearance
|
||||||
},
|
},
|
||||||
)))
|
)))
|
||||||
.width(Length::Fixed(800.0)),
|
.width(Length::Fill)
|
||||||
|
.height(Length::Shrink),
|
||||||
)
|
)
|
||||||
.padding([32.0, 0.0, 0.0, 0.0])
|
.padding([32.0, 0.0, 0.0, 0.0])
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
.height(Length::Fill)
|
.height(Length::Fill)
|
||||||
.align_x(alignment::Horizontal::Center)
|
.align_x(alignment::Horizontal::Center)
|
||||||
.align_y(alignment::Vertical::Top)
|
.align_y(alignment::Vertical::Top)
|
||||||
.class(cosmic::theme::Container::Transparent),
|
.class(cosmic::theme::Container::Transparent)
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: cache wallpapers by source?
|
||||||
|
fn update_wallpapers(&mut self) {
|
||||||
|
for (output, surface_id) in self.surface_ids.iter() {
|
||||||
|
if self.surface_images.contains_key(surface_id) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let output_name = match self.surface_names.get(surface_id) {
|
||||||
|
Some(some) => some,
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
|
||||||
|
log::info!("updating wallpaper for {:?}", output_name);
|
||||||
|
|
||||||
|
for (wallpaper_output_name, wallpaper_source) in self.flags.wallpapers.iter() {
|
||||||
|
if wallpaper_output_name == output_name {
|
||||||
|
match wallpaper_source {
|
||||||
|
cosmic_bg_config::Source::Path(path) => {
|
||||||
|
match fs::read(path) {
|
||||||
|
Ok(bytes) => {
|
||||||
|
let image = widget::image::Handle::from_bytes(bytes);
|
||||||
|
self.surface_images.insert(*surface_id, image);
|
||||||
|
//TODO: what to do about duplicates?
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
log::warn!(
|
||||||
|
"output {}: failed to load wallpaper {:?}: {:?}",
|
||||||
|
output.id(),
|
||||||
|
path,
|
||||||
|
err
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cosmic_bg_config::Source::Color(color) => {
|
||||||
|
//TODO: support color sources
|
||||||
|
log::warn!("output {}: unsupported source {:?}", output.id(), color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implement [`cosmic::Application`] to integrate with COSMIC.
|
||||||
|
impl cosmic::Application for App {
|
||||||
|
/// Default async executor to use with the app.
|
||||||
|
type Executor = executor::Default;
|
||||||
|
|
||||||
|
/// Argument received [`cosmic::Application::new`].
|
||||||
|
type Flags = Flags;
|
||||||
|
|
||||||
|
/// Message type specific to our [`App`].
|
||||||
|
type Message = Message;
|
||||||
|
|
||||||
|
/// The unique application ID to supply to the window manager.
|
||||||
|
const APP_ID: &'static str = "com.system76.CosmicGreeter";
|
||||||
|
|
||||||
|
fn core(&self) -> &Core {
|
||||||
|
&self.core
|
||||||
|
}
|
||||||
|
|
||||||
|
fn core_mut(&mut self) -> &mut Core {
|
||||||
|
&mut self.core
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates the application, and optionally emits command on initialize.
|
||||||
|
fn init(mut core: Core, flags: Self::Flags) -> (Self, Task<Self::Message>) {
|
||||||
|
core.window.show_window_menu = false;
|
||||||
|
core.window.show_headerbar = false;
|
||||||
|
// XXX must be false or define custom style to have transparent bg
|
||||||
|
core.window.sharp_corners = false;
|
||||||
|
core.window.show_maximize = false;
|
||||||
|
core.window.show_minimize = false;
|
||||||
|
core.window.use_template = false;
|
||||||
|
|
||||||
|
let already_locked = match flags.lockfile_opt {
|
||||||
|
Some(ref lockfile) => lockfile.exists(),
|
||||||
|
None => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut app = App {
|
||||||
|
core,
|
||||||
|
flags,
|
||||||
|
state: State::Unlocked,
|
||||||
|
surface_ids: HashMap::new(),
|
||||||
|
active_surface_id_opt: None,
|
||||||
|
output_names: HashMap::new(),
|
||||||
|
surface_images: HashMap::new(),
|
||||||
|
surface_names: HashMap::new(),
|
||||||
|
text_input_ids: HashMap::new(),
|
||||||
|
subsurface_rects: HashMap::new(),
|
||||||
|
inhibit_opt: None,
|
||||||
|
network_icon_opt: None,
|
||||||
|
power_info_opt: None,
|
||||||
|
value_tx_opt: None,
|
||||||
|
prompt_opt: None,
|
||||||
|
error_opt: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let command = if cfg!(feature = "logind") {
|
||||||
|
if already_locked {
|
||||||
|
// Recover previously locked state
|
||||||
|
log::info!("recovering previous locked state");
|
||||||
|
app.state = State::Locking;
|
||||||
|
lock()
|
||||||
|
} else {
|
||||||
|
// When logind feature is used, wait for lock signal
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// When logind feature not used, lock immediately
|
||||||
|
log::info!("locking immediately");
|
||||||
|
app.state = State::Locking;
|
||||||
|
lock()
|
||||||
|
};
|
||||||
|
|
||||||
|
(app, command)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handle application events here.
|
||||||
|
fn update(&mut self, message: Self::Message) -> Task<Self::Message> {
|
||||||
|
match message {
|
||||||
|
Message::None => {}
|
||||||
|
Message::OutputEvent(output_event, output) => {
|
||||||
|
match output_event {
|
||||||
|
OutputEvent::Created(output_info_opt) => {
|
||||||
|
log::info!("output {}: created", output.id());
|
||||||
|
|
||||||
|
let surface_id = SurfaceId::unique();
|
||||||
|
let subsurface_id = SurfaceId::unique();
|
||||||
|
|
||||||
|
match self.surface_ids.insert(output.clone(), surface_id) {
|
||||||
|
Some(old_surface_id) => {
|
||||||
|
//TODO: remove old surface?
|
||||||
|
log::warn!(
|
||||||
|
"output {}: already had surface ID {:?}",
|
||||||
|
output.id(),
|
||||||
|
old_surface_id
|
||||||
|
);
|
||||||
|
return Task::none();
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
let size = if let Some((w, h)) =
|
||||||
|
output_info_opt.as_ref().and_then(|info| info.logical_size)
|
||||||
|
{
|
||||||
|
Some((Some(w as u32), Some(h as u32)))
|
||||||
|
} else {
|
||||||
|
Some((None, None))
|
||||||
|
};
|
||||||
|
match output_info_opt {
|
||||||
|
Some(output_info) => match output_info.name {
|
||||||
|
Some(output_name) => {
|
||||||
|
self.output_names
|
||||||
|
.insert(output.clone(), output_name.clone());
|
||||||
|
self.surface_names.insert(surface_id, output_name.clone());
|
||||||
|
self.surface_names
|
||||||
|
.insert(subsurface_id, output_name.clone());
|
||||||
|
self.surface_images.remove(&surface_id);
|
||||||
|
self.update_wallpapers();
|
||||||
|
let text_input_id =
|
||||||
|
widget::Id::new(format!("input-{output_name}",));
|
||||||
|
self.text_input_ids
|
||||||
|
.insert(output_name.clone(), text_input_id.clone());
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
log::warn!("output {}: no output name", output.id());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
log::warn!("output {}: no output info", output.id());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let unwrapped_size = size
|
||||||
|
.map(|s| (s.0.unwrap_or(1920), s.1.unwrap_or(1080)))
|
||||||
|
.unwrap_or((1920, 1080));
|
||||||
|
let (loc, sub_size) = if unwrapped_size.0 > 800 {
|
||||||
|
(
|
||||||
|
Point::new(unwrapped_size.0 as f32 / 2. - 400., 32.),
|
||||||
|
Size::new(800., unwrapped_size.1 as f32 - 32.),
|
||||||
)
|
)
|
||||||
.image(match self.surface_images.get(&surface_id) {
|
} else {
|
||||||
Some(some) => some.clone(),
|
(
|
||||||
//TODO: default image
|
Point::new(0., 32.),
|
||||||
None => widget::image::Handle::from_rgba(1, 1, vec![0x00, 0x00, 0x00, 0xFF]),
|
Size::new(unwrapped_size.0 as f32, unwrapped_size.1 as f32 - 32.),
|
||||||
})
|
)
|
||||||
|
};
|
||||||
|
self.subsurface_rects
|
||||||
|
.insert(output.clone(), Rectangle::new(loc, sub_size));
|
||||||
|
|
||||||
|
let msg = cosmic::surface::action::subsurface(
|
||||||
|
move |_: &mut App| SctkSubsurfaceSettings {
|
||||||
|
parent: surface_id,
|
||||||
|
id: subsurface_id,
|
||||||
|
loc,
|
||||||
|
size: Some(sub_size),
|
||||||
|
z: 10,
|
||||||
|
steal_keyboard_focus: true,
|
||||||
|
gravity: Gravity::BottomRight,
|
||||||
|
offset: (0, 0),
|
||||||
|
input_zone: None,
|
||||||
|
},
|
||||||
|
Some(Box::new(move |app: &App| {
|
||||||
|
app.menu(subsurface_id).map(cosmic::Action::App)
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
|
||||||
|
if matches!(self.state, State::Locked) {
|
||||||
|
return Task::batch([
|
||||||
|
get_lock_surface(surface_id, output),
|
||||||
|
cosmic::task::message(cosmic::Action::Cosmic(
|
||||||
|
cosmic::app::Action::Surface(msg),
|
||||||
|
)),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OutputEvent::Removed => {
|
||||||
|
log::info!("output {}: removed", output.id());
|
||||||
|
match self.surface_ids.remove(&output) {
|
||||||
|
Some(surface_id) => {
|
||||||
|
self.surface_images.remove(&surface_id);
|
||||||
|
self.surface_names.remove(&surface_id);
|
||||||
|
if let Some(n) = self.surface_names.remove(&surface_id) {
|
||||||
|
self.text_input_ids.remove(&n);
|
||||||
|
}
|
||||||
|
if matches!(self.state, State::Locked) {
|
||||||
|
return destroy_lock_surface(surface_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
log::warn!("output {}: no surface found", output.id());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OutputEvent::InfoUpdate(info) => {
|
||||||
|
let size = if let Some((w, h)) = info.logical_size {
|
||||||
|
Some((Some(w as u32), Some(h as u32)))
|
||||||
|
} else {
|
||||||
|
Some((None, None))
|
||||||
|
};
|
||||||
|
let unwrapped_size = size
|
||||||
|
.map(|s| (s.0.unwrap_or(1920), s.1.unwrap_or(1080)))
|
||||||
|
.unwrap_or((1920, 1080));
|
||||||
|
let (loc, sub_size) = if unwrapped_size.0 > 800 {
|
||||||
|
(
|
||||||
|
Point::new(unwrapped_size.0 as f32 / 2. - 400., 32.),
|
||||||
|
Size::new(800., unwrapped_size.1 as f32 - 32.),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(Point::ORIGIN, Size::new(1920., 1080.))
|
||||||
|
};
|
||||||
|
self.subsurface_rects
|
||||||
|
.insert(output.clone(), Rectangle::new(loc, sub_size));
|
||||||
|
|
||||||
|
log::info!("output {}: info update", output.id());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Message::SessionLockEvent(session_lock_event) => match session_lock_event {
|
||||||
|
SessionLockEvent::Focused(..) => {}
|
||||||
|
SessionLockEvent::Locked => {
|
||||||
|
log::info!("session locked");
|
||||||
|
if matches!(self.state, State::Locked) {
|
||||||
|
return Task::none();
|
||||||
|
}
|
||||||
|
self.state = State::Locked;
|
||||||
|
// Allow suspend
|
||||||
|
self.inhibit_opt = None;
|
||||||
|
// Create lock surfaces
|
||||||
|
|
||||||
|
let mut commands = Vec::with_capacity(self.surface_ids.len());
|
||||||
|
for (output, surface_id) in self.surface_ids.iter() {
|
||||||
|
commands.push(get_lock_surface(*surface_id, output.clone()));
|
||||||
|
|
||||||
|
if let Some((rect, name)) = self
|
||||||
|
.subsurface_rects
|
||||||
|
.get(output)
|
||||||
|
.copied()
|
||||||
|
.zip(self.output_names.get(output))
|
||||||
|
{
|
||||||
|
let subsurface_id = SurfaceId::unique();
|
||||||
|
let surface_id = *surface_id;
|
||||||
|
self.surface_names.insert(surface_id, name.clone());
|
||||||
|
self.surface_names.insert(subsurface_id, name.clone());
|
||||||
|
let msg = cosmic::surface::action::subsurface(
|
||||||
|
move |_: &mut App| SctkSubsurfaceSettings {
|
||||||
|
parent: surface_id,
|
||||||
|
id: subsurface_id,
|
||||||
|
loc: Point::new(rect.x, rect.y),
|
||||||
|
size: Some(Size::new(rect.width, rect.height)),
|
||||||
|
z: 10,
|
||||||
|
steal_keyboard_focus: true,
|
||||||
|
gravity: Gravity::BottomRight,
|
||||||
|
offset: (0, 0),
|
||||||
|
input_zone: None,
|
||||||
|
},
|
||||||
|
Some(Box::new(move |app: &App| {
|
||||||
|
app.menu(subsurface_id).map(cosmic::Action::App)
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
commands.push(cosmic::task::message(cosmic::Action::Cosmic(
|
||||||
|
cosmic::app::Action::Surface(msg),
|
||||||
|
)));
|
||||||
|
} else {
|
||||||
|
log::error!("no rectangle for subsurface...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Task::batch(commands);
|
||||||
|
}
|
||||||
|
SessionLockEvent::Unlocked => {
|
||||||
|
log::info!("session unlocked");
|
||||||
|
self.state = State::Unlocked;
|
||||||
|
|
||||||
|
let mut commands = Vec::new();
|
||||||
|
for (_output, surface_id) in self.surface_ids.iter() {
|
||||||
|
self.surface_names.remove(surface_id);
|
||||||
|
|
||||||
|
commands.push(destroy_lock_surface(*surface_id));
|
||||||
|
}
|
||||||
|
if cfg!(feature = "logind") {
|
||||||
|
return Task::batch(commands);
|
||||||
|
// When using logind feature, stick around for more lock signals
|
||||||
|
} else {
|
||||||
|
// When not using logind feature, exit immediately after unlocking
|
||||||
|
//TODO: cleaner method to exit?
|
||||||
|
process::exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//TODO: handle finished signal
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
Message::Channel(value_tx) => {
|
||||||
|
self.value_tx_opt = Some(value_tx);
|
||||||
|
}
|
||||||
|
Message::BackgroundState(background_state) => {
|
||||||
|
self.flags.wallpapers = background_state.wallpapers;
|
||||||
|
self.surface_images.clear();
|
||||||
|
self.update_wallpapers();
|
||||||
|
}
|
||||||
|
Message::Inhibit(inhibit) => {
|
||||||
|
self.inhibit_opt = Some(inhibit);
|
||||||
|
}
|
||||||
|
Message::NetworkIcon(network_icon_opt) => {
|
||||||
|
self.network_icon_opt = network_icon_opt;
|
||||||
|
}
|
||||||
|
Message::PowerInfo(power_info_opt) => {
|
||||||
|
self.power_info_opt = power_info_opt;
|
||||||
|
}
|
||||||
|
Message::Focus(surface_id) => {
|
||||||
|
self.active_surface_id_opt = Some(surface_id);
|
||||||
|
self.active_surface_id_opt = Some(surface_id);
|
||||||
|
if let Some(text_input_id) = self
|
||||||
|
.surface_names
|
||||||
|
.get(&surface_id)
|
||||||
|
.and_then(|id| self.text_input_ids.get(id))
|
||||||
|
{
|
||||||
|
return widget::text_input::focus(text_input_id.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Message::Prompt(prompt, secret, value_opt) => {
|
||||||
|
let prompt_was_none = self.prompt_opt.is_none();
|
||||||
|
self.prompt_opt = Some((prompt, secret, value_opt));
|
||||||
|
if prompt_was_none {
|
||||||
|
if let Some(surface_id) = self.active_surface_id_opt {
|
||||||
|
if let Some(text_input_id) = self
|
||||||
|
.surface_names
|
||||||
|
.get(&surface_id)
|
||||||
|
.and_then(|id| self.text_input_ids.get(id))
|
||||||
|
{
|
||||||
|
log::error!("focus surface found id {:?}", text_input_id);
|
||||||
|
|
||||||
|
return widget::text_input::focus(text_input_id.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Message::Submit(value) => match self.value_tx_opt.take() {
|
||||||
|
Some(value_tx) => {
|
||||||
|
// Clear errors
|
||||||
|
self.error_opt = None;
|
||||||
|
return cosmic::task::future(async move {
|
||||||
|
value_tx.send(value).await.unwrap();
|
||||||
|
Message::Channel(value_tx)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
None => log::warn!("tried to submit when value_tx_opt not set"),
|
||||||
|
},
|
||||||
|
Message::Suspend => {
|
||||||
|
#[cfg(feature = "logind")]
|
||||||
|
return cosmic::task::future(async move {
|
||||||
|
match crate::logind::suspend().await {
|
||||||
|
Ok(()) => cosmic::action::none(),
|
||||||
|
Err(err) => {
|
||||||
|
log::error!("failed to suspend: {:?}", err);
|
||||||
|
cosmic::Action::App(Message::Error(err.to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Message::Error(error) => {
|
||||||
|
self.error_opt = Some(error);
|
||||||
|
}
|
||||||
|
Message::Lock => match self.state {
|
||||||
|
State::Unlocked => {
|
||||||
|
log::info!("session locking");
|
||||||
|
self.state = State::Locking;
|
||||||
|
// Clear errors
|
||||||
|
self.error_opt = None;
|
||||||
|
// Clear value_tx
|
||||||
|
self.value_tx_opt = None;
|
||||||
|
// Try to create lockfile when locking
|
||||||
|
if let Some(ref lockfile) = self.flags.lockfile_opt {
|
||||||
|
if let Err(err) = fs::File::create(lockfile) {
|
||||||
|
log::warn!("failed to create lockfile {:?}: {}", lockfile, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Tell compositor to lock
|
||||||
|
return lock();
|
||||||
|
}
|
||||||
|
State::Unlocking => {
|
||||||
|
log::info!("session still unlocking");
|
||||||
|
}
|
||||||
|
State::Locking | State::Locked => {
|
||||||
|
log::info!("session already locking or locked");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Message::Unlock => {
|
||||||
|
match self.state {
|
||||||
|
State::Locked => {
|
||||||
|
log::info!("sessing unlocking");
|
||||||
|
self.state = State::Unlocking;
|
||||||
|
// Clear errors
|
||||||
|
self.error_opt = None;
|
||||||
|
// Clear value_tx
|
||||||
|
self.value_tx_opt = None;
|
||||||
|
// Try to delete lockfile when unlocking
|
||||||
|
if let Some(ref lockfile) = self.flags.lockfile_opt {
|
||||||
|
if let Err(err) = fs::remove_file(lockfile) {
|
||||||
|
log::warn!("failed to remove lockfile {:?}: {}", lockfile, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destroy lock surfaces
|
||||||
|
let mut commands = Vec::with_capacity(self.surface_ids.len() + 1);
|
||||||
|
// Tell compositor to unlock
|
||||||
|
commands.push(unlock());
|
||||||
|
|
||||||
|
// Wait to exit until `Unlocked` event, when server has processed unlock
|
||||||
|
return Task::batch(commands);
|
||||||
|
}
|
||||||
|
State::Locking => {
|
||||||
|
log::info!("session still locking");
|
||||||
|
}
|
||||||
|
State::Unlocking | State::Unlocked => {
|
||||||
|
log::info!("session already unlocking or unlocked");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Message::Surface(a) => {
|
||||||
|
return cosmic::task::message(cosmic::Action::Cosmic(
|
||||||
|
cosmic::app::Action::Surface(a),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not used for layer surface window
|
||||||
|
fn view(&self) -> Element<Self::Message> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a view after each update.
|
||||||
|
fn view_window(&self, surface_id: SurfaceId) -> Element<Self::Message> {
|
||||||
|
let img = self
|
||||||
|
.surface_images
|
||||||
|
.get(&surface_id)
|
||||||
|
.unwrap_or(&self.flags.fallback_background);
|
||||||
|
widget::image(img)
|
||||||
.content_fit(iced::ContentFit::Cover)
|
.content_fit(iced::ContentFit::Cover)
|
||||||
|
.width(Length::Fill)
|
||||||
|
.height(Length::Fill)
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn subscription(&self) -> Subscription<Self::Message> {
|
fn subscription(&self) -> Subscription<Self::Message> {
|
||||||
let mut subscriptions = Vec::with_capacity(7);
|
let mut subscriptions = Vec::with_capacity(7);
|
||||||
|
|
||||||
subscriptions.push(event::listen_with(|event, _, _| match event {
|
subscriptions.push(event::listen_with(|event, _, id| match event {
|
||||||
iced::Event::PlatformSpecific(iced::event::PlatformSpecific::Wayland(
|
iced::Event::PlatformSpecific(iced::event::PlatformSpecific::Wayland(
|
||||||
wayland_event,
|
wayland_event,
|
||||||
)) => match wayland_event {
|
)) => match wayland_event {
|
||||||
|
|
@ -806,6 +945,7 @@ impl cosmic::Application for App {
|
||||||
WaylandEvent::SessionLock(evt) => Some(Message::SessionLockEvent(evt)),
|
WaylandEvent::SessionLock(evt) => Some(Message::SessionLockEvent(evt)),
|
||||||
_ => None,
|
_ => None,
|
||||||
},
|
},
|
||||||
|
iced::Event::Window(iced::window::Event::Focused) => Some(Message::Focus(id)),
|
||||||
_ => None,
|
_ => None,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue