feat: subsurfaces
This commit is contained in:
parent
0f37c9922d
commit
93bc4bbd88
33 changed files with 1898 additions and 2651 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -5812,7 +5812,7 @@ dependencies = [
|
||||||
name = "sctk_subsurface"
|
name = "sctk_subsurface"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"calloop 0.12.4",
|
"calloop 0.13.0",
|
||||||
"cosmic-client-toolkit",
|
"cosmic-client-toolkit",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ mod overlap_notify;
|
||||||
mod popup;
|
mod popup;
|
||||||
mod seat;
|
mod seat;
|
||||||
mod session_lock;
|
mod session_lock;
|
||||||
|
mod subsurface;
|
||||||
mod window;
|
mod window;
|
||||||
|
|
||||||
use crate::{time::Instant, window::Id};
|
use crate::{time::Instant, window::Id};
|
||||||
|
|
@ -17,6 +18,7 @@ pub use overlap_notify::*;
|
||||||
pub use popup::*;
|
pub use popup::*;
|
||||||
pub use seat::*;
|
pub use seat::*;
|
||||||
pub use session_lock::*;
|
pub use session_lock::*;
|
||||||
|
pub use subsurface::*;
|
||||||
pub use window::*;
|
pub use window::*;
|
||||||
|
|
||||||
/// wayland events
|
/// wayland events
|
||||||
|
|
@ -40,4 +42,6 @@ pub enum Event {
|
||||||
Frame(Instant, WlSurface, Id),
|
Frame(Instant, WlSurface, Id),
|
||||||
/// Request Resize
|
/// Request Resize
|
||||||
RequestResize,
|
RequestResize,
|
||||||
|
/// Subsurface
|
||||||
|
Subsurface(SubsurfaceEvent),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
8
core/src/event/wayland/subsurface.rs
Normal file
8
core/src/event/wayland/subsurface.rs
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
/// popup events
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum SubsurfaceEvent {
|
||||||
|
/// Destroyed
|
||||||
|
Destroyed,
|
||||||
|
/// repositioned,
|
||||||
|
Created,
|
||||||
|
}
|
||||||
|
|
@ -162,6 +162,7 @@ impl Window {
|
||||||
scale_input: "1.0".to_string(),
|
scale_input: "1.0".to_string(),
|
||||||
current_scale: 1.0,
|
current_scale: 1.0,
|
||||||
theme: Theme::ALL[count % Theme::ALL.len()].clone(),
|
theme: Theme::ALL[count % Theme::ALL.len()].clone(),
|
||||||
|
input_id: text_input::Id::unique(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,6 @@ iced = { path = "../..", default-features = false, features = [
|
||||||
iced_runtime = { path = "../../runtime" }
|
iced_runtime = { path = "../../runtime" }
|
||||||
env_logger = "0.10"
|
env_logger = "0.10"
|
||||||
futures-channel = "0.3.29"
|
futures-channel = "0.3.29"
|
||||||
calloop = "0.12.3"
|
calloop = "0.13"
|
||||||
rustix = { version = "0.38.30", features = ["fs", "shm"] }
|
rustix = { version = "0.38.30", features = ["fs", "shm"] }
|
||||||
cctk.workspace = true
|
cctk.workspace = true
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,24 @@
|
||||||
// Shows a subsurface with a 1x1 px red buffer, stretch to window size
|
// Shows a subsurface with a 1x1 px red buffer, stretch to window size
|
||||||
|
|
||||||
|
use cctk::sctk::reexports::{
|
||||||
|
client::{Connection, Proxy},
|
||||||
|
protocols::xdg::shell::client::xdg_positioner::{Anchor, Gravity},
|
||||||
|
};
|
||||||
|
|
||||||
|
use iced::platform_specific::shell::commands::subsurface::get_subsurface;
|
||||||
use iced::{
|
use iced::{
|
||||||
event::wayland::Event as WaylandEvent,
|
event::wayland::Event as WaylandEvent,
|
||||||
platform_specific::shell::subsurface_widget::{self, SubsurfaceBuffer},
|
platform_specific::{
|
||||||
widget::text,
|
runtime::wayland::subsurface::SctkSubsurfaceSettings,
|
||||||
|
shell::subsurface_widget::{self, SubsurfaceBuffer},
|
||||||
|
},
|
||||||
|
widget::{button, column, text, text_input},
|
||||||
window::{self, Id, Settings},
|
window::{self, Id, Settings},
|
||||||
Element, Length, Subscription, Task,
|
Element, Length, Subscription, Task,
|
||||||
};
|
};
|
||||||
use cctk::sctk::reexports::client::{Connection, Proxy};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
mod subsurface_container;
|
||||||
mod wayland;
|
mod wayland;
|
||||||
|
|
||||||
fn main() -> iced::Result {
|
fn main() -> iced::Result {
|
||||||
|
|
@ -23,8 +33,11 @@ fn main() -> iced::Result {
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
struct SubsurfaceApp {
|
struct SubsurfaceApp {
|
||||||
|
text: Arc<Mutex<String>>,
|
||||||
|
counter: Arc<Mutex<u32>>,
|
||||||
connection: Option<Connection>,
|
connection: Option<Connection>,
|
||||||
red_buffer: Option<SubsurfaceBuffer>,
|
red_buffer: Option<SubsurfaceBuffer>,
|
||||||
|
green_buffer: Option<SubsurfaceBuffer>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
@ -33,6 +46,8 @@ pub enum Message {
|
||||||
Wayland(wayland::Event),
|
Wayland(wayland::Event),
|
||||||
Pressed(&'static str),
|
Pressed(&'static str),
|
||||||
Id(Id),
|
Id(Id),
|
||||||
|
Inc,
|
||||||
|
Text(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SubsurfaceApp {
|
impl SubsurfaceApp {
|
||||||
|
|
@ -55,47 +70,106 @@ impl SubsurfaceApp {
|
||||||
|
|
||||||
fn update(&mut self, message: Message) -> Task<Message> {
|
fn update(&mut self, message: Message) -> Task<Message> {
|
||||||
match message {
|
match message {
|
||||||
Message::WaylandEvent(evt) => match evt {
|
Message::WaylandEvent(evt) => {
|
||||||
WaylandEvent::Output(_evt, output) => {
|
dbg!(&evt);
|
||||||
if self.connection.is_none() {
|
match evt {
|
||||||
if let Some(backend) = output.backend().upgrade() {
|
WaylandEvent::Output(_evt, output) => {
|
||||||
self.connection =
|
if self.connection.is_none() {
|
||||||
Some(Connection::from_backend(backend));
|
if let Some(backend) = output.backend().upgrade() {
|
||||||
|
self.connection =
|
||||||
|
Some(Connection::from_backend(backend));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
_ => {}
|
}
|
||||||
},
|
|
||||||
Message::Wayland(evt) => match evt {
|
Message::Wayland(evt) => match evt {
|
||||||
wayland::Event::RedBuffer(buffer) => {
|
wayland::Event::RedBuffer(buffer) => {
|
||||||
self.red_buffer = Some(buffer);
|
self.red_buffer = Some(buffer);
|
||||||
}
|
}
|
||||||
|
wayland::Event::GreenBuffer(buffer) => {
|
||||||
|
self.green_buffer = Some(buffer);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Message::Pressed(side) => println!("{side} surface pressed"),
|
Message::Pressed(side) => println!("{side} surface pressed"),
|
||||||
Message::Id(_) => {}
|
Message::Id(id) => {
|
||||||
|
let my_text = self.text.clone();
|
||||||
|
let my_counter = self.counter.clone();
|
||||||
|
return get_subsurface(SctkSubsurfaceSettings {
|
||||||
|
id: window::Id::unique(),
|
||||||
|
parent: id,
|
||||||
|
loc: iced::Point::new(100., 200.),
|
||||||
|
size: Some(iced::Size::new(100., 100.)),
|
||||||
|
z: 1000,
|
||||||
|
steal_keyboard_focus: false,
|
||||||
|
gravity: Gravity::BottomRight,
|
||||||
|
input_zone: None,
|
||||||
|
offset: (0, 0),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Message::Inc => {
|
||||||
|
let mut guard = self.counter.lock().unwrap();
|
||||||
|
|
||||||
|
*guard += 1;
|
||||||
|
}
|
||||||
|
Message::Text(s) => {
|
||||||
|
let mut guard = self.text.lock().unwrap();
|
||||||
|
*guard = s;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn view(&self, _id: window::Id) -> Element<Message> {
|
fn view(&self, id: window::Id) -> Element<Message> {
|
||||||
if let Some(buffer) = &self.red_buffer {
|
let my_text_guard = self.text.lock().unwrap();
|
||||||
iced::widget::row![
|
if let Some((red_buffer, green_buffer)) =
|
||||||
iced::widget::button(
|
self.red_buffer.iter().zip(self.green_buffer.iter()).next()
|
||||||
subsurface_widget::Subsurface::new(1, 1, buffer)
|
{
|
||||||
.width(Length::Fill)
|
column![
|
||||||
.height(Length::Fill)
|
iced::widget::row![
|
||||||
)
|
iced::widget::button(
|
||||||
.width(Length::Fill)
|
subsurface_container::SubsurfaceContainer::new()
|
||||||
.height(Length::Fill)
|
.width(Length::Fill)
|
||||||
.on_press(Message::Pressed("left")),
|
.height(Length::Fill)
|
||||||
iced::widget::button(
|
.push(
|
||||||
subsurface_widget::Subsurface::new(1, 1, buffer)
|
subsurface_widget::Subsurface::new(
|
||||||
.width(Length::Fill)
|
red_buffer.clone()
|
||||||
.height(Length::Fill)
|
)
|
||||||
)
|
.width(Length::Fill)
|
||||||
.width(Length::Fill)
|
.height(Length::Fill)
|
||||||
.height(Length::Fill)
|
.z(0)
|
||||||
.on_press(Message::Pressed("right"))
|
)
|
||||||
|
.push(
|
||||||
|
subsurface_widget::Subsurface::new(
|
||||||
|
green_buffer.clone()
|
||||||
|
)
|
||||||
|
.width(Length::Fixed(1920.))
|
||||||
|
.height(Length::Fixed(200.))
|
||||||
|
.z(1)
|
||||||
|
)
|
||||||
|
.push(
|
||||||
|
subsurface_widget::Subsurface::new(
|
||||||
|
red_buffer.clone()
|
||||||
|
)
|
||||||
|
.width(Length::Fill)
|
||||||
|
.height(Length::Fixed(100.))
|
||||||
|
.z(2)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.width(Length::Fill)
|
||||||
|
.height(Length::Fill)
|
||||||
|
.on_press(Message::Pressed("left")),
|
||||||
|
iced::widget::button(
|
||||||
|
subsurface_widget::Subsurface::new(red_buffer.clone())
|
||||||
|
.width(Length::Fill)
|
||||||
|
.height(Length::Fill)
|
||||||
|
)
|
||||||
|
.width(Length::Fill)
|
||||||
|
.height(Length::Fill)
|
||||||
|
.on_press(Message::Pressed("right"))
|
||||||
|
],
|
||||||
|
text_input("asdf", &my_text_guard).on_input(Message::Text)
|
||||||
]
|
]
|
||||||
.into()
|
.into()
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -106,10 +180,13 @@ impl SubsurfaceApp {
|
||||||
fn subscription(&self) -> Subscription<Message> {
|
fn subscription(&self) -> Subscription<Message> {
|
||||||
let mut subscriptions = vec![iced::event::listen_with(|evt, _, _| {
|
let mut subscriptions = vec![iced::event::listen_with(|evt, _, _| {
|
||||||
if let iced::Event::PlatformSpecific(
|
if let iced::Event::PlatformSpecific(
|
||||||
iced::event::PlatformSpecific::Wayland(evt),
|
iced::event::PlatformSpecific::Wayland(WaylandEvent::Output(
|
||||||
|
evt,
|
||||||
|
output,
|
||||||
|
)),
|
||||||
) = evt
|
) = evt
|
||||||
{
|
{
|
||||||
Some(Message::WaylandEvent(evt))
|
Some(Message::WaylandEvent(WaylandEvent::Output(evt, output)))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
|
||||||
430
examples/sctk_subsurface/src/subsurface_container.rs
Normal file
430
examples/sctk_subsurface/src/subsurface_container.rs
Normal file
|
|
@ -0,0 +1,430 @@
|
||||||
|
//! Distribute content vertically.
|
||||||
|
use iced::core::alignment::{self, Alignment};
|
||||||
|
use iced::core::event::{self, Event};
|
||||||
|
use iced::core::layout;
|
||||||
|
use iced::core::mouse;
|
||||||
|
use iced::core::overlay;
|
||||||
|
use iced::core::renderer;
|
||||||
|
use iced::core::widget::{Operation, Tree};
|
||||||
|
use iced::core::{
|
||||||
|
Clipboard, Element, Layout, Length, Padding, Pixels, Rectangle, Shell,
|
||||||
|
Size, Vector, Widget,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A container that distributes its contents vertically.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
/// ```no_run
|
||||||
|
/// # mod iced { pub mod widget { pub use iced_widget::*; } }
|
||||||
|
/// # pub type State = ();
|
||||||
|
/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>;
|
||||||
|
/// use iced::widget::{button, SubsurfaceContainer};
|
||||||
|
///
|
||||||
|
/// #[derive(Debug, Clone)]
|
||||||
|
/// enum Message {
|
||||||
|
/// // ...
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn view(state: &State) -> Element<'_, Message> {
|
||||||
|
/// SubsurfaceContainer![
|
||||||
|
/// "I am on top!",
|
||||||
|
/// button("I am in the center!"),
|
||||||
|
/// "I am below.",
|
||||||
|
/// ].into()
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[allow(missing_debug_implementations)]
|
||||||
|
pub struct SubsurfaceContainer<
|
||||||
|
'a,
|
||||||
|
Message,
|
||||||
|
Theme = iced::Theme,
|
||||||
|
Renderer = iced::Renderer,
|
||||||
|
> {
|
||||||
|
spacing: f32,
|
||||||
|
padding: Padding,
|
||||||
|
width: Length,
|
||||||
|
height: Length,
|
||||||
|
max_width: f32,
|
||||||
|
align: Alignment,
|
||||||
|
clip: bool,
|
||||||
|
children: Vec<Element<'a, Message, Theme, Renderer>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, Message, Theme, Renderer>
|
||||||
|
SubsurfaceContainer<'a, Message, Theme, Renderer>
|
||||||
|
where
|
||||||
|
Renderer: iced::core::Renderer,
|
||||||
|
{
|
||||||
|
/// Creates an empty [`SubsurfaceContainer`].
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::from_vec(Vec::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a [`SubsurfaceContainer`] with the given capacity.
|
||||||
|
pub fn with_capacity(capacity: usize) -> Self {
|
||||||
|
Self::from_vec(Vec::with_capacity(capacity))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a [`SubsurfaceContainer`] with the given elements.
|
||||||
|
pub fn with_children(
|
||||||
|
children: impl IntoIterator<Item = Element<'a, Message, Theme, Renderer>>,
|
||||||
|
) -> Self {
|
||||||
|
let iterator = children.into_iter();
|
||||||
|
|
||||||
|
Self::with_capacity(iterator.size_hint().0).extend(iterator)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a [`SubsurfaceContainer`] from an already allocated [`Vec`].
|
||||||
|
///
|
||||||
|
/// Keep in mind that the [`SubsurfaceContainer`] will not inspect the [`Vec`], which means
|
||||||
|
/// it won't automatically adapt to the sizing strategy of its contents.
|
||||||
|
///
|
||||||
|
/// If any of the children have a [`Length::Fill`] strategy, you will need to
|
||||||
|
/// call [`SubsurfaceContainer::width`] or [`SubsurfaceContainer::height`] accordingly.
|
||||||
|
pub fn from_vec(
|
||||||
|
children: Vec<Element<'a, Message, Theme, Renderer>>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
spacing: 0.0,
|
||||||
|
padding: Padding::ZERO,
|
||||||
|
width: Length::Shrink,
|
||||||
|
height: Length::Shrink,
|
||||||
|
max_width: f32::INFINITY,
|
||||||
|
align: Alignment::Start,
|
||||||
|
clip: false,
|
||||||
|
children,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the vertical spacing _between_ elements.
|
||||||
|
///
|
||||||
|
/// Custom margins per element do not exist in iced. You should use this
|
||||||
|
/// method instead! While less flexible, it helps you keep spacing between
|
||||||
|
/// elements consistent.
|
||||||
|
pub fn spacing(mut self, amount: impl Into<Pixels>) -> Self {
|
||||||
|
self.spacing = amount.into().0;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the [`Padding`] of the [`SubsurfaceContainer`].
|
||||||
|
pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self {
|
||||||
|
self.padding = padding.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the width of the [`SubsurfaceContainer`].
|
||||||
|
pub fn width(mut self, width: impl Into<Length>) -> Self {
|
||||||
|
self.width = width.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the height of the [`SubsurfaceContainer`].
|
||||||
|
pub fn height(mut self, height: impl Into<Length>) -> Self {
|
||||||
|
self.height = height.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the maximum width of the [`SubsurfaceContainer`].
|
||||||
|
pub fn max_width(mut self, max_width: impl Into<Pixels>) -> Self {
|
||||||
|
self.max_width = max_width.into().0;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the horizontal alignment of the contents of the [`SubsurfaceContainer`] .
|
||||||
|
pub fn align_x(mut self, align: impl Into<alignment::Horizontal>) -> Self {
|
||||||
|
self.align = Alignment::from(align.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets whether the contents of the [`SubsurfaceContainer`] should be clipped on
|
||||||
|
/// overflow.
|
||||||
|
pub fn clip(mut self, clip: bool) -> Self {
|
||||||
|
self.clip = clip;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds an element to the [`SubsurfaceContainer`].
|
||||||
|
pub fn push(
|
||||||
|
mut self,
|
||||||
|
child: impl Into<Element<'a, Message, Theme, Renderer>>,
|
||||||
|
) -> Self {
|
||||||
|
let child = child.into();
|
||||||
|
let child_size = child.as_widget().size_hint();
|
||||||
|
|
||||||
|
self.width = self.width.enclose(child_size.width);
|
||||||
|
self.height = self.height.enclose(child_size.height);
|
||||||
|
|
||||||
|
self.children.push(child);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds an element to the [`SubsurfaceContainer`], if `Some`.
|
||||||
|
pub fn push_maybe(
|
||||||
|
self,
|
||||||
|
child: Option<impl Into<Element<'a, Message, Theme, Renderer>>>,
|
||||||
|
) -> Self {
|
||||||
|
if let Some(child) = child {
|
||||||
|
self.push(child)
|
||||||
|
} else {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extends the [`SubsurfaceContainer`] with the given children.
|
||||||
|
pub fn extend(
|
||||||
|
self,
|
||||||
|
children: impl IntoIterator<Item = Element<'a, Message, Theme, Renderer>>,
|
||||||
|
) -> Self {
|
||||||
|
children.into_iter().fold(self, Self::push)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, Message, Renderer> Default
|
||||||
|
for SubsurfaceContainer<'a, Message, Renderer>
|
||||||
|
where
|
||||||
|
Renderer: iced::core::Renderer,
|
||||||
|
{
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, Message, Theme, Renderer: iced::core::Renderer>
|
||||||
|
FromIterator<Element<'a, Message, Theme, Renderer>>
|
||||||
|
for SubsurfaceContainer<'a, Message, Theme, Renderer>
|
||||||
|
{
|
||||||
|
fn from_iter<
|
||||||
|
T: IntoIterator<Item = Element<'a, Message, Theme, Renderer>>,
|
||||||
|
>(
|
||||||
|
iter: T,
|
||||||
|
) -> Self {
|
||||||
|
Self::with_children(iter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, Message, Theme, Renderer> Widget<Message, Theme, Renderer>
|
||||||
|
for SubsurfaceContainer<'a, Message, Theme, Renderer>
|
||||||
|
where
|
||||||
|
Renderer: iced::core::Renderer,
|
||||||
|
{
|
||||||
|
fn children(&self) -> Vec<Tree> {
|
||||||
|
self.children.iter().map(Tree::new).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn diff(&mut self, tree: &mut Tree) {
|
||||||
|
tree.diff_children(self.children.as_mut_slice());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size(&self) -> Size<Length> {
|
||||||
|
Size {
|
||||||
|
width: self.width,
|
||||||
|
height: self.height,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn layout(
|
||||||
|
&self,
|
||||||
|
tree: &mut Tree,
|
||||||
|
renderer: &Renderer,
|
||||||
|
limits: &layout::Limits,
|
||||||
|
) -> layout::Node {
|
||||||
|
let limits = limits.max_width(self.max_width);
|
||||||
|
let nodes = self
|
||||||
|
.children
|
||||||
|
.iter()
|
||||||
|
.zip(tree.children.iter_mut())
|
||||||
|
.map(|c| {
|
||||||
|
let size = c.0.as_widget().size();
|
||||||
|
layout::positioned(
|
||||||
|
&limits.max_width(self.max_width),
|
||||||
|
size.width,
|
||||||
|
size.height,
|
||||||
|
self.padding,
|
||||||
|
|limits| c.0.as_widget().layout(c.1, renderer, limits),
|
||||||
|
|content, size| {
|
||||||
|
content.align(self.align, Alignment::Start, size)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let size = limits.resolve(self.width, self.height, Size::ZERO);
|
||||||
|
|
||||||
|
layout::Node::with_children(size, nodes)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn operate(
|
||||||
|
&self,
|
||||||
|
tree: &mut Tree,
|
||||||
|
layout: Layout<'_>,
|
||||||
|
renderer: &Renderer,
|
||||||
|
operation: &mut dyn Operation,
|
||||||
|
) {
|
||||||
|
operation.container(None, layout.bounds(), &mut |operation| {
|
||||||
|
self.children
|
||||||
|
.iter()
|
||||||
|
.zip(&mut tree.children)
|
||||||
|
.zip(layout.children())
|
||||||
|
.for_each(|((child, state), layout)| {
|
||||||
|
child
|
||||||
|
.as_widget()
|
||||||
|
.operate(state, 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.children
|
||||||
|
.iter_mut()
|
||||||
|
.zip(&mut tree.children)
|
||||||
|
.zip(layout.children())
|
||||||
|
.map(|((child, state), layout)| {
|
||||||
|
child.as_widget_mut().on_event(
|
||||||
|
state,
|
||||||
|
event.clone(),
|
||||||
|
layout,
|
||||||
|
cursor,
|
||||||
|
renderer,
|
||||||
|
clipboard,
|
||||||
|
shell,
|
||||||
|
viewport,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.fold(event::Status::Ignored, event::Status::merge)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mouse_interaction(
|
||||||
|
&self,
|
||||||
|
tree: &Tree,
|
||||||
|
layout: Layout<'_>,
|
||||||
|
cursor: mouse::Cursor,
|
||||||
|
viewport: &Rectangle,
|
||||||
|
renderer: &Renderer,
|
||||||
|
) -> mouse::Interaction {
|
||||||
|
self.children
|
||||||
|
.iter()
|
||||||
|
.zip(&tree.children)
|
||||||
|
.zip(layout.children())
|
||||||
|
.map(|((child, state), layout)| {
|
||||||
|
child.as_widget().mouse_interaction(
|
||||||
|
state, layout, cursor, viewport, renderer,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.max()
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(
|
||||||
|
&self,
|
||||||
|
tree: &Tree,
|
||||||
|
renderer: &mut Renderer,
|
||||||
|
theme: &Theme,
|
||||||
|
style: &renderer::Style,
|
||||||
|
layout: Layout<'_>,
|
||||||
|
cursor: mouse::Cursor,
|
||||||
|
viewport: &Rectangle,
|
||||||
|
) {
|
||||||
|
if let Some(clipped_viewport) = layout.bounds().intersection(viewport) {
|
||||||
|
let viewport = if self.clip {
|
||||||
|
&clipped_viewport
|
||||||
|
} else {
|
||||||
|
viewport
|
||||||
|
};
|
||||||
|
|
||||||
|
for ((child, state), layout) in self
|
||||||
|
.children
|
||||||
|
.iter()
|
||||||
|
.zip(&tree.children)
|
||||||
|
.zip(layout.children())
|
||||||
|
.filter(|(_, layout)| layout.bounds().intersects(viewport))
|
||||||
|
{
|
||||||
|
child.as_widget().draw(
|
||||||
|
state, renderer, theme, style, layout, cursor, viewport,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn overlay<'b>(
|
||||||
|
&'b mut self,
|
||||||
|
tree: &'b mut Tree,
|
||||||
|
layout: Layout<'_>,
|
||||||
|
renderer: &Renderer,
|
||||||
|
translation: Vector,
|
||||||
|
) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
|
||||||
|
overlay::from_children(
|
||||||
|
&mut self.children,
|
||||||
|
tree,
|
||||||
|
layout,
|
||||||
|
renderer,
|
||||||
|
translation,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "a11y")]
|
||||||
|
/// get the a11y nodes for the widget
|
||||||
|
fn a11y_nodes(
|
||||||
|
&self,
|
||||||
|
layout: Layout<'_>,
|
||||||
|
state: &Tree,
|
||||||
|
cursor: mouse::Cursor,
|
||||||
|
) -> iced_accessibility::A11yTree {
|
||||||
|
use iced_accessibility::A11yTree;
|
||||||
|
A11yTree::join(
|
||||||
|
self.children
|
||||||
|
.iter()
|
||||||
|
.zip(layout.children())
|
||||||
|
.zip(state.children.iter())
|
||||||
|
.map(|((c, c_layout), state)| {
|
||||||
|
c.as_widget().a11y_nodes(c_layout, state, cursor)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drag_destinations(
|
||||||
|
&self,
|
||||||
|
state: &Tree,
|
||||||
|
layout: Layout<'_>,
|
||||||
|
renderer: &Renderer,
|
||||||
|
dnd_rectangles: &mut iced::core::clipboard::DndDestinationRectangles,
|
||||||
|
) {
|
||||||
|
for ((e, layout), state) in self
|
||||||
|
.children
|
||||||
|
.iter()
|
||||||
|
.zip(layout.children())
|
||||||
|
.zip(state.children.iter())
|
||||||
|
{
|
||||||
|
e.as_widget().drag_destinations(
|
||||||
|
state,
|
||||||
|
layout,
|
||||||
|
renderer,
|
||||||
|
dnd_rectangles,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, Message, Theme, Renderer>
|
||||||
|
From<SubsurfaceContainer<'a, Message, Theme, Renderer>>
|
||||||
|
for Element<'a, Message, Theme, Renderer>
|
||||||
|
where
|
||||||
|
Message: 'a,
|
||||||
|
Theme: 'a,
|
||||||
|
Renderer: iced::core::Renderer + 'a,
|
||||||
|
{
|
||||||
|
fn from(
|
||||||
|
SubsurfaceContainer: SubsurfaceContainer<'a, Message, Theme, Renderer>,
|
||||||
|
) -> Self {
|
||||||
|
Self::new(SubsurfaceContainer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,11 +1,5 @@
|
||||||
use futures_channel::mpsc;
|
|
||||||
use iced::{
|
|
||||||
futures::{FutureExt, SinkExt},
|
|
||||||
platform_specific::shell::subsurface_widget::{Shmbuf, SubsurfaceBuffer},
|
|
||||||
};
|
|
||||||
use iced_runtime::futures::subscription;
|
|
||||||
use rustix::{io::Errno, shm::ShmOFlags};
|
|
||||||
use cctk::sctk::{
|
use cctk::sctk::{
|
||||||
|
self,
|
||||||
reexports::{
|
reexports::{
|
||||||
calloop_wayland_source::WaylandSource,
|
calloop_wayland_source::WaylandSource,
|
||||||
client::{
|
client::{
|
||||||
|
|
@ -18,16 +12,24 @@ use cctk::sctk::{
|
||||||
registry::{ProvidesRegistryState, RegistryState},
|
registry::{ProvidesRegistryState, RegistryState},
|
||||||
shm::{Shm, ShmHandler},
|
shm::{Shm, ShmHandler},
|
||||||
};
|
};
|
||||||
|
use futures_channel::mpsc;
|
||||||
|
use iced::{
|
||||||
|
futures::{FutureExt, SinkExt},
|
||||||
|
platform_specific::shell::subsurface_widget::{Shmbuf, SubsurfaceBuffer},
|
||||||
|
};
|
||||||
|
use iced_runtime::futures::subscription;
|
||||||
|
use rustix::{io::Errno, shm::ShmOFlags};
|
||||||
use std::{
|
use std::{
|
||||||
os::fd::OwnedFd,
|
os::fd::OwnedFd,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
thread,
|
thread,
|
||||||
time::{SystemTime, UNIX_EPOCH},
|
time::{Duration, SystemTime, UNIX_EPOCH},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
RedBuffer(SubsurfaceBuffer),
|
RedBuffer(SubsurfaceBuffer),
|
||||||
|
GreenBuffer(SubsurfaceBuffer),
|
||||||
}
|
}
|
||||||
|
|
||||||
struct AppData {
|
struct AppData {
|
||||||
|
|
@ -80,6 +82,20 @@ async fn start(conn: Connection) -> mpsc::Receiver<Event> {
|
||||||
format: wl_shm::Format::Xrgb8888,
|
format: wl_shm::Format::Xrgb8888,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let buffer = SubsurfaceBuffer::new(Arc::new(shmbuf.into())).0;
|
||||||
|
let _ = sender.send(Event::GreenBuffer(buffer)).await;
|
||||||
|
|
||||||
|
let fd = create_memfile().unwrap();
|
||||||
|
rustix::io::write(&fd, &[0, 0, 255, 255]).unwrap();
|
||||||
|
|
||||||
|
let shmbuf = Shmbuf {
|
||||||
|
fd,
|
||||||
|
offset: 0,
|
||||||
|
width: 1,
|
||||||
|
height: 1,
|
||||||
|
stride: 4,
|
||||||
|
format: wl_shm::Format::Xrgb8888,
|
||||||
|
};
|
||||||
let buffer = SubsurfaceBuffer::new(Arc::new(shmbuf.into())).0;
|
let buffer = SubsurfaceBuffer::new(Arc::new(shmbuf.into())).0;
|
||||||
let _ = sender.send(Event::RedBuffer(buffer)).await;
|
let _ = sender.send(Event::RedBuffer(buffer)).await;
|
||||||
|
|
||||||
|
|
@ -90,6 +106,7 @@ async fn start(conn: Connection) -> mpsc::Receiver<Event> {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
loop {
|
loop {
|
||||||
event_loop.dispatch(None, &mut app_data).unwrap();
|
event_loop.dispatch(None, &mut app_data).unwrap();
|
||||||
|
std::thread::sleep(Duration::from_millis(500));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use iced_core::layout::Limits;
|
|
||||||
use cctk::sctk::{
|
use cctk::sctk::{
|
||||||
reexports::client::protocol::wl_output::WlOutput,
|
reexports::client::protocol::wl_output::WlOutput,
|
||||||
shell::wlr_layer::{Anchor, KeyboardInteractivity, Layer},
|
shell::wlr_layer::{Anchor, KeyboardInteractivity, Layer},
|
||||||
};
|
};
|
||||||
|
use iced_core::layout::Limits;
|
||||||
|
|
||||||
use iced_core::window::Id;
|
use iced_core::window::Id;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,9 @@ pub mod popup;
|
||||||
/// session locks
|
/// session locks
|
||||||
pub mod session_lock;
|
pub mod session_lock;
|
||||||
|
|
||||||
|
// subsurfaces
|
||||||
|
pub mod subsurface;
|
||||||
|
|
||||||
/// Platform specific actions defined for wayland
|
/// Platform specific actions defined for wayland
|
||||||
pub enum Action {
|
pub enum Action {
|
||||||
/// LayerSurface Actions
|
/// LayerSurface Actions
|
||||||
|
|
@ -26,6 +29,8 @@ pub enum Action {
|
||||||
SessionLock(session_lock::Action),
|
SessionLock(session_lock::Action),
|
||||||
/// Overlap Notify
|
/// Overlap Notify
|
||||||
OverlapNotify(Id, bool),
|
OverlapNotify(Id, bool),
|
||||||
|
/// Subsurfaces
|
||||||
|
Subsurface(subsurface::Action),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for Action {
|
impl Debug for Action {
|
||||||
|
|
@ -44,6 +49,9 @@ impl Debug for Action {
|
||||||
Action::OverlapNotify(id, _) => {
|
Action::OverlapNotify(id, _) => {
|
||||||
f.debug_tuple("OverlapNotify").field(id).finish()
|
f.debug_tuple("OverlapNotify").field(id).finish()
|
||||||
}
|
}
|
||||||
|
Action::Subsurface(action) => {
|
||||||
|
f.debug_tuple("Subsurface").field(action).finish()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,15 @@
|
||||||
|
use std::any::Any;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use iced_core::layout::Limits;
|
|
||||||
use iced_core::window::Id;
|
|
||||||
use iced_core::Rectangle;
|
|
||||||
use cctk::sctk::reexports::protocols::xdg::shell::client::xdg_positioner::{
|
use cctk::sctk::reexports::protocols::xdg::shell::client::xdg_positioner::{
|
||||||
Anchor, Gravity,
|
Anchor, Gravity,
|
||||||
};
|
};
|
||||||
|
use iced_core::layout::Limits;
|
||||||
|
use iced_core::window::Id;
|
||||||
|
use iced_core::{Element, Rectangle};
|
||||||
|
|
||||||
/// Popup creation details
|
/// Popup creation details
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct SctkPopupSettings {
|
pub struct SctkPopupSettings {
|
||||||
|
|
@ -20,6 +23,11 @@ pub struct SctkPopupSettings {
|
||||||
pub parent_size: Option<(u32, u32)>,
|
pub parent_size: Option<(u32, u32)>,
|
||||||
/// whether a grab should be requested for the popup after creation
|
/// whether a grab should be requested for the popup after creation
|
||||||
pub grab: bool,
|
pub grab: bool,
|
||||||
|
/// whether a popup should close when its child popups close
|
||||||
|
pub close_with_children: bool,
|
||||||
|
/// input zone
|
||||||
|
/// None results in accepting all input
|
||||||
|
pub input_zone: Option<Rectangle>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Hash for SctkPopupSettings {
|
impl Hash for SctkPopupSettings {
|
||||||
|
|
|
||||||
77
runtime/src/platform_specific/wayland/subsurface.rs
Normal file
77
runtime/src/platform_specific/wayland/subsurface.rs
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
use std::any::Any;
|
||||||
|
use std::fmt;
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use cctk::sctk::reexports::protocols::xdg::shell::client::xdg_positioner::{
|
||||||
|
Anchor, Gravity,
|
||||||
|
};
|
||||||
|
use iced_core::layout::Limits;
|
||||||
|
use iced_core::window::Id;
|
||||||
|
use iced_core::{Element, Point, Rectangle, Size};
|
||||||
|
|
||||||
|
/// Subsurface creation details
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct SctkSubsurfaceSettings {
|
||||||
|
/// XXX must be unique, id of the parent
|
||||||
|
pub parent: Id,
|
||||||
|
/// XXX must be unique, id of the subsurface
|
||||||
|
pub id: Id,
|
||||||
|
/// anchor position of the subsurface
|
||||||
|
pub loc: Point,
|
||||||
|
/// size of the subsurface
|
||||||
|
pub size: Option<Size>,
|
||||||
|
// pub subsurface_view: Option<Arc<dyn Any + Send + Sync>>,
|
||||||
|
/// Z
|
||||||
|
pub z: u32,
|
||||||
|
/// Steal Keyboard focus from parent while open.
|
||||||
|
/// Will not work on a regular window.
|
||||||
|
pub steal_keyboard_focus: bool,
|
||||||
|
|
||||||
|
/// offset of the subsurface from the anchor
|
||||||
|
pub offset: (i32, i32),
|
||||||
|
/// the gravity of the popup
|
||||||
|
pub gravity: Gravity,
|
||||||
|
|
||||||
|
/// input zone
|
||||||
|
/// None results in accepting all input
|
||||||
|
pub input_zone: Option<Rectangle>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hash for SctkSubsurfaceSettings {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
self.id.hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
/// Window Action
|
||||||
|
pub enum Action {
|
||||||
|
/// create a window and receive a message with its Id
|
||||||
|
Subsurface {
|
||||||
|
/// subsurface
|
||||||
|
subsurface: SctkSubsurfaceSettings,
|
||||||
|
},
|
||||||
|
/// destroy the subsurface
|
||||||
|
Destroy {
|
||||||
|
/// id of the subsurface
|
||||||
|
id: Id,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Action {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Action::Subsurface { subsurface, .. } => write!(
|
||||||
|
f,
|
||||||
|
"Action::SubsurfaceAction::Subsurface {{ subsurface: {:?} }}",
|
||||||
|
subsurface
|
||||||
|
),
|
||||||
|
Action::Destroy { id } => write!(
|
||||||
|
f,
|
||||||
|
"Action::SubsurfaceAction::Destroy {{ id: {:?} }}",
|
||||||
|
id
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -45,6 +45,7 @@ pub enum SurfaceIdWrapper {
|
||||||
Window(window::Id),
|
Window(window::Id),
|
||||||
Popup(window::Id),
|
Popup(window::Id),
|
||||||
SessionLock(window::Id),
|
SessionLock(window::Id),
|
||||||
|
Subsurface(window::Id),
|
||||||
}
|
}
|
||||||
impl SurfaceIdWrapper {
|
impl SurfaceIdWrapper {
|
||||||
pub fn inner(&self) -> window::Id {
|
pub fn inner(&self) -> window::Id {
|
||||||
|
|
@ -53,6 +54,7 @@ impl SurfaceIdWrapper {
|
||||||
SurfaceIdWrapper::Window(id) => *id,
|
SurfaceIdWrapper::Window(id) => *id,
|
||||||
SurfaceIdWrapper::Popup(id) => *id,
|
SurfaceIdWrapper::Popup(id) => *id,
|
||||||
SurfaceIdWrapper::SessionLock(id) => *id,
|
SurfaceIdWrapper::SessionLock(id) => *id,
|
||||||
|
SurfaceIdWrapper::Subsurface(id) => *id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,3 +5,4 @@ pub mod layer_surface;
|
||||||
pub mod overlap_notify;
|
pub mod overlap_notify;
|
||||||
pub mod popup;
|
pub mod popup;
|
||||||
pub mod session_lock;
|
pub mod session_lock;
|
||||||
|
pub mod subsurface;
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
use crate::core::window::Id as SurfaceId;
|
use crate::core::window::Id as SurfaceId;
|
||||||
|
use cctk::sctk::reexports::client::protocol::wl_output::WlOutput;
|
||||||
use iced_runtime::{
|
use iced_runtime::{
|
||||||
self,
|
self,
|
||||||
platform_specific::{self, wayland},
|
platform_specific::{self, wayland},
|
||||||
task, Action, Task,
|
task, Action, Task,
|
||||||
};
|
};
|
||||||
use cctk::sctk::reexports::client::protocol::wl_output::WlOutput;
|
|
||||||
|
|
||||||
pub fn lock<Message>() -> Task<Message> {
|
pub fn lock<Message>() -> Task<Message> {
|
||||||
task::effect(Action::PlatformSpecific(
|
task::effect(Action::PlatformSpecific(
|
||||||
|
|
|
||||||
28
winit/src/platform_specific/wayland/commands/subsurface.rs
Normal file
28
winit/src/platform_specific/wayland/commands/subsurface.rs
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
use crate::core::window::Id as SurfaceId;
|
||||||
|
pub use cctk::sctk::shell::wlr_layer::{Anchor, KeyboardInteractivity, Layer};
|
||||||
|
use iced_runtime::{
|
||||||
|
self,
|
||||||
|
platform_specific::{
|
||||||
|
self,
|
||||||
|
wayland::{self, subsurface::SctkSubsurfaceSettings},
|
||||||
|
},
|
||||||
|
task, Action, Task,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn get_subsurface<Message>(
|
||||||
|
subsurface: SctkSubsurfaceSettings,
|
||||||
|
) -> Task<Message> {
|
||||||
|
task::effect(Action::PlatformSpecific(
|
||||||
|
platform_specific::Action::Wayland(wayland::Action::Subsurface(
|
||||||
|
wayland::subsurface::Action::Subsurface { subsurface },
|
||||||
|
)),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroy_subsurface<Message>(id: SurfaceId) -> Task<Message> {
|
||||||
|
task::effect(Action::PlatformSpecific(
|
||||||
|
platform_specific::Action::Wayland(wayland::Action::Subsurface(
|
||||||
|
wayland::subsurface::Action::Destroy { id },
|
||||||
|
)),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
@ -73,47 +73,3 @@ pub fn modifiers_to_native(mods: Modifiers) -> keyboard::Modifiers {
|
||||||
// }
|
// }
|
||||||
native_mods
|
native_mods
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub fn keysym_to_vkey(keysym: RawKeysym) -> Option<KeyCode> {
|
|
||||||
// key_conversion.get(&keysym).cloned()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// pub(crate) fn cursor_icon(cursor: winit::window::CursorIcon) -> CursorIcon {
|
|
||||||
// match cursor {
|
|
||||||
// CursorIcon::Default => todo!(),
|
|
||||||
// CursorIcon::ContextMenu => todo!(),
|
|
||||||
// CursorIcon::Help => todo!(),
|
|
||||||
// CursorIcon::Pointer => todo!(),
|
|
||||||
// CursorIcon::Progress => todo!(),
|
|
||||||
// CursorIcon::Wait => todo!(),
|
|
||||||
// CursorIcon::Cell => todo!(),
|
|
||||||
// CursorIcon::Crosshair => todo!(),
|
|
||||||
// CursorIcon::Text => todo!(),
|
|
||||||
// CursorIcon::VerticalText => todo!(),
|
|
||||||
// CursorIcon::Alias => todo!(),
|
|
||||||
// CursorIcon::Copy => todo!(),
|
|
||||||
// CursorIcon::Move => todo!(),
|
|
||||||
// CursorIcon::NoDrop => todo!(),
|
|
||||||
// CursorIcon::NotAllowed => todo!(),
|
|
||||||
// CursorIcon::Grab => todo!(),
|
|
||||||
// CursorIcon::Grabbing => todo!(),
|
|
||||||
// CursorIcon::EResize => todo!(),
|
|
||||||
// CursorIcon::NResize => todo!(),
|
|
||||||
// CursorIcon::NeResize => todo!(),
|
|
||||||
// CursorIcon::NwResize => todo!(),
|
|
||||||
// CursorIcon::SResize => todo!(),
|
|
||||||
// CursorIcon::SeResize => todo!(),
|
|
||||||
// CursorIcon::SwResize => todo!(),
|
|
||||||
// CursorIcon::WResize => todo!(),
|
|
||||||
// CursorIcon::EwResize => todo!(),
|
|
||||||
// CursorIcon::NsResize => todo!(),
|
|
||||||
// CursorIcon::NeswResize => todo!(),
|
|
||||||
// CursorIcon::NwseResize => todo!(),
|
|
||||||
// CursorIcon::ColResize => todo!(),
|
|
||||||
// CursorIcon::RowResize => todo!(),
|
|
||||||
// CursorIcon::AllScroll => todo!(),
|
|
||||||
// CursorIcon::ZoomIn => todo!(),
|
|
||||||
// CursorIcon::ZoomOut => todo!(),
|
|
||||||
// _ => todo!(),
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ use cctk::{
|
||||||
toplevel_management::ToplevelManagerState,
|
toplevel_management::ToplevelManagerState,
|
||||||
};
|
};
|
||||||
use raw_window_handle::HasDisplayHandle;
|
use raw_window_handle::HasDisplayHandle;
|
||||||
use state::{FrameStatus, SctkWindow};
|
use state::{FrameStatus, SctkWindow, send_event};
|
||||||
#[cfg(feature = "a11y")]
|
#[cfg(feature = "a11y")]
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::{
|
use std::{
|
||||||
|
|
@ -50,7 +50,7 @@ use std::{
|
||||||
};
|
};
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
use wayland_backend::client::Backend;
|
use wayland_backend::client::Backend;
|
||||||
use winit::event_loop::OwnedDisplayHandle;
|
use winit::{dpi::LogicalSize, event_loop::OwnedDisplayHandle};
|
||||||
|
|
||||||
use self::state::SctkState;
|
use self::state::SctkState;
|
||||||
|
|
||||||
|
|
@ -120,7 +120,36 @@ impl SctkEventLoop {
|
||||||
id,
|
id,
|
||||||
) => {
|
) => {
|
||||||
// TODO clean up popups matching the window.
|
// TODO clean up popups matching the window.
|
||||||
state.windows.retain(|window| id != window.id);
|
if let Some(pos) = state
|
||||||
|
.windows
|
||||||
|
.iter()
|
||||||
|
.position(|window| id == window.id)
|
||||||
|
{
|
||||||
|
let w = state.windows.remove(pos);
|
||||||
|
for subsurface_id in state
|
||||||
|
.subsurfaces
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.filter_map(|(i, s)| {
|
||||||
|
(winit::window::WindowId::from_raw(
|
||||||
|
s.instance.parent.as_ptr()
|
||||||
|
as usize,
|
||||||
|
) == w.window.id())
|
||||||
|
.then_some(i)
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
{
|
||||||
|
let s = state
|
||||||
|
.subsurfaces
|
||||||
|
.remove(subsurface_id);
|
||||||
|
crate::subsurface_widget::remove_iced_subsurface(
|
||||||
|
&s.instance.wl_surface,
|
||||||
|
);
|
||||||
|
send_event(&state.events_sender, &state.proxy,
|
||||||
|
SctkEvent::SubsurfaceEvent( crate::sctk_event::SubsurfaceEventVariant::Destroyed(s.instance) )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
crate::platform_specific::Action::SetCursor(
|
crate::platform_specific::Action::SetCursor(
|
||||||
icon,
|
icon,
|
||||||
|
|
@ -144,6 +173,57 @@ impl SctkEventLoop {
|
||||||
crate::platform_specific::Action::Dropped(id) => {
|
crate::platform_specific::Action::Dropped(id) => {
|
||||||
_ = state.destroyed.remove(&id.inner());
|
_ = state.destroyed.remove(&id.inner());
|
||||||
}
|
}
|
||||||
|
crate::platform_specific::Action::SubsurfaceResize(id, size) => {
|
||||||
|
// reposition the surface
|
||||||
|
if let Some(pos) = state
|
||||||
|
.subsurfaces
|
||||||
|
.iter()
|
||||||
|
.position(|window| id == window.id)
|
||||||
|
{
|
||||||
|
let subsurface = &mut state.subsurfaces[pos];
|
||||||
|
let settings = &subsurface.settings;
|
||||||
|
let mut loc = settings.loc;
|
||||||
|
let guard = subsurface.common.lock().unwrap();
|
||||||
|
let size: LogicalSize<f32> = size.to_logical(guard.fractional_scale.unwrap_or(1.));
|
||||||
|
let half_w = size.width / 2.;
|
||||||
|
let half_h = size.height / 2.;
|
||||||
|
match settings.gravity {
|
||||||
|
wayland_protocols::xdg::shell::client::xdg_positioner::Gravity::None => {
|
||||||
|
// center on
|
||||||
|
loc.x -= half_w;
|
||||||
|
loc.y -= half_h;
|
||||||
|
},
|
||||||
|
wayland_protocols::xdg::shell::client::xdg_positioner::Gravity::Top => {
|
||||||
|
loc.x -= half_w;
|
||||||
|
loc.y -= size.height;
|
||||||
|
},
|
||||||
|
wayland_protocols::xdg::shell::client::xdg_positioner::Gravity::Bottom => {
|
||||||
|
loc.x -= half_w;
|
||||||
|
},
|
||||||
|
wayland_protocols::xdg::shell::client::xdg_positioner::Gravity::Left => {
|
||||||
|
loc.y -= half_h;
|
||||||
|
loc.x -= size.width;
|
||||||
|
},
|
||||||
|
wayland_protocols::xdg::shell::client::xdg_positioner::Gravity::Right => {
|
||||||
|
loc.y -= half_h;
|
||||||
|
},
|
||||||
|
wayland_protocols::xdg::shell::client::xdg_positioner::Gravity::TopLeft => {
|
||||||
|
loc.y -= size.height;
|
||||||
|
loc.x -= size.width;
|
||||||
|
},
|
||||||
|
wayland_protocols::xdg::shell::client::xdg_positioner::Gravity::BottomLeft => {
|
||||||
|
loc.x -= size.width;
|
||||||
|
},
|
||||||
|
wayland_protocols::xdg::shell::client::xdg_positioner::Gravity::TopRight => {
|
||||||
|
loc.y -= size.height;
|
||||||
|
},
|
||||||
|
wayland_protocols::xdg::shell::client::xdg_positioner::Gravity::BottomRight => {},
|
||||||
|
_ => unimplemented!(),
|
||||||
|
};
|
||||||
|
subsurface.instance.wl_subsurface.set_position(loc.x as i32, loc.y as i32);
|
||||||
|
|
||||||
|
}
|
||||||
|
send_event(&state.events_sender, &state.proxy, SctkEvent::SubsurfaceEvent(crate::sctk_event::SubsurfaceEventVariant::Resized(id, size)))},
|
||||||
},
|
},
|
||||||
calloop::channel::Event::Closed => {
|
calloop::channel::Event::Closed => {
|
||||||
log::info!("Calloop channel closed.");
|
log::info!("Calloop channel closed.");
|
||||||
|
|
@ -167,7 +247,7 @@ impl SctkEventLoop {
|
||||||
let (viewporter_state, fractional_scaling_manager) =
|
let (viewporter_state, fractional_scaling_manager) =
|
||||||
match FractionalScalingManager::new(&globals, &qh) {
|
match FractionalScalingManager::new(&globals, &qh) {
|
||||||
Ok(m) => {
|
Ok(m) => {
|
||||||
let viewporter_state =
|
let viewporter_state: Option<ViewporterState> =
|
||||||
match ViewporterState::new(&globals, &qh) {
|
match ViewporterState::new(&globals, &qh) {
|
||||||
Ok(s) => Some(s),
|
Ok(s) => Some(s),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
|
@ -227,6 +307,7 @@ impl SctkEventLoop {
|
||||||
layer_surfaces: Vec::new(),
|
layer_surfaces: Vec::new(),
|
||||||
popups: Vec::new(),
|
popups: Vec::new(),
|
||||||
lock_surfaces: Vec::new(),
|
lock_surfaces: Vec::new(),
|
||||||
|
subsurfaces: Vec::new(),
|
||||||
_kbd_focus: None,
|
_kbd_focus: None,
|
||||||
touch_points: HashMap::new(),
|
touch_points: HashMap::new(),
|
||||||
sctk_events: Vec::new(),
|
sctk_events: Vec::new(),
|
||||||
|
|
@ -243,6 +324,7 @@ impl SctkEventLoop {
|
||||||
activation_token_ctr: 0,
|
activation_token_ctr: 0,
|
||||||
token_senders: HashMap::new(),
|
token_senders: HashMap::new(),
|
||||||
overlap_notifications: HashMap::new(),
|
overlap_notifications: HashMap::new(),
|
||||||
|
subsurface_state: None,
|
||||||
},
|
},
|
||||||
_features: Default::default(),
|
_features: Default::default(),
|
||||||
};
|
};
|
||||||
|
|
@ -280,20 +362,23 @@ impl SctkEventLoop {
|
||||||
if let (Ok(wl_subcompositor), Ok(wp_viewporter)) =
|
if let (Ok(wl_subcompositor), Ok(wp_viewporter)) =
|
||||||
(wl_subcompositor, wp_viewporter)
|
(wl_subcompositor, wp_viewporter)
|
||||||
{
|
{
|
||||||
|
let subsurface_state = SubsurfaceState {
|
||||||
|
wl_compositor,
|
||||||
|
wl_subcompositor,
|
||||||
|
wp_viewporter,
|
||||||
|
wl_shm,
|
||||||
|
wp_dmabuf,
|
||||||
|
wp_alpha_modifier,
|
||||||
|
qh: state.state.queue_handle.clone(),
|
||||||
|
buffers: HashMap::new(),
|
||||||
|
unmapped_subsurfaces: Vec::new(),
|
||||||
|
new_iced_subsurfaces: Vec::new(),
|
||||||
|
};
|
||||||
|
state.state.subsurface_state = Some(subsurface_state.clone());
|
||||||
state::send_event(
|
state::send_event(
|
||||||
&state.state.events_sender,
|
&state.state.events_sender,
|
||||||
&state.state.proxy,
|
&state.state.proxy,
|
||||||
SctkEvent::Subcompositor(SubsurfaceState {
|
SctkEvent::Subcompositor(subsurface_state),
|
||||||
wl_compositor,
|
|
||||||
wl_subcompositor,
|
|
||||||
wp_viewporter,
|
|
||||||
wl_shm,
|
|
||||||
wp_dmabuf,
|
|
||||||
wp_alpha_modifier,
|
|
||||||
qh: state.state.queue_handle.clone(),
|
|
||||||
buffers: HashMap::new(),
|
|
||||||
unmapped_subsurfaces: Vec::new(),
|
|
||||||
}),
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
log::warn!("Subsurfaces not supported.")
|
log::warn!("Subsurfaces not supported.")
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
|
use cctk::sctk::reexports::calloop;
|
||||||
use iced_futures::futures::{
|
use iced_futures::futures::{
|
||||||
channel::mpsc,
|
channel::mpsc,
|
||||||
task::{Context, Poll},
|
task::{Context, Poll},
|
||||||
Sink,
|
Sink,
|
||||||
};
|
};
|
||||||
use cctk::sctk::reexports::calloop;
|
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
/// An event loop proxy that implements `Sink`.
|
/// An event loop proxy that implements `Sink`.
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
Control,
|
Control,
|
||||||
|
sctk_event::KeyboardEventVariant,
|
||||||
|
subsurface_widget::SubsurfaceState,
|
||||||
|
wayland::SubsurfaceInstance,
|
||||||
handlers::{
|
handlers::{
|
||||||
activation::IcedRequestData,
|
activation::IcedRequestData,
|
||||||
overlap::{OverlapNotificationV1, OverlapNotifyV1},
|
overlap::{OverlapNotificationV1, OverlapNotifyV1},
|
||||||
|
|
@ -15,13 +18,17 @@ use crate::{
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use iced_futures::futures::channel::{mpsc, oneshot};
|
use iced_futures::{
|
||||||
|
core::{Rectangle, Size},
|
||||||
|
futures::channel::{mpsc, oneshot},
|
||||||
|
};
|
||||||
use raw_window_handle::HasWindowHandle;
|
use raw_window_handle::HasWindowHandle;
|
||||||
use std::{
|
use std::{
|
||||||
collections::{HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
convert::Infallible,
|
convert::Infallible,
|
||||||
fmt::Debug,
|
fmt::Debug,
|
||||||
sync::{Arc, Mutex, atomic::AtomicU32},
|
sync::{atomic::AtomicU32, Arc, Mutex},
|
||||||
|
thread::panicking,
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
use wayland_backend::client::ObjectId;
|
use wayland_backend::client::ObjectId;
|
||||||
|
|
@ -36,9 +43,7 @@ use iced_runtime::{
|
||||||
platform_specific::{
|
platform_specific::{
|
||||||
self,
|
self,
|
||||||
wayland::{
|
wayland::{
|
||||||
Action,
|
layer_surface::{IcedMargin, IcedOutput, SctkLayerSurfaceSettings}, popup::SctkPopupSettings, subsurface::{self, SctkSubsurfaceSettings}, Action
|
||||||
layer_surface::{IcedMargin, IcedOutput, SctkLayerSurfaceSettings},
|
|
||||||
popup::SctkPopupSettings,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -87,7 +92,6 @@ use cctk::{cosmic_protocols::overlap_notify::v1::client::zcosmic_overlap_notific
|
||||||
},
|
},
|
||||||
shm::{multi::MultiPool, Shm},
|
shm::{multi::MultiPool, Shm},
|
||||||
}, toplevel_info::ToplevelInfoState, toplevel_management::ToplevelManagerState};
|
}, toplevel_info::ToplevelInfoState, toplevel_management::ToplevelManagerState};
|
||||||
|
|
||||||
use wayland_protocols::{
|
use wayland_protocols::{
|
||||||
wp::{
|
wp::{
|
||||||
fractional_scale::v1::client::wp_fractional_scale_v1::WpFractionalScaleV1,
|
fractional_scale::v1::client::wp_fractional_scale_v1::WpFractionalScaleV1,
|
||||||
|
|
@ -183,6 +187,10 @@ pub enum CommonSurface {
|
||||||
Popup(Popup, Arc<XdgPositioner>),
|
Popup(Popup, Arc<XdgPositioner>),
|
||||||
Layer(LayerSurface),
|
Layer(LayerSurface),
|
||||||
Lock(SessionLockSurface),
|
Lock(SessionLockSurface),
|
||||||
|
Subsurface {
|
||||||
|
wl_surface: WlSurface,
|
||||||
|
wl_subsurface: WlSubsurface,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CommonSurface {
|
impl CommonSurface {
|
||||||
|
|
@ -193,6 +201,7 @@ impl CommonSurface {
|
||||||
CommonSurface::Lock(session_lock_surface) => {
|
CommonSurface::Lock(session_lock_surface) => {
|
||||||
session_lock_surface.wl_surface()
|
session_lock_surface.wl_surface()
|
||||||
}
|
}
|
||||||
|
CommonSurface::Subsurface { wl_surface, .. } => wl_surface,
|
||||||
};
|
};
|
||||||
wl_surface
|
wl_surface
|
||||||
}
|
}
|
||||||
|
|
@ -241,6 +250,7 @@ pub struct SctkPopup {
|
||||||
pub(crate) data: SctkPopupData,
|
pub(crate) data: SctkPopupData,
|
||||||
pub(crate) common: Arc<Mutex<Common>>,
|
pub(crate) common: Arc<Mutex<Common>>,
|
||||||
pub(crate) wp_fractional_scale: Option<WpFractionalScaleV1>,
|
pub(crate) wp_fractional_scale: Option<WpFractionalScaleV1>,
|
||||||
|
pub(crate) close_with_children: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SctkPopup {
|
impl SctkPopup {
|
||||||
|
|
@ -249,10 +259,19 @@ impl SctkPopup {
|
||||||
self.popup
|
self.popup
|
||||||
.xdg_surface()
|
.xdg_surface()
|
||||||
.set_window_geometry(0, 0, w as i32, h as i32);
|
.set_window_geometry(0, 0, w as i32, h as i32);
|
||||||
|
self.update_viewport(w, h);
|
||||||
// update positioner
|
// update positioner
|
||||||
self.data.positioner.set_size(w as i32, h as i32);
|
self.data.positioner.set_size(w as i32, h as i32);
|
||||||
self.popup.reposition(&self.data.positioner, token);
|
self.popup.reposition(&self.data.positioner, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn update_viewport(&mut self, w: u32, h: u32) {
|
||||||
|
let common = self.common.lock().unwrap();
|
||||||
|
if let Some(viewport) = common.wp_viewport.as_ref() {
|
||||||
|
// Set inner size without the borders.
|
||||||
|
viewport.set_destination(w as i32, h as i32);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -261,8 +280,27 @@ pub struct SctkLockSurface {
|
||||||
pub(crate) session_lock_surface: SessionLockSurface,
|
pub(crate) session_lock_surface: SessionLockSurface,
|
||||||
pub(crate) last_configure: Option<SessionLockSurfaceConfigure>,
|
pub(crate) last_configure: Option<SessionLockSurfaceConfigure>,
|
||||||
pub(crate) wp_fractional_scale: Option<WpFractionalScaleV1>,
|
pub(crate) wp_fractional_scale: Option<WpFractionalScaleV1>,
|
||||||
pub(crate) wp_viewport: Option<WpViewport>,
|
|
||||||
pub(crate) common: Arc<Mutex<Common>>,
|
pub(crate) common: Arc<Mutex<Common>>,
|
||||||
|
pub(crate) output: WlOutput,
|
||||||
|
}
|
||||||
|
impl SctkLockSurface {
|
||||||
|
pub(crate) fn update_viewport(&mut self, w: u32, h: u32) {
|
||||||
|
let mut common = self.common.lock().unwrap();
|
||||||
|
|
||||||
|
common.size = LogicalSize::new(w, h);
|
||||||
|
if let Some(viewport) = common.wp_viewport.as_ref() {
|
||||||
|
// Set inner size without the borders.
|
||||||
|
viewport.set_destination(w as i32, h as i32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct SctkSubsurface {
|
||||||
|
pub(crate) common: Arc<Mutex<Common>>,
|
||||||
|
pub(crate) steals_keyboard_focus: bool,
|
||||||
|
pub(crate) id: core::window::Id,
|
||||||
|
pub(crate) instance: SubsurfaceInstance,
|
||||||
|
pub(crate) settings: SctkSubsurfaceSettings,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -271,6 +309,7 @@ pub struct SctkPopupData {
|
||||||
pub(crate) parent: PopupParent,
|
pub(crate) parent: PopupParent,
|
||||||
pub(crate) toplevel: WlSurface,
|
pub(crate) toplevel: WlSurface,
|
||||||
pub(crate) positioner: Arc<XdgPositioner>,
|
pub(crate) positioner: Arc<XdgPositioner>,
|
||||||
|
pub(crate) grab: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SctkWindow {
|
pub struct SctkWindow {
|
||||||
|
|
@ -347,6 +386,7 @@ pub struct SctkState {
|
||||||
pub(crate) windows: Vec<SctkWindow>,
|
pub(crate) windows: Vec<SctkWindow>,
|
||||||
pub(crate) layer_surfaces: Vec<SctkLayerSurface>,
|
pub(crate) layer_surfaces: Vec<SctkLayerSurface>,
|
||||||
pub(crate) popups: Vec<SctkPopup>,
|
pub(crate) popups: Vec<SctkPopup>,
|
||||||
|
pub(crate) subsurfaces: Vec<SctkSubsurface>,
|
||||||
pub(crate) lock_surfaces: Vec<SctkLockSurface>,
|
pub(crate) lock_surfaces: Vec<SctkLockSurface>,
|
||||||
pub(crate) _kbd_focus: Option<WlSurface>,
|
pub(crate) _kbd_focus: Option<WlSurface>,
|
||||||
pub(crate) touch_points: HashMap<touch::Finger, (WlSurface, Point)>,
|
pub(crate) touch_points: HashMap<touch::Finger, (WlSurface, Point)>,
|
||||||
|
|
@ -390,6 +430,7 @@ pub struct SctkState {
|
||||||
pub(crate) overlap_notify: Option<OverlapNotifyV1>,
|
pub(crate) overlap_notify: Option<OverlapNotifyV1>,
|
||||||
pub(crate) toplevel_info: Option<ToplevelInfoState>,
|
pub(crate) toplevel_info: Option<ToplevelInfoState>,
|
||||||
pub(crate) toplevel_manager: Option<ToplevelManagerState>,
|
pub(crate) toplevel_manager: Option<ToplevelManagerState>,
|
||||||
|
pub(crate) subsurface_state: Option<SubsurfaceState>,
|
||||||
|
|
||||||
pub(crate) activation_token_ctr: u32,
|
pub(crate) activation_token_ctr: u32,
|
||||||
pub(crate) token_senders: HashMap<u32, oneshot::Sender<Option<String>>>,
|
pub(crate) token_senders: HashMap<u32, oneshot::Sender<Option<String>>>,
|
||||||
|
|
@ -431,6 +472,22 @@ pub enum LayerSurfaceCreationError {
|
||||||
LayerSurfaceCreationFailed(GlobalError),
|
LayerSurfaceCreationFailed(GlobalError),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An error that occurred while running an application.
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum SubsurfaceCreationError {
|
||||||
|
/// Subsurface creation failed
|
||||||
|
#[error("Subsurface creation failed")]
|
||||||
|
CreationFailed(GlobalError),
|
||||||
|
|
||||||
|
/// The specified parent is missing
|
||||||
|
#[error("The specified parent is missing")]
|
||||||
|
ParentMissing,
|
||||||
|
|
||||||
|
/// Subsurfaces are unsupported
|
||||||
|
#[error("Subsurfaces are unsupported")]
|
||||||
|
Unsupported,
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn receive_frame(
|
pub(crate) fn receive_frame(
|
||||||
frame_status: &mut HashMap<ObjectId, FrameStatus>,
|
frame_status: &mut HashMap<ObjectId, FrameStatus>,
|
||||||
s: &WlSurface,
|
s: &WlSurface,
|
||||||
|
|
@ -460,6 +517,18 @@ impl SctkState {
|
||||||
) {
|
) {
|
||||||
let mut id = None;
|
let mut id = None;
|
||||||
|
|
||||||
|
for subsurface in &self.subsurfaces {
|
||||||
|
if subsurface.instance.parent != surface.id() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.sctk_events.push(SctkEvent::SurfaceScaleFactorChanged(
|
||||||
|
scale_factor,
|
||||||
|
surface.clone(),
|
||||||
|
subsurface.id,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(popup) = self
|
if let Some(popup) = self
|
||||||
.popups
|
.popups
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
|
|
@ -688,6 +757,19 @@ impl SctkState {
|
||||||
log::error!("Can't take grab on popup. Missing serial.");
|
log::error!("Can't take grab on popup. Missing serial.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(z) = settings.input_zone {
|
||||||
|
let region = self
|
||||||
|
.compositor_state
|
||||||
|
.wl_compositor()
|
||||||
|
.create_region(&self.queue_handle, ());
|
||||||
|
region.add(
|
||||||
|
z.x.round() as i32,
|
||||||
|
z.y.round() as i32,
|
||||||
|
z.width.round() as i32,
|
||||||
|
z.height.round() as i32,
|
||||||
|
);
|
||||||
|
}
|
||||||
popup.xdg_surface().set_window_geometry(
|
popup.xdg_surface().set_window_geometry(
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
|
|
@ -719,11 +801,13 @@ impl SctkState {
|
||||||
parent: parent.clone(),
|
parent: parent.clone(),
|
||||||
toplevel: toplevel.clone(),
|
toplevel: toplevel.clone(),
|
||||||
positioner: positioner.clone(),
|
positioner: positioner.clone(),
|
||||||
|
grab: settings.grab,
|
||||||
},
|
},
|
||||||
last_configure: None,
|
last_configure: None,
|
||||||
_pending_requests: Default::default(),
|
_pending_requests: Default::default(),
|
||||||
wp_fractional_scale,
|
wp_fractional_scale,
|
||||||
common: common.clone(),
|
common: common.clone(),
|
||||||
|
close_with_children: settings.close_with_children,
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
|
|
@ -863,15 +947,16 @@ impl SctkState {
|
||||||
self.fractional_scaling_manager.as_ref().map(|fsm| {
|
self.fractional_scaling_manager.as_ref().map(|fsm| {
|
||||||
fsm.fractional_scaling(&wl_surface, &self.queue_handle)
|
fsm.fractional_scaling(&wl_surface, &self.queue_handle)
|
||||||
});
|
});
|
||||||
let common =
|
let mut common = Common::from(LogicalSize::new(1, 1));
|
||||||
Arc::new(Mutex::new(Common::from(LogicalSize::new(1, 1))));
|
common.wp_viewport = wp_viewport;
|
||||||
|
let common = Arc::new(Mutex::new(common));
|
||||||
self.lock_surfaces.push(SctkLockSurface {
|
self.lock_surfaces.push(SctkLockSurface {
|
||||||
id,
|
id,
|
||||||
session_lock_surface: session_lock_surface.clone(),
|
session_lock_surface: session_lock_surface.clone(),
|
||||||
last_configure: None,
|
last_configure: None,
|
||||||
wp_fractional_scale,
|
wp_fractional_scale,
|
||||||
wp_viewport,
|
|
||||||
common: common.clone(),
|
common: common.clone(),
|
||||||
|
output: output.clone(),
|
||||||
});
|
});
|
||||||
Some((CommonSurface::Lock(session_lock_surface), common))
|
Some((CommonSurface::Lock(session_lock_surface), common))
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -920,6 +1005,25 @@ impl SctkState {
|
||||||
platform_specific::wayland::layer_surface::Action::Destroy(id) => {
|
platform_specific::wayland::layer_surface::Action::Destroy(id) => {
|
||||||
if let Some(i) = self.layer_surfaces.iter().position(|l| l.id == id) {
|
if let Some(i) = self.layer_surfaces.iter().position(|l| l.id == id) {
|
||||||
let l = self.layer_surfaces.remove(i);
|
let l = self.layer_surfaces.remove(i);
|
||||||
|
|
||||||
|
let (removed, remaining): (Vec<_>, Vec<_>) = self
|
||||||
|
.subsurfaces
|
||||||
|
.drain(..)
|
||||||
|
.partition(|s| {
|
||||||
|
s.instance.parent == l.surface.wl_surface().id()
|
||||||
|
});
|
||||||
|
|
||||||
|
self.subsurfaces = remaining;
|
||||||
|
for s in removed
|
||||||
|
{
|
||||||
|
crate::subsurface_widget::remove_iced_subsurface(
|
||||||
|
&s.instance.wl_surface,
|
||||||
|
);
|
||||||
|
send_event(&self.events_sender, &self.proxy,
|
||||||
|
SctkEvent::SubsurfaceEvent( crate::sctk_event::SubsurfaceEventVariant::Destroyed(s.instance) )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(destroyed) = self.id_map.remove(&l.surface.wl_surface().id()) {
|
if let Some(destroyed) = self.id_map.remove(&l.surface.wl_surface().id()) {
|
||||||
_ = self.destroyed.insert(destroyed);
|
_ = self.destroyed.insert(destroyed);
|
||||||
}
|
}
|
||||||
|
|
@ -976,28 +1080,77 @@ impl SctkState {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Action::Popup(action) => match action {
|
Action::Popup(action) => match action {
|
||||||
platform_specific::wayland::popup::Action::Popup { popup, .. } => {
|
platform_specific::wayland::popup::Action::Popup { popup: settings } => {
|
||||||
let parent_mismatch = self.popups.last().is_some_and(|p| {
|
// first check existing popup
|
||||||
self.id_map.get(&p.popup.wl_surface().id()).map_or(true, |p| *p != popup.parent)
|
if let Some(existing) = self.popups.iter().position(|p| p.data.id == settings.id
|
||||||
|
&& (
|
||||||
|
self.popups.iter().any(|parent| parent.popup.wl_surface() == p.data.parent.wl_surface() && parent.data.id == settings.parent)
|
||||||
|
|| self.windows.iter().any(|w| w.id == settings.parent && *p.data.parent.wl_surface() == w.wl_surface(&self.connection))
|
||||||
|
|| self.layer_surfaces.iter().any(|l| l.id == settings.parent && p.data.parent.wl_surface() == l.surface.wl_surface()))
|
||||||
|
) {
|
||||||
|
let existing = &mut self.popups[existing];
|
||||||
|
let size = if settings.positioner.size.is_none() {
|
||||||
|
log::info!("No configured popup size");
|
||||||
|
(1, 1)
|
||||||
|
} else {
|
||||||
|
settings.positioner.size.unwrap()
|
||||||
|
};
|
||||||
|
let Ok(positioner) = XdgPositioner::new(&self.xdg_shell_state)
|
||||||
|
.map_err(PopupCreationError::PositionerCreationFailed) else {
|
||||||
|
log::error!("Failed to create popup positioner");
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
positioner.set_anchor(settings.positioner.anchor);
|
||||||
|
positioner.set_anchor_rect(
|
||||||
|
settings.positioner.anchor_rect.x,
|
||||||
|
settings.positioner.anchor_rect.y,
|
||||||
|
settings.positioner.anchor_rect.width,
|
||||||
|
settings.positioner.anchor_rect.height,
|
||||||
|
);
|
||||||
|
if let Ok(constraint_adjustment) =
|
||||||
|
settings.positioner.constraint_adjustment.try_into()
|
||||||
|
{
|
||||||
|
positioner.set_constraint_adjustment(constraint_adjustment);
|
||||||
|
}
|
||||||
|
positioner.set_gravity(settings.positioner.gravity);
|
||||||
|
positioner.set_offset(
|
||||||
|
settings.positioner.offset.0,
|
||||||
|
settings.positioner.offset.1,
|
||||||
|
);
|
||||||
|
if settings.positioner.reactive {
|
||||||
|
positioner.set_reactive();
|
||||||
|
}
|
||||||
|
positioner.set_size(size.0 as i32, size.1 as i32);
|
||||||
|
existing.data.positioner = Arc::new(positioner);
|
||||||
|
existing.set_size(size.0, size.1, TOKEN_CTR.fetch_add(1, std::sync::atomic::Ordering::Relaxed));
|
||||||
|
_ = send_event(&self.events_sender, &self.proxy,
|
||||||
|
SctkEvent::PopupEvent { variant: crate::sctk_event::PopupEventVariant::Size(size.0, size.1), toplevel_id: existing.data.parent.wl_surface().clone(), parent_id: existing.data.parent.wl_surface().clone(), id: existing.popup.wl_surface().clone() });
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let parent_mismatch = self.popups.iter().rev().find(|p| {
|
||||||
|
self.id_map.get(&p.popup.wl_surface().id()).map_or(true, |p_id|{
|
||||||
|
*p_id != settings.parent && p.data.grab && settings.grab})
|
||||||
});
|
});
|
||||||
if !self.destroyed.is_empty() || parent_mismatch {
|
if !self.destroyed.is_empty() || parent_mismatch.is_some() {
|
||||||
if parent_mismatch {
|
if parent_mismatch.is_some() {
|
||||||
for i in 0..self.popups.len() {
|
for i in 0..self.popups.len() {
|
||||||
let id = self.id_map.get(&self.popups[i].popup.wl_surface().id());
|
let id = self.id_map.get(&self.popups[i].popup.wl_surface().id());
|
||||||
if let Some(id) = id {
|
if let Some(id) = id {
|
||||||
if *id != popup.parent {
|
if *id != settings.parent {
|
||||||
_ = self.handle_action(Action::Popup(platform_specific::wayland::popup::Action::Destroy{id: *id}));
|
_ = self.handle_action(Action::Popup(platform_specific::wayland::popup::Action::Destroy{id: *id}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if self.pending_popup.replace((popup, 0)).is_none() {
|
if self.pending_popup.replace((settings, 0)).is_none() {
|
||||||
|
|
||||||
let timer = cctk::sctk::reexports::calloop::timer::Timer::from_duration(Duration::from_millis(30));
|
let timer = cctk::sctk::reexports::calloop::timer::Timer::from_duration(Duration::from_millis(30));
|
||||||
let queue_handle = self.queue_handle.clone();
|
let queue_handle = self.queue_handle.clone();
|
||||||
_ = self.loop_handle.insert_source(timer, move |_, _, state| {
|
_ = self.loop_handle.insert_source(timer, move |_, _, state| {
|
||||||
let Some((popup, attempt)) = state.pending_popup.take() else {
|
let Some((mut popup, attempt)) = state.pending_popup.take() else {
|
||||||
return TimeoutAction::Drop;
|
return TimeoutAction::Drop;
|
||||||
};
|
};
|
||||||
|
|
||||||
if !state.destroyed.is_empty() || state.popups.last().is_some_and(|p| {
|
if !state.destroyed.is_empty() || state.popups.last().is_some_and(|p| {
|
||||||
state.id_map.get(&p.popup.wl_surface().id()).map_or(true, |p| *p != popup.parent)
|
state.id_map.get(&p.popup.wl_surface().id()).map_or(true, |p| *p != popup.parent)
|
||||||
}) {
|
}) {
|
||||||
|
|
@ -1029,7 +1182,7 @@ impl SctkState {
|
||||||
// log::error!("Invalid popup Id {:?}", popup.id);
|
// log::error!("Invalid popup Id {:?}", popup.id);
|
||||||
} else {
|
} else {
|
||||||
self.pending_popup = None;
|
self.pending_popup = None;
|
||||||
match self.get_popup(popup) {
|
match self.get_popup(settings) {
|
||||||
Ok((id, parent_id, toplevel_id, surface, common)) => {
|
Ok((id, parent_id, toplevel_id, surface, common)) => {
|
||||||
let wl_surface = surface.wl_surface().clone();
|
let wl_surface = surface.wl_surface().clone();
|
||||||
receive_frame(&mut self.frame_status, &wl_surface);
|
receive_frame(&mut self.frame_status, &wl_surface);
|
||||||
|
|
@ -1054,31 +1207,51 @@ impl SctkState {
|
||||||
{
|
{
|
||||||
Some(p) => self.popups.remove(p),
|
Some(p) => self.popups.remove(p),
|
||||||
None => {
|
None => {
|
||||||
log::warn!("No popup to destroy");
|
log::info!("No popup to destroy");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
let mut to_destroy = vec![sctk_popup];
|
let mut to_destroy = vec![sctk_popup];
|
||||||
while let Some(popup_to_destroy) = to_destroy.last() {
|
// TODO optionally destroy parents if they request to be destroyed with children
|
||||||
match popup_to_destroy.data.parent.clone() {
|
while let Some(popup_to_destroy_last) = to_destroy.last().and_then(|popup| self
|
||||||
PopupParent::LayerSurface(_) | PopupParent::Window(_) => {
|
.popups
|
||||||
break;
|
.iter()
|
||||||
}
|
.position(|p| popup.data.parent.wl_surface() == p.popup.wl_surface() && p.close_with_children)) {
|
||||||
PopupParent::Popup(popup_to_destroy_first) => {
|
let popup_to_destroy_last = self.popups.remove(popup_to_destroy_last);
|
||||||
let popup_to_destroy_first = self
|
to_destroy.push(popup_to_destroy_last);
|
||||||
.popups
|
}
|
||||||
.iter()
|
to_destroy.reverse();
|
||||||
.position(|p| p.popup.wl_surface() == &popup_to_destroy_first)
|
|
||||||
.unwrap();
|
while let Some(popup_to_destroy_first) = to_destroy.last().and_then(|popup| self
|
||||||
let popup_to_destroy_first = self.popups.remove(popup_to_destroy_first);
|
.popups
|
||||||
to_destroy.push(popup_to_destroy_first);
|
.iter()
|
||||||
}
|
.position(|p| p.data.parent.wl_surface() == popup.popup.wl_surface())) {
|
||||||
}
|
let popup_to_destroy_first = self.popups.remove(popup_to_destroy_first);
|
||||||
|
to_destroy.push(popup_to_destroy_first);
|
||||||
}
|
}
|
||||||
for popup in to_destroy.into_iter().rev() {
|
for popup in to_destroy.into_iter().rev() {
|
||||||
if let Some(id) = self.id_map.remove(&popup.popup.wl_surface().id()) {
|
if let Some(id) = self.id_map.remove(&popup.popup.wl_surface().id()) {
|
||||||
_ = self.destroyed.insert(id);
|
_ = self.destroyed.insert(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let (removed, remaining): (Vec<_>, Vec<_>) = self
|
||||||
|
.subsurfaces
|
||||||
|
.drain(..)
|
||||||
|
.partition(|s| {
|
||||||
|
s.instance.parent == popup.popup.wl_surface().id()
|
||||||
|
});
|
||||||
|
|
||||||
|
self.subsurfaces = remaining;
|
||||||
|
for s in removed
|
||||||
|
{
|
||||||
|
|
||||||
|
crate::subsurface_widget::remove_iced_subsurface(
|
||||||
|
&s.instance.wl_surface,
|
||||||
|
);
|
||||||
|
send_event(&self.events_sender, &self.proxy,
|
||||||
|
SctkEvent::SubsurfaceEvent( crate::sctk_event::SubsurfaceEventVariant::Destroyed(s.instance) )
|
||||||
|
);
|
||||||
|
}
|
||||||
_ = send_event(&self.events_sender, &self.proxy,
|
_ = send_event(&self.events_sender, &self.proxy,
|
||||||
SctkEvent::PopupEvent { variant: crate::sctk_event::PopupEventVariant::Done, toplevel_id: popup.data.toplevel.clone(), parent_id: popup.data.parent.wl_surface().clone(), id: popup.popup.wl_surface().clone() });
|
SctkEvent::PopupEvent { variant: crate::sctk_event::PopupEventVariant::Done, toplevel_id: popup.data.toplevel.clone(), parent_id: popup.data.parent.wl_surface().clone(), id: popup.popup.wl_surface().clone() });
|
||||||
}
|
}
|
||||||
|
|
@ -1165,11 +1338,15 @@ impl SctkState {
|
||||||
send_event(&self.events_sender, &self.proxy, SctkEvent::SessionUnlocked);
|
send_event(&self.events_sender, &self.proxy, SctkEvent::SessionUnlocked);
|
||||||
}
|
}
|
||||||
platform_specific::wayland::session_lock::Action::LockSurface { id, output } => {
|
platform_specific::wayland::session_lock::Action::LockSurface { id, output } => {
|
||||||
|
// Should we panic if the id does not match?
|
||||||
|
if self.lock_surfaces.iter().any(|s| s.output == output) {
|
||||||
|
tracing::warn!("Cannot create multiple lock surfaces for a single output.");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
// TODO how to handle this when there's no lock?
|
// TODO how to handle this when there's no lock?
|
||||||
if let Some((surface, common)) = self.get_lock_surface(id, &output) {
|
if let Some((surface, _)) = self.get_lock_surface(id, &output) {
|
||||||
let wl_surface = surface.wl_surface();
|
let wl_surface = surface.wl_surface();
|
||||||
receive_frame(&mut self.frame_status, &wl_surface);
|
receive_frame(&mut self.frame_status, &wl_surface);
|
||||||
send_event(&self.events_sender, &self.proxy, SctkEvent::SessionLockSurfaceCreated { queue_handle: self.queue_handle.clone(), surface, native_id: id, common, display: self.connection.display() });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
platform_specific::wayland::session_lock::Action::DestroyLockSurface { id } => {
|
platform_specific::wayland::session_lock::Action::DestroyLockSurface { id } => {
|
||||||
|
|
@ -1179,6 +1356,24 @@ impl SctkState {
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
let surface = self.lock_surfaces.remove(i);
|
let surface = self.lock_surfaces.remove(i);
|
||||||
|
let (removed, remaining): (Vec<_>, Vec<_>) = self
|
||||||
|
.subsurfaces
|
||||||
|
.drain(..)
|
||||||
|
.partition(|s| {
|
||||||
|
s.instance.parent == surface.session_lock_surface.wl_surface().id()
|
||||||
|
});
|
||||||
|
|
||||||
|
self.subsurfaces = remaining;
|
||||||
|
for s in removed
|
||||||
|
{
|
||||||
|
|
||||||
|
crate::subsurface_widget::remove_iced_subsurface(
|
||||||
|
&s.instance.wl_surface,
|
||||||
|
);
|
||||||
|
send_event(&self.events_sender, &self.proxy,
|
||||||
|
SctkEvent::SubsurfaceEvent( crate::sctk_event::SubsurfaceEventVariant::Destroyed(s.instance) )
|
||||||
|
);
|
||||||
|
}
|
||||||
if let Some(id) = self.id_map.remove(&surface.session_lock_surface.wl_surface().id()) {
|
if let Some(id) = self.id_map.remove(&surface.session_lock_surface.wl_surface().id()) {
|
||||||
_ = self.destroyed.insert(id);
|
_ = self.destroyed.insert(id);
|
||||||
}
|
}
|
||||||
|
|
@ -1208,9 +1403,254 @@ impl SctkState {
|
||||||
tracing::error!("Overlap notify subscription cannot be created for surface. No matching layer surface found.");
|
tracing::error!("Overlap notify subscription cannot be created for surface. No matching layer surface found.");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Action::Subsurface(action) => match action {
|
||||||
|
platform_specific::wayland::subsurface::Action::Subsurface { subsurface: subsurface_settings } => {
|
||||||
|
let parent_id = subsurface_settings.parent;
|
||||||
|
if let Ok((_, parent, subsurface, common_surface, common)) = self.get_subsurface(subsurface_settings.clone()) {
|
||||||
|
// TODO Ashley: all surfaces should probably have an optional title for a11y if nothing else
|
||||||
|
receive_frame(&mut self.frame_status, &subsurface);
|
||||||
|
send_event(&self.events_sender, &self.proxy,
|
||||||
|
SctkEvent::SubsurfaceEvent (crate::sctk_event::SubsurfaceEventVariant::Created{
|
||||||
|
parent_id,
|
||||||
|
parent,
|
||||||
|
surface: subsurface,
|
||||||
|
qh: self.queue_handle.clone(),
|
||||||
|
common_surface,
|
||||||
|
surface_id: subsurface_settings.id,
|
||||||
|
common,
|
||||||
|
display: self.connection.display(),
|
||||||
|
z: subsurface_settings.z,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
platform_specific::wayland::subsurface::Action::Destroy { id } => {
|
||||||
|
let mut destroyed = vec![];
|
||||||
|
if let Some(subsurface) = self.subsurfaces.iter().position(|s| s.id == id) {
|
||||||
|
let subsurface = self.subsurfaces.remove(subsurface);
|
||||||
|
destroyed.push((subsurface.instance.wl_surface.clone(), subsurface.instance.parent.clone()));
|
||||||
|
|
||||||
|
subsurface.instance.wl_surface.attach(None, 0, 0);
|
||||||
|
subsurface.instance.wl_surface.commit();
|
||||||
|
send_event(&self.events_sender, &self.proxy,
|
||||||
|
SctkEvent::SubsurfaceEvent( crate::sctk_event::SubsurfaceEventVariant::Destroyed(subsurface.instance) )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
for (destroyed, parent) in destroyed {
|
||||||
|
if let Some((wl_surface, f)) = self.seats.iter_mut().find(|f| {
|
||||||
|
f.kbd_focus.as_ref().is_some_and(|f| *f == destroyed)
|
||||||
|
}).and_then(|f| WlSurface::from_id(&self.connection, parent).ok().map(|wl| (wl, &mut f.kbd_focus))) {
|
||||||
|
*f = Some(wl_surface);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_subsurface(
|
||||||
|
&mut self,
|
||||||
|
settings: SctkSubsurfaceSettings,
|
||||||
|
) -> Result<
|
||||||
|
(
|
||||||
|
core::window::Id,
|
||||||
|
WlSurface,
|
||||||
|
WlSurface,
|
||||||
|
CommonSurface,
|
||||||
|
Arc<Mutex<Common>>,
|
||||||
|
),
|
||||||
|
SubsurfaceCreationError,
|
||||||
|
> {
|
||||||
|
let Some(subsurface_state) = self.subsurface_state.as_ref() else {
|
||||||
|
return Err(SubsurfaceCreationError::Unsupported);
|
||||||
|
};
|
||||||
|
|
||||||
|
let size = settings.size.unwrap_or(Size::new(1., 1.));
|
||||||
|
let half_w = size.width / 2.;
|
||||||
|
let half_h = size.height / 2.;
|
||||||
|
|
||||||
|
let mut loc = settings.loc;
|
||||||
|
match settings.gravity {
|
||||||
|
wayland_protocols::xdg::shell::client::xdg_positioner::Gravity::None => {
|
||||||
|
// center on
|
||||||
|
loc.x -= half_w;
|
||||||
|
loc.y -= half_h;
|
||||||
|
},
|
||||||
|
wayland_protocols::xdg::shell::client::xdg_positioner::Gravity::Top => {
|
||||||
|
loc.x -= half_w;
|
||||||
|
loc.y -= size.height;
|
||||||
|
},
|
||||||
|
wayland_protocols::xdg::shell::client::xdg_positioner::Gravity::Bottom => {
|
||||||
|
loc.x -= half_w;
|
||||||
|
},
|
||||||
|
wayland_protocols::xdg::shell::client::xdg_positioner::Gravity::Left => {
|
||||||
|
loc.y -= half_h;
|
||||||
|
loc.x -= size.width;
|
||||||
|
},
|
||||||
|
wayland_protocols::xdg::shell::client::xdg_positioner::Gravity::Right => {
|
||||||
|
loc.y -= half_h;
|
||||||
|
},
|
||||||
|
wayland_protocols::xdg::shell::client::xdg_positioner::Gravity::TopLeft => {
|
||||||
|
loc.y -= size.height;
|
||||||
|
loc.x -= size.width;
|
||||||
|
},
|
||||||
|
wayland_protocols::xdg::shell::client::xdg_positioner::Gravity::BottomLeft => {
|
||||||
|
loc.x -= size.width;
|
||||||
|
},
|
||||||
|
wayland_protocols::xdg::shell::client::xdg_positioner::Gravity::TopRight => {
|
||||||
|
loc.y -= size.height;
|
||||||
|
},
|
||||||
|
wayland_protocols::xdg::shell::client::xdg_positioner::Gravity::BottomRight => {},
|
||||||
|
_ => unimplemented!(),
|
||||||
|
};
|
||||||
|
let bounds = Rectangle::new(loc, size);
|
||||||
|
|
||||||
|
let parent = if let Some(parent) =
|
||||||
|
self.layer_surfaces.iter().find(|l| l.id == settings.parent)
|
||||||
|
{
|
||||||
|
PopupParent::LayerSurface(parent.surface.wl_surface().clone())
|
||||||
|
} else if let Some(parent) =
|
||||||
|
self.windows.iter().find(|w| w.id == settings.parent)
|
||||||
|
{
|
||||||
|
PopupParent::Window(parent.wl_surface(&self.connection))
|
||||||
|
} else if let Some(i) = self
|
||||||
|
.popups
|
||||||
|
.iter()
|
||||||
|
.position(|p| p.data.id == settings.parent)
|
||||||
|
{
|
||||||
|
let parent = &self.popups[i];
|
||||||
|
PopupParent::Popup(parent.popup.wl_surface().clone())
|
||||||
|
} else if let Some(i) = self
|
||||||
|
.lock_surfaces
|
||||||
|
.iter()
|
||||||
|
.position(|p| p.id == settings.parent)
|
||||||
|
{
|
||||||
|
let parent = &self.lock_surfaces[i];
|
||||||
|
PopupParent::Popup(parent.session_lock_surface.wl_surface().clone())
|
||||||
|
} else {
|
||||||
|
return Err(SubsurfaceCreationError::ParentMissing);
|
||||||
|
};
|
||||||
|
|
||||||
|
let wl_surface =
|
||||||
|
self.compositor_state.create_surface(&self.queue_handle);
|
||||||
|
_ = self.id_map.insert(wl_surface.id(), settings.id.clone());
|
||||||
|
|
||||||
|
for s in self.seats.iter_mut() {
|
||||||
|
if s.kbd_focus
|
||||||
|
.as_ref()
|
||||||
|
.is_some_and(|f| f == parent.wl_surface())
|
||||||
|
{
|
||||||
|
s.kbd_focus = Some(wl_surface.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let parent_wl_surface = parent.wl_surface();
|
||||||
|
let wl_subsurface = subsurface_state.wl_subcompositor.get_subsurface(
|
||||||
|
&wl_surface,
|
||||||
|
parent_wl_surface,
|
||||||
|
&self.queue_handle,
|
||||||
|
(),
|
||||||
|
);
|
||||||
|
wl_subsurface.set_position(bounds.x as i32, bounds.y as i32);
|
||||||
|
_ = wl_surface.frame(&self.queue_handle, wl_surface.clone());
|
||||||
|
if let Some(zone) = settings.input_zone {
|
||||||
|
let region = self
|
||||||
|
.compositor_state
|
||||||
|
.wl_compositor()
|
||||||
|
.create_region(&self.queue_handle, ());
|
||||||
|
region.add(
|
||||||
|
zone.x.round() as i32,
|
||||||
|
zone.y.round() as i32,
|
||||||
|
zone.width.round() as i32,
|
||||||
|
zone.height.round() as i32,
|
||||||
|
);
|
||||||
|
wl_surface.set_input_region(Some(®ion));
|
||||||
|
region.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
wl_surface.commit();
|
||||||
|
|
||||||
|
let wp_viewport = subsurface_state.wp_viewporter.get_viewport(
|
||||||
|
&wl_surface,
|
||||||
|
&self.queue_handle,
|
||||||
|
cctk::sctk::globals::GlobalData,
|
||||||
|
);
|
||||||
|
|
||||||
|
let wp_alpha_modifier_surface = subsurface_state
|
||||||
|
.wp_alpha_modifier
|
||||||
|
.as_ref()
|
||||||
|
.map(|wp_alpha_modifier| {
|
||||||
|
wp_alpha_modifier.get_surface(
|
||||||
|
&wl_surface,
|
||||||
|
&self.queue_handle,
|
||||||
|
(),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
wp_viewport.set_destination(size.width as i32, size.height as i32);
|
||||||
|
|
||||||
|
let mut common: Common =
|
||||||
|
LogicalSize::new(size.width as u32, size.height as u32).into();
|
||||||
|
let instance = SubsurfaceInstance {
|
||||||
|
wl_surface: wl_surface.clone(),
|
||||||
|
wl_subsurface: wl_subsurface.clone(),
|
||||||
|
wp_viewport: wp_viewport.clone(),
|
||||||
|
wp_alpha_modifier_surface: wp_alpha_modifier_surface,
|
||||||
|
|
||||||
|
wl_buffer: None,
|
||||||
|
bounds: Some(bounds),
|
||||||
|
transform:
|
||||||
|
cctk::wayland_client::protocol::wl_output::Transform::Normal,
|
||||||
|
z: settings.z,
|
||||||
|
parent: parent_wl_surface.id(),
|
||||||
|
};
|
||||||
|
common.wp_viewport = Some(wp_viewport);
|
||||||
|
let common = Arc::new(Mutex::new(common));
|
||||||
|
|
||||||
|
for focus in &mut self.seats {
|
||||||
|
if focus
|
||||||
|
.kbd_focus
|
||||||
|
.as_ref()
|
||||||
|
.is_some_and(|s| s == parent_wl_surface)
|
||||||
|
{
|
||||||
|
let id = winit::window::WindowId::from_raw(
|
||||||
|
wl_surface.id().as_ptr() as usize,
|
||||||
|
);
|
||||||
|
self.sctk_events.push(SctkEvent::Winit(
|
||||||
|
id,
|
||||||
|
winit::event::WindowEvent::Focused(true),
|
||||||
|
));
|
||||||
|
self.sctk_events.push(SctkEvent::KeyboardEvent {
|
||||||
|
variant: KeyboardEventVariant::Enter(wl_surface.clone()),
|
||||||
|
kbd_id: focus.kbd.clone().unwrap(),
|
||||||
|
seat_id: focus.seat.clone(),
|
||||||
|
surface: wl_surface.clone(),
|
||||||
|
});
|
||||||
|
focus.kbd_focus = Some(wl_surface.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let id = settings.id;
|
||||||
|
self.subsurfaces.push(SctkSubsurface {
|
||||||
|
common: common.clone(),
|
||||||
|
steals_keyboard_focus: settings.steal_keyboard_focus,
|
||||||
|
id: settings.id,
|
||||||
|
instance,
|
||||||
|
settings,
|
||||||
|
});
|
||||||
|
// XXX subsurfaces need to be sorted by z in descending order
|
||||||
|
self.subsurfaces
|
||||||
|
.sort_by(|a, b| b.instance.z.cmp(&a.instance.z));
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
id,
|
||||||
|
parent.wl_surface().clone(),
|
||||||
|
wl_surface.clone(),
|
||||||
|
CommonSurface::Subsurface {
|
||||||
|
wl_surface,
|
||||||
|
wl_subsurface,
|
||||||
|
},
|
||||||
|
common,
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn send_event(
|
pub(crate) fn send_event(
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
use iced_futures::futures::channel::oneshot::Sender;
|
|
||||||
use cctk::sctk::{
|
use cctk::sctk::{
|
||||||
activation::{ActivationHandler, RequestData, RequestDataExt},
|
activation::{ActivationHandler, RequestData, RequestDataExt},
|
||||||
delegate_activation,
|
delegate_activation,
|
||||||
reexports::client::protocol::{wl_seat::WlSeat, wl_surface::WlSurface},
|
reexports::client::protocol::{wl_seat::WlSeat, wl_surface::WlSurface},
|
||||||
};
|
};
|
||||||
|
use iced_futures::futures::channel::oneshot::Sender;
|
||||||
|
|
||||||
use crate::platform_specific::wayland::event_loop::state::SctkState;
|
use crate::platform_specific::wayland::event_loop::state::SctkState;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ impl KeyboardHandler for SctkState {
|
||||||
_raw: &[u32],
|
_raw: &[u32],
|
||||||
_keysyms: &[Keysym],
|
_keysyms: &[Keysym],
|
||||||
) {
|
) {
|
||||||
self.request_redraw(surface);
|
|
||||||
let (i, mut is_active, seat) = {
|
let (i, mut is_active, seat) = {
|
||||||
let (i, is_active, my_seat) =
|
let (i, is_active, my_seat) =
|
||||||
match self.seats.iter_mut().enumerate().find_map(|(i, s)| {
|
match self.seats.iter_mut().enumerate().find_map(|(i, s)| {
|
||||||
|
|
@ -32,36 +31,49 @@ impl KeyboardHandler for SctkState {
|
||||||
Some((i, s)) => (i, i == 0, s),
|
Some((i, s)) => (i, i == 0, s),
|
||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let surface = if let Some(subsurface) =
|
||||||
|
self.subsurfaces.iter().find(|s| {
|
||||||
|
s.steals_keyboard_focus && s.instance.parent == surface.id()
|
||||||
|
}) {
|
||||||
|
&subsurface.instance.wl_surface
|
||||||
|
} else {
|
||||||
|
surface
|
||||||
|
};
|
||||||
_ = my_seat.kbd_focus.replace(surface.clone());
|
_ = my_seat.kbd_focus.replace(surface.clone());
|
||||||
|
|
||||||
let seat = my_seat.seat.clone();
|
let seat = my_seat.seat.clone();
|
||||||
(i, is_active, seat)
|
(i, is_active, seat)
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO Ashley: thoroughly test this
|
|
||||||
// swap the active seat to be the current seat if the current "active" seat is not focused on the application anyway
|
|
||||||
if !is_active && self.seats[0].kbd_focus.is_none() {
|
if !is_active && self.seats[0].kbd_focus.is_none() {
|
||||||
is_active = true;
|
is_active = true;
|
||||||
self.seats.swap(0, i);
|
self.seats.swap(0, i);
|
||||||
}
|
}
|
||||||
|
self.request_redraw(&surface);
|
||||||
|
|
||||||
if is_active {
|
let surfaces = self.subsurfaces.iter().filter_map(|s| {
|
||||||
let id = winit::window::WindowId::from_raw(
|
(s.instance.parent == surface.id()).then(|| &s.instance.wl_surface)
|
||||||
surface.id().as_ptr() as usize
|
});
|
||||||
);
|
for surface in surfaces.chain(std::iter::once(surface)) {
|
||||||
if self.windows.iter().any(|w| w.window.id() == id) {
|
if is_active {
|
||||||
return;
|
let id = winit::window::WindowId::from_raw(
|
||||||
|
surface.id().as_ptr() as usize,
|
||||||
|
);
|
||||||
|
if self.windows.iter().any(|w| w.window.id() == id) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
self.sctk_events.push(SctkEvent::Winit(
|
||||||
|
id,
|
||||||
|
winit::event::WindowEvent::Focused(true),
|
||||||
|
));
|
||||||
|
self.sctk_events.push(SctkEvent::KeyboardEvent {
|
||||||
|
variant: KeyboardEventVariant::Enter(surface.clone()),
|
||||||
|
kbd_id: keyboard.clone(),
|
||||||
|
seat_id: seat.clone(),
|
||||||
|
surface: surface.clone(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
self.sctk_events.push(SctkEvent::Winit(
|
|
||||||
id,
|
|
||||||
winit::event::WindowEvent::Focused(true),
|
|
||||||
));
|
|
||||||
self.sctk_events.push(SctkEvent::KeyboardEvent {
|
|
||||||
variant: KeyboardEventVariant::Enter(surface.clone()),
|
|
||||||
kbd_id: keyboard.clone(),
|
|
||||||
seat_id: seat,
|
|
||||||
surface: surface.clone(),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -91,38 +103,42 @@ impl KeyboardHandler for SctkState {
|
||||||
_ = my_seat.kbd_focus.take();
|
_ = my_seat.kbd_focus.take();
|
||||||
(is_active, seat, kbd)
|
(is_active, seat, kbd)
|
||||||
};
|
};
|
||||||
|
let surfaces = self.subsurfaces.iter().filter_map(|s| {
|
||||||
if is_active {
|
(s.instance.parent == surface.id()).then(|| &s.instance.wl_surface)
|
||||||
self.sctk_events.push(SctkEvent::KeyboardEvent {
|
});
|
||||||
variant: KeyboardEventVariant::Leave(surface.clone()),
|
for surface in surfaces.chain(std::iter::once(surface)) {
|
||||||
kbd_id: kbd,
|
if is_active {
|
||||||
seat_id: seat,
|
|
||||||
surface: surface.clone(),
|
|
||||||
});
|
|
||||||
// if there is another seat with a keyboard focused on a surface make that the new active seat
|
|
||||||
if let Some(i) =
|
|
||||||
self.seats.iter().position(|s| s.kbd_focus.is_some())
|
|
||||||
{
|
|
||||||
self.seats.swap(0, i);
|
|
||||||
let s = &self.seats[0];
|
|
||||||
let id = winit::window::WindowId::from_raw(
|
|
||||||
surface.id().as_ptr() as usize,
|
|
||||||
);
|
|
||||||
if self.windows.iter().any(|w| w.window.id() == id) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
self.sctk_events.push(SctkEvent::Winit(
|
|
||||||
id,
|
|
||||||
winit::event::WindowEvent::Focused(true),
|
|
||||||
));
|
|
||||||
self.sctk_events.push(SctkEvent::KeyboardEvent {
|
self.sctk_events.push(SctkEvent::KeyboardEvent {
|
||||||
variant: KeyboardEventVariant::Enter(
|
variant: KeyboardEventVariant::Leave(surface.clone()),
|
||||||
s.kbd_focus.clone().unwrap(),
|
kbd_id: kbd.clone(),
|
||||||
),
|
seat_id: seat.clone(),
|
||||||
kbd_id: s.kbd.clone().unwrap(),
|
|
||||||
seat_id: s.seat.clone(),
|
|
||||||
surface: surface.clone(),
|
surface: surface.clone(),
|
||||||
})
|
});
|
||||||
|
// if there is another seat with a keyboard focused on a surface make that the new active seat
|
||||||
|
if let Some(i) =
|
||||||
|
self.seats.iter().position(|s| s.kbd_focus.is_some())
|
||||||
|
{
|
||||||
|
self.seats.swap(0, i);
|
||||||
|
let s = &self.seats[0];
|
||||||
|
let id = winit::window::WindowId::from_raw(
|
||||||
|
surface.id().as_ptr() as usize,
|
||||||
|
);
|
||||||
|
if self.windows.iter().any(|w| w.window.id() == id) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
self.sctk_events.push(SctkEvent::Winit(
|
||||||
|
id,
|
||||||
|
winit::event::WindowEvent::Focused(true),
|
||||||
|
));
|
||||||
|
self.sctk_events.push(SctkEvent::KeyboardEvent {
|
||||||
|
variant: KeyboardEventVariant::Enter(
|
||||||
|
s.kbd_focus.clone().unwrap(),
|
||||||
|
),
|
||||||
|
kbd_id: s.kbd.clone().unwrap(),
|
||||||
|
seat_id: s.seat.clone(),
|
||||||
|
surface: surface.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -150,35 +166,20 @@ impl KeyboardHandler for SctkState {
|
||||||
let kbd_id = keyboard.clone();
|
let kbd_id = keyboard.clone();
|
||||||
_ = my_seat.last_kbd_press.replace((event.clone(), serial));
|
_ = my_seat.last_kbd_press.replace((event.clone(), serial));
|
||||||
if is_active {
|
if is_active {
|
||||||
// FIXME can't create winit key events because of private field
|
|
||||||
// if let Some(id) = id {
|
|
||||||
// let physical_key = raw_keycode_to_physicalkey(event.raw_code);
|
|
||||||
// let (logical_key, location) =
|
|
||||||
// keysym_to_vkey_location(event.keysym);
|
|
||||||
// self.sctk_events.push(SctkEvent::Winit(
|
|
||||||
// id,
|
|
||||||
// winit::event::WindowEvent::KeyboardInput {
|
|
||||||
// device_id: Default::default(),
|
|
||||||
// event: winit::event::KeyEvent {
|
|
||||||
// physical_key,
|
|
||||||
// logical_key,
|
|
||||||
// text: event.utf8.map(|s| s.into()),
|
|
||||||
// location,
|
|
||||||
// state: winit::event::ElementState::Pressed,
|
|
||||||
// repeat: false, // TODO we don't have this info...
|
|
||||||
// },
|
|
||||||
// is_synthetic: false,
|
|
||||||
// },
|
|
||||||
// ))
|
|
||||||
// }
|
|
||||||
if let Some(surface) = my_seat.kbd_focus.clone() {
|
if let Some(surface) = my_seat.kbd_focus.clone() {
|
||||||
self.request_redraw(&surface);
|
self.request_redraw(&surface);
|
||||||
self.sctk_events.push(SctkEvent::KeyboardEvent {
|
let surfaces = self.subsurfaces.iter().filter_map(|s| {
|
||||||
variant: KeyboardEventVariant::Press(event),
|
(s.instance.parent == surface.id())
|
||||||
kbd_id,
|
.then(|| &s.instance.wl_surface)
|
||||||
seat_id,
|
|
||||||
surface,
|
|
||||||
});
|
});
|
||||||
|
for surface in surfaces.chain(std::iter::once(&surface)) {
|
||||||
|
self.sctk_events.push(SctkEvent::KeyboardEvent {
|
||||||
|
variant: KeyboardEventVariant::Press(event.clone()),
|
||||||
|
kbd_id: kbd_id.clone(),
|
||||||
|
seat_id: seat_id.clone(),
|
||||||
|
surface: surface.clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -208,12 +209,18 @@ impl KeyboardHandler for SctkState {
|
||||||
if is_active {
|
if is_active {
|
||||||
if let Some(surface) = my_seat.kbd_focus.clone() {
|
if let Some(surface) = my_seat.kbd_focus.clone() {
|
||||||
self.request_redraw(&surface);
|
self.request_redraw(&surface);
|
||||||
self.sctk_events.push(SctkEvent::KeyboardEvent {
|
let surfaces = self.subsurfaces.iter().filter_map(|s| {
|
||||||
variant: KeyboardEventVariant::Release(event),
|
(s.instance.parent == surface.id())
|
||||||
kbd_id,
|
.then(|| &s.instance.wl_surface)
|
||||||
seat_id,
|
|
||||||
surface,
|
|
||||||
});
|
});
|
||||||
|
for surface in surfaces.chain(std::iter::once(&surface)) {
|
||||||
|
self.sctk_events.push(SctkEvent::KeyboardEvent {
|
||||||
|
variant: KeyboardEventVariant::Release(event.clone()),
|
||||||
|
kbd_id: kbd_id.clone(),
|
||||||
|
seat_id: seat_id.clone(),
|
||||||
|
surface: surface.clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -244,12 +251,20 @@ impl KeyboardHandler for SctkState {
|
||||||
if is_active {
|
if is_active {
|
||||||
if let Some(surface) = my_seat.kbd_focus.clone() {
|
if let Some(surface) = my_seat.kbd_focus.clone() {
|
||||||
self.request_redraw(&surface);
|
self.request_redraw(&surface);
|
||||||
self.sctk_events.push(SctkEvent::KeyboardEvent {
|
let surfaces = self.subsurfaces.iter().filter_map(|s| {
|
||||||
variant: KeyboardEventVariant::Modifiers(modifiers),
|
(s.instance.parent == surface.id())
|
||||||
kbd_id,
|
.then(|| &s.instance.wl_surface)
|
||||||
seat_id,
|
|
||||||
surface,
|
|
||||||
});
|
});
|
||||||
|
for surface in surfaces.chain(std::iter::once(&surface)) {
|
||||||
|
self.sctk_events.push(SctkEvent::KeyboardEvent {
|
||||||
|
variant: KeyboardEventVariant::Modifiers(
|
||||||
|
modifiers.clone(),
|
||||||
|
),
|
||||||
|
kbd_id: kbd_id.clone(),
|
||||||
|
seat_id: seat_id.clone(),
|
||||||
|
surface: surface.clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ use cctk::sctk::{
|
||||||
CursorIcon, PointerEvent, PointerEventKind, PointerHandler,
|
CursorIcon, PointerEvent, PointerEventKind, PointerHandler,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
use iced_futures::core::Point;
|
||||||
use winit::{
|
use winit::{
|
||||||
dpi::PhysicalPosition,
|
dpi::PhysicalPosition,
|
||||||
event::{
|
event::{
|
||||||
|
|
@ -56,6 +57,7 @@ impl PointerHandler for SctkState {
|
||||||
if self.windows.iter().any(|w| w.window.id() == id) {
|
if self.windows.iter().any(|w| w.window.id() == id) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let entry = self
|
let entry = self
|
||||||
.frame_status
|
.frame_status
|
||||||
.entry(e.surface.id())
|
.entry(e.surface.id())
|
||||||
|
|
@ -63,6 +65,7 @@ impl PointerHandler for SctkState {
|
||||||
if matches!(entry, FrameStatus::Received) {
|
if matches!(entry, FrameStatus::Received) {
|
||||||
*entry = FrameStatus::Ready;
|
*entry = FrameStatus::Ready;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let PointerEventKind::Motion { time } = &e.kind {
|
if let PointerEventKind::Motion { time } = &e.kind {
|
||||||
self.sctk_events.push(SctkEvent::PointerEvent {
|
self.sctk_events.push(SctkEvent::PointerEvent {
|
||||||
variant: PointerEvent {
|
variant: PointerEvent {
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ use crate::{
|
||||||
event_loop::state::SctkState, sctk_event::SctkEvent,
|
event_loop::state::SctkState, sctk_event::SctkEvent,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use iced_runtime::core::{touch, Point};
|
|
||||||
use cctk::sctk::{
|
use cctk::sctk::{
|
||||||
delegate_touch,
|
delegate_touch,
|
||||||
reexports::client::{
|
reexports::client::{
|
||||||
|
|
@ -15,6 +14,7 @@ use cctk::sctk::{
|
||||||
},
|
},
|
||||||
seat::touch::TouchHandler,
|
seat::touch::TouchHandler,
|
||||||
};
|
};
|
||||||
|
use iced_runtime::core::{touch, Point};
|
||||||
|
|
||||||
impl TouchHandler for SctkState {
|
impl TouchHandler for SctkState {
|
||||||
fn down(
|
fn down(
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::platform_specific::wayland::{
|
use crate::{
|
||||||
handlers::SctkState, sctk_event::SctkEvent,
|
event_loop::state::CommonSurface,
|
||||||
|
platform_specific::wayland::{handlers::SctkState, sctk_event::SctkEvent},
|
||||||
};
|
};
|
||||||
use cctk::sctk::{
|
use cctk::sctk::{
|
||||||
delegate_session_lock,
|
delegate_session_lock,
|
||||||
|
|
@ -15,8 +16,9 @@ impl SessionLockHandler for SctkState {
|
||||||
&mut self,
|
&mut self,
|
||||||
_conn: &Connection,
|
_conn: &Connection,
|
||||||
_qh: &QueueHandle<Self>,
|
_qh: &QueueHandle<Self>,
|
||||||
_session_lock: SessionLock,
|
session_lock: SessionLock,
|
||||||
) {
|
) {
|
||||||
|
self.session_lock = Some(session_lock);
|
||||||
self.sctk_events.push(SctkEvent::SessionLocked);
|
self.sctk_events.push(SctkEvent::SessionLocked);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -44,8 +46,21 @@ impl SessionLockHandler for SctkState {
|
||||||
Some(l) => l,
|
Some(l) => l,
|
||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
|
lock_surface
|
||||||
|
.update_viewport(configure.new_size.0, configure.new_size.1);
|
||||||
|
|
||||||
let first = lock_surface.last_configure.is_none();
|
let first = lock_surface.last_configure.is_none();
|
||||||
_ = lock_surface.last_configure.replace(configure.clone());
|
_ = lock_surface.last_configure.replace(configure.clone());
|
||||||
|
|
||||||
|
self.sctk_events.push(SctkEvent::SessionLockSurfaceCreated {
|
||||||
|
queue_handle: self.queue_handle.clone(),
|
||||||
|
surface: CommonSurface::Lock(
|
||||||
|
lock_surface.session_lock_surface.clone(),
|
||||||
|
),
|
||||||
|
native_id: lock_surface.id,
|
||||||
|
common: lock_surface.common.clone(),
|
||||||
|
display: self.connection.display(),
|
||||||
|
});
|
||||||
self.sctk_events
|
self.sctk_events
|
||||||
.push(SctkEvent::SessionLockSurfaceConfigure {
|
.push(SctkEvent::SessionLockSurfaceConfigure {
|
||||||
surface: session_lock_surface.wl_surface().clone(),
|
surface: session_lock_surface.wl_surface().clone(),
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ use cctk::sctk::{
|
||||||
delegate_xdg_popup, reexports::client::Proxy,
|
delegate_xdg_popup, reexports::client::Proxy,
|
||||||
shell::xdg::popup::PopupHandler,
|
shell::xdg::popup::PopupHandler,
|
||||||
};
|
};
|
||||||
|
use winit::dpi::LogicalSize;
|
||||||
|
|
||||||
impl PopupHandler for SctkState {
|
impl PopupHandler for SctkState {
|
||||||
fn configure(
|
fn configure(
|
||||||
|
|
@ -24,6 +25,9 @@ impl PopupHandler for SctkState {
|
||||||
};
|
};
|
||||||
let first = sctk_popup.last_configure.is_none();
|
let first = sctk_popup.last_configure.is_none();
|
||||||
_ = sctk_popup.last_configure.replace(configure.clone());
|
_ = sctk_popup.last_configure.replace(configure.clone());
|
||||||
|
let mut guard = sctk_popup.common.lock().unwrap();
|
||||||
|
guard.size =
|
||||||
|
LogicalSize::new(configure.width as u32, configure.height as u32);
|
||||||
|
|
||||||
self.sctk_events.push(SctkEvent::PopupEvent {
|
self.sctk_events.push(SctkEvent::PopupEvent {
|
||||||
variant: PopupEventVariant::Configure(
|
variant: PopupEventVariant::Configure(
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
use cctk::{
|
use cctk::{
|
||||||
cosmic_protocols::{
|
cosmic_protocols::toplevel_management::v1::client::zcosmic_toplevel_manager_v1,
|
||||||
toplevel_management::v1::client::zcosmic_toplevel_manager_v1,
|
|
||||||
},
|
|
||||||
toplevel_info::{ToplevelInfoHandler, ToplevelInfoState},
|
toplevel_info::{ToplevelInfoHandler, ToplevelInfoState},
|
||||||
toplevel_management::ToplevelManagerHandler,
|
toplevel_management::ToplevelManagerHandler,
|
||||||
wayland_client::{self, WEnum},
|
wayland_client::{self, WEnum},
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ use std::{collections::HashMap, sync::Arc};
|
||||||
use subsurface_widget::{SubsurfaceInstance, SubsurfaceState};
|
use subsurface_widget::{SubsurfaceInstance, SubsurfaceState};
|
||||||
use wayland_backend::client::ObjectId;
|
use wayland_backend::client::ObjectId;
|
||||||
use wayland_client::{Connection, Proxy};
|
use wayland_client::{Connection, Proxy};
|
||||||
|
use winit::dpi::Size;
|
||||||
use winit::event_loop::OwnedDisplayHandle;
|
use winit::event_loop::OwnedDisplayHandle;
|
||||||
|
|
||||||
pub(crate) enum Action {
|
pub(crate) enum Action {
|
||||||
|
|
@ -34,6 +35,7 @@ pub(crate) enum Action {
|
||||||
TrackWindow(Arc<dyn winit::window::Window>, window::Id),
|
TrackWindow(Arc<dyn winit::window::Window>, window::Id),
|
||||||
RemoveWindow(window::Id),
|
RemoveWindow(window::Id),
|
||||||
Dropped(SurfaceIdWrapper),
|
Dropped(SurfaceIdWrapper),
|
||||||
|
SubsurfaceResize(window::Id, Size),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for Action {
|
impl std::fmt::Debug for Action {
|
||||||
|
|
@ -53,6 +55,11 @@ impl std::fmt::Debug for Action {
|
||||||
f.debug_tuple("RemoveWindow").field(arg0).finish()
|
f.debug_tuple("RemoveWindow").field(arg0).finish()
|
||||||
}
|
}
|
||||||
Self::Dropped(_surface_id_wrapper) => write!(f, "Dropped"),
|
Self::Dropped(_surface_id_wrapper) => write!(f, "Dropped"),
|
||||||
|
Self::SubsurfaceResize(id, size) => f
|
||||||
|
.debug_tuple("SubsurfaceResize")
|
||||||
|
.field(id)
|
||||||
|
.field(size)
|
||||||
|
.finish(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -68,8 +68,10 @@ use cctk::{
|
||||||
xdg::{popup::PopupConfigure, window::WindowConfigure},
|
xdg::{popup::PopupConfigure, window::WindowConfigure},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
wayland_client::protocol::wl_subsurface::WlSubsurface,
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
|
any::Any,
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
num::NonZeroU32,
|
num::NonZeroU32,
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
|
|
@ -79,14 +81,18 @@ use wayland_protocols::{
|
||||||
wp::viewporter::client::wp_viewport::WpViewport,
|
wp::viewporter::client::wp_viewport::WpViewport,
|
||||||
};
|
};
|
||||||
use winit::{
|
use winit::{
|
||||||
dpi::PhysicalSize, event::WindowEvent, event_loop::EventLoopProxy,
|
dpi::{self, PhysicalSize},
|
||||||
|
event::WindowEvent,
|
||||||
|
event_loop::EventLoopProxy,
|
||||||
window::WindowId,
|
window::WindowId,
|
||||||
};
|
};
|
||||||
use xkeysym::Keysym;
|
use xkeysym::Keysym;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
|
SubsurfaceInstance,
|
||||||
event_loop::state::{Common, CommonSurface, SctkState},
|
event_loop::state::{Common, CommonSurface, SctkState},
|
||||||
keymap::raw_keycode_to_physicalkey,
|
keymap::raw_keycode_to_physicalkey,
|
||||||
|
subsurface_widget::remove_iced_subsurface,
|
||||||
winit_window::SctkWinitWindow,
|
winit_window::SctkWinitWindow,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -160,6 +166,8 @@ pub enum SctkEvent {
|
||||||
id: WlSurface,
|
id: WlSurface,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
SubsurfaceEvent(SubsurfaceEventVariant),
|
||||||
|
|
||||||
//
|
//
|
||||||
// output events
|
// output events
|
||||||
//
|
//
|
||||||
|
|
@ -273,6 +281,26 @@ pub enum PopupEventVariant {
|
||||||
ScaleFactorChanged(f64, Option<WpViewport>),
|
ScaleFactorChanged(f64, Option<WpViewport>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum SubsurfaceEventVariant {
|
||||||
|
/// Popup Created
|
||||||
|
Created {
|
||||||
|
parent_id: window::Id,
|
||||||
|
parent: WlSurface,
|
||||||
|
surface: WlSurface,
|
||||||
|
qh: QueueHandle<SctkState>,
|
||||||
|
common_surface: CommonSurface,
|
||||||
|
surface_id: SurfaceId,
|
||||||
|
common: Arc<Mutex<Common>>,
|
||||||
|
display: WlDisplay,
|
||||||
|
z: u32,
|
||||||
|
},
|
||||||
|
/// Destroyed
|
||||||
|
Destroyed(SubsurfaceInstance),
|
||||||
|
/// Resized
|
||||||
|
Resized(SurfaceId, dpi::Size),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum LayerSurfaceEventVariant {
|
pub enum LayerSurfaceEventVariant {
|
||||||
/// sent after creation of the layer surface
|
/// sent after creation of the layer surface
|
||||||
|
|
@ -485,6 +513,7 @@ impl SctkEvent {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
SurfaceIdWrapper::Subsurface(id) => None,
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
events.push((
|
events.push((
|
||||||
|
|
@ -546,6 +575,7 @@ impl SctkEvent {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
SurfaceIdWrapper::Subsurface(_) => None,
|
||||||
}
|
}
|
||||||
.map(|e| (Some(id.inner()), e))
|
.map(|e| (Some(id.inner()), e))
|
||||||
})
|
})
|
||||||
|
|
@ -568,7 +598,6 @@ impl SctkEvent {
|
||||||
let physical_key = raw_keycode_to_physicalkey(ke.raw_code);
|
let physical_key = raw_keycode_to_physicalkey(ke.raw_code);
|
||||||
let physical_key =
|
let physical_key =
|
||||||
crate::conversion::physical_key(physical_key);
|
crate::conversion::physical_key(physical_key);
|
||||||
|
|
||||||
events.push((
|
events.push((
|
||||||
surface_ids.get(&surface.id()).map(|id| id.inner()),
|
surface_ids.get(&surface.id()).map(|id| id.inner()),
|
||||||
iced_runtime::core::Event::Keyboard(
|
iced_runtime::core::Event::Keyboard(
|
||||||
|
|
@ -853,19 +882,19 @@ impl SctkEvent {
|
||||||
events.push((
|
events.push((
|
||||||
Some(id),
|
Some(id),
|
||||||
iced_runtime::core::Event::Window(
|
iced_runtime::core::Event::Window(
|
||||||
window::Event::Resized(
|
window::Event::Opened {
|
||||||
w.state.logical_size(),
|
size: w.state.logical_size(),
|
||||||
),
|
position: Default::default(),
|
||||||
|
},
|
||||||
),
|
),
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
events.push((
|
events.push((
|
||||||
Some(id),
|
Some(id),
|
||||||
iced_runtime::core::Event::Window(
|
iced_runtime::core::Event::Window(
|
||||||
window::Event::Opened {
|
window::Event::Resized(
|
||||||
size: w.state.logical_size(),
|
w.state.logical_size(),
|
||||||
position: Default::default(),
|
),
|
||||||
},
|
|
||||||
),
|
),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
@ -1068,17 +1097,30 @@ impl SctkEvent {
|
||||||
configure.width as f32,
|
configure.width as f32,
|
||||||
configure.height as f32,
|
configure.height as f32,
|
||||||
);
|
);
|
||||||
if let Some(id) =
|
|
||||||
surface_ids.get(&surface.id()).map(|id| id.inner())
|
if let Some((id, w)) =
|
||||||
|
surface_ids.get(&surface.id()).and_then(|id| {
|
||||||
|
window_manager
|
||||||
|
.get_mut(id.inner())
|
||||||
|
.map(|v| (id.inner(), v))
|
||||||
|
})
|
||||||
{
|
{
|
||||||
|
let scale = w.state.scale_factor();
|
||||||
|
let p_w = (configure.width.max(1) as f64 * scale)
|
||||||
|
.ceil()
|
||||||
|
as u32;
|
||||||
|
let p_h = (configure.height.max(1) as f64 * scale)
|
||||||
|
.ceil()
|
||||||
|
as u32;
|
||||||
|
|
||||||
|
w.state.update(
|
||||||
|
program,
|
||||||
|
w.raw.as_ref(),
|
||||||
|
&WindowEvent::SurfaceResized(
|
||||||
|
PhysicalSize::new(p_w, p_h),
|
||||||
|
),
|
||||||
|
);
|
||||||
if first {
|
if first {
|
||||||
events.push((
|
|
||||||
Some(id),
|
|
||||||
iced_runtime::core::Event::Window(
|
|
||||||
window::Event::Resized(size),
|
|
||||||
),
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
events.push((
|
events.push((
|
||||||
Some(id),
|
Some(id),
|
||||||
iced_runtime::core::Event::Window(
|
iced_runtime::core::Event::Window(
|
||||||
|
|
@ -1088,6 +1130,13 @@ impl SctkEvent {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
))
|
))
|
||||||
|
} else {
|
||||||
|
events.push((
|
||||||
|
Some(id),
|
||||||
|
iced_runtime::core::Event::Window(
|
||||||
|
window::Event::Resized(size),
|
||||||
|
),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // TODO
|
} // TODO
|
||||||
|
|
@ -1247,17 +1296,26 @@ impl SctkEvent {
|
||||||
configure.new_size.0 as f32,
|
configure.new_size.0 as f32,
|
||||||
configure.new_size.1 as f32,
|
configure.new_size.1 as f32,
|
||||||
);
|
);
|
||||||
if let Some(id) =
|
if let Some((id, w)) =
|
||||||
surface_ids.get(&surface.id()).map(|id| id.inner())
|
surface_ids.get(&surface.id()).and_then(|id| {
|
||||||
|
window_manager
|
||||||
|
.get_mut(id.inner())
|
||||||
|
.map(|v| (id.inner(), v))
|
||||||
|
})
|
||||||
{
|
{
|
||||||
|
let scale = w.state.scale_factor();
|
||||||
|
let p_w = (configure.new_size.0.max(1) as f64 * scale)
|
||||||
|
.round() as u32;
|
||||||
|
let p_h = (configure.new_size.1.max(1) as f64 * scale)
|
||||||
|
.round() as u32;
|
||||||
|
w.state.update(
|
||||||
|
program,
|
||||||
|
w.raw.as_ref(),
|
||||||
|
&WindowEvent::SurfaceResized(PhysicalSize::new(
|
||||||
|
p_w, p_h,
|
||||||
|
)),
|
||||||
|
);
|
||||||
if first {
|
if first {
|
||||||
events.push((
|
|
||||||
Some(id),
|
|
||||||
iced_runtime::core::Event::Window(
|
|
||||||
window::Event::Resized(size),
|
|
||||||
),
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
events.push((
|
events.push((
|
||||||
Some(id),
|
Some(id),
|
||||||
iced_runtime::core::Event::Window(
|
iced_runtime::core::Event::Window(
|
||||||
|
|
@ -1267,6 +1325,13 @@ impl SctkEvent {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
))
|
))
|
||||||
|
} else {
|
||||||
|
events.push((
|
||||||
|
Some(id),
|
||||||
|
iced_runtime::core::Event::Window(
|
||||||
|
window::Event::Resized(size),
|
||||||
|
),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1375,6 +1440,237 @@ impl SctkEvent {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
SctkEvent::SubsurfaceEvent(variant) => match variant {
|
||||||
|
SubsurfaceEventVariant::Created {
|
||||||
|
parent_id,
|
||||||
|
common_surface,
|
||||||
|
common,
|
||||||
|
z,
|
||||||
|
parent,
|
||||||
|
surface: _,
|
||||||
|
qh,
|
||||||
|
surface_id,
|
||||||
|
display,
|
||||||
|
} => {
|
||||||
|
let CommonSurface::Subsurface {
|
||||||
|
wl_surface,
|
||||||
|
wl_subsurface,
|
||||||
|
} = &common_surface
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if let Some(subsurface_state) = subsurface_state.as_mut() {
|
||||||
|
subsurface_state.new_iced_subsurfaces.push((
|
||||||
|
parent_id,
|
||||||
|
parent.id(),
|
||||||
|
surface_id,
|
||||||
|
wl_subsurface.clone(),
|
||||||
|
wl_surface.clone(),
|
||||||
|
z,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let wrapper = SurfaceIdWrapper::Popup(surface_id);
|
||||||
|
_ = surface_ids.insert(wl_surface.id(), wrapper.clone());
|
||||||
|
let sctk_winit = SctkWinitWindow::new(
|
||||||
|
sctk_tx.clone(),
|
||||||
|
common,
|
||||||
|
wrapper,
|
||||||
|
common_surface,
|
||||||
|
display,
|
||||||
|
qh,
|
||||||
|
);
|
||||||
|
// #[cfg(feature = "a11y")]
|
||||||
|
// {
|
||||||
|
// use crate::a11y::*;
|
||||||
|
// use iced_accessibility::accesskit::{
|
||||||
|
// ActivationHandler, NodeBuilder, NodeId, Role, Tree,
|
||||||
|
// TreeUpdate,
|
||||||
|
// };
|
||||||
|
// use iced_accessibility::accesskit_winit::Adapter;
|
||||||
|
|
||||||
|
// let node_id = iced_runtime::core::id::window_node_id();
|
||||||
|
|
||||||
|
// let activation_handler = WinitActivationHandler {
|
||||||
|
// proxy: control_sender.clone(),
|
||||||
|
// title: String::new(),
|
||||||
|
// };
|
||||||
|
|
||||||
|
// let action_handler = WinitActionHandler {
|
||||||
|
// id: surface_id,
|
||||||
|
// proxy: control_sender.clone(),
|
||||||
|
// };
|
||||||
|
|
||||||
|
// let deactivation_handler = WinitDeactivationHandler {
|
||||||
|
// proxy: control_sender.clone(),
|
||||||
|
// };
|
||||||
|
// _ = adapters.insert(
|
||||||
|
// surface_id,
|
||||||
|
// (
|
||||||
|
// node_id,
|
||||||
|
// Adapter::with_direct_handlers(
|
||||||
|
// sctk_winit.as_ref(),
|
||||||
|
// activation_handler,
|
||||||
|
// action_handler,
|
||||||
|
// deactivation_handler,
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
if clipboard.window_id().is_none() {
|
||||||
|
*clipboard = Clipboard::connect(
|
||||||
|
sctk_winit.clone(),
|
||||||
|
crate::clipboard::ControlSender {
|
||||||
|
sender: control_sender.clone(),
|
||||||
|
proxy: proxy.clone(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let window = window_manager.insert(
|
||||||
|
surface_id,
|
||||||
|
sctk_winit,
|
||||||
|
program,
|
||||||
|
compositor,
|
||||||
|
false, // TODO do we want to get this value here?
|
||||||
|
theme::Mode::None,
|
||||||
|
);
|
||||||
|
let logical_size = window.logical_size();
|
||||||
|
|
||||||
|
let mut ui = crate::build_user_interface(
|
||||||
|
program,
|
||||||
|
user_interface::Cache::default(),
|
||||||
|
&mut window.renderer,
|
||||||
|
logical_size,
|
||||||
|
surface_id,
|
||||||
|
window.raw.clone(),
|
||||||
|
window.prev_dnd_destination_rectangles_count,
|
||||||
|
clipboard,
|
||||||
|
);
|
||||||
|
|
||||||
|
_ = ui.update(
|
||||||
|
&vec![iced_runtime::core::Event::PlatformSpecific(
|
||||||
|
iced_runtime::core::event::PlatformSpecific::Wayland(
|
||||||
|
iced_runtime::core::event::wayland::Event::RequestResize,
|
||||||
|
),
|
||||||
|
)],
|
||||||
|
window.state.cursor(),
|
||||||
|
&mut window.renderer,
|
||||||
|
clipboard,
|
||||||
|
&mut Vec::new(),
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some(requested_size) =
|
||||||
|
clipboard.requested_logical_size.lock().unwrap().take()
|
||||||
|
{
|
||||||
|
let requested_physical_size =
|
||||||
|
winit::dpi::PhysicalSize::new(
|
||||||
|
(requested_size.width as f64
|
||||||
|
* window.state.scale_factor())
|
||||||
|
.ceil() as u32,
|
||||||
|
(requested_size.height as f64
|
||||||
|
* window.state.scale_factor())
|
||||||
|
.ceil() as u32,
|
||||||
|
);
|
||||||
|
let physical_size = window.state.physical_size();
|
||||||
|
if requested_physical_size.width != physical_size.width
|
||||||
|
|| requested_physical_size.height
|
||||||
|
!= physical_size.height
|
||||||
|
{
|
||||||
|
// FIXME what to do when we are stuck in a configure event/resize request loop
|
||||||
|
// We don't have control over how winit handles this.
|
||||||
|
window.resize_enabled = true;
|
||||||
|
|
||||||
|
let s = winit::dpi::Size::Physical(
|
||||||
|
requested_physical_size,
|
||||||
|
);
|
||||||
|
_ = window.raw.request_surface_size(s);
|
||||||
|
window.raw.set_min_surface_size(Some(s));
|
||||||
|
window.raw.set_max_surface_size(Some(s));
|
||||||
|
window.state.synchronize(
|
||||||
|
&program,
|
||||||
|
surface_id,
|
||||||
|
window.raw.as_ref(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
events.push((
|
||||||
|
Some(surface_id),
|
||||||
|
iced_runtime::core::Event::PlatformSpecific(
|
||||||
|
PlatformSpecific::Wayland(
|
||||||
|
wayland::Event::Subsurface(
|
||||||
|
wayland::SubsurfaceEvent::Created,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
let _ = user_interfaces.insert(surface_id, ui);
|
||||||
|
}
|
||||||
|
SubsurfaceEventVariant::Destroyed(instance) => {
|
||||||
|
remove_iced_subsurface(&instance.wl_surface);
|
||||||
|
|
||||||
|
if let Some(id_wrapper) =
|
||||||
|
surface_ids.remove(&instance.wl_surface.id())
|
||||||
|
{
|
||||||
|
_ = user_interfaces.remove(&id_wrapper.inner());
|
||||||
|
|
||||||
|
if let Some(w) =
|
||||||
|
window_manager.remove(id_wrapper.inner())
|
||||||
|
{
|
||||||
|
clipboard.register_dnd_destination(
|
||||||
|
DndSurface(Arc::new(Box::new(w.raw.clone()))),
|
||||||
|
Vec::new(),
|
||||||
|
);
|
||||||
|
if clipboard
|
||||||
|
.window_id()
|
||||||
|
.is_some_and(|id| w.raw.id() == id)
|
||||||
|
{
|
||||||
|
*clipboard = Clipboard::unconnected();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
events.push((
|
||||||
|
Some(id_wrapper.inner()),
|
||||||
|
iced_runtime::core::Event::PlatformSpecific(
|
||||||
|
PlatformSpecific::Wayland(
|
||||||
|
wayland::Event::Subsurface(
|
||||||
|
wayland::SubsurfaceEvent::Destroyed,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if let Some(subsurface_state) = subsurface_state.as_mut() {
|
||||||
|
subsurface_state.unmapped_subsurfaces.push(instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SubsurfaceEventVariant::Resized(id, size) => {
|
||||||
|
if let Some((id, w)) =
|
||||||
|
window_manager.get_mut(id).map(|v| (id, v))
|
||||||
|
{
|
||||||
|
let scale = w.state.scale_factor();
|
||||||
|
let physical_size = size.to_physical(scale);
|
||||||
|
w.state.update(
|
||||||
|
program,
|
||||||
|
w.raw.as_ref(),
|
||||||
|
&WindowEvent::SurfaceResized(PhysicalSize::new(
|
||||||
|
physical_size.width,
|
||||||
|
physical_size.height,
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
|
||||||
|
events.push((
|
||||||
|
Some(id),
|
||||||
|
iced_runtime::core::Event::Window(
|
||||||
|
window::Event::Opened {
|
||||||
|
size: w.state.logical_size(),
|
||||||
|
position: Default::default(),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ use crate::core::{
|
||||||
widget::{self, Widget},
|
widget::{self, Widget},
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
|
borrow::BorrowMut,
|
||||||
cell::RefCell,
|
cell::RefCell,
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
fmt::Debug,
|
fmt::Debug,
|
||||||
|
|
@ -55,6 +56,7 @@ use wayland_protocols::wp::{
|
||||||
wp_viewport::WpViewport, wp_viewporter::WpViewporter,
|
wp_viewport::WpViewport, wp_viewporter::WpViewporter,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
use winit::window::WindowId;
|
||||||
|
|
||||||
use crate::platform_specific::{
|
use crate::platform_specific::{
|
||||||
SurfaceIdWrapper, event_loop::state::SctkState,
|
SurfaceIdWrapper, event_loop::state::SctkState,
|
||||||
|
|
@ -364,7 +366,15 @@ pub struct SubsurfaceState {
|
||||||
pub wp_alpha_modifier: Option<WpAlphaModifierV1>,
|
pub wp_alpha_modifier: Option<WpAlphaModifierV1>,
|
||||||
pub qh: QueueHandle<SctkState>,
|
pub qh: QueueHandle<SctkState>,
|
||||||
pub(crate) buffers: HashMap<WeakBufferSource, Vec<WlBuffer>>,
|
pub(crate) buffers: HashMap<WeakBufferSource, Vec<WlBuffer>>,
|
||||||
pub unmapped_subsurfaces: Vec<SubsurfaceInstance>,
|
pub(crate) unmapped_subsurfaces: Vec<SubsurfaceInstance>,
|
||||||
|
pub new_iced_subsurfaces: Vec<(
|
||||||
|
window::Id,
|
||||||
|
ObjectId,
|
||||||
|
window::Id,
|
||||||
|
WlSubsurface,
|
||||||
|
WlSurface,
|
||||||
|
u32,
|
||||||
|
)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SubsurfaceState {
|
impl SubsurfaceState {
|
||||||
|
|
@ -446,6 +456,8 @@ impl SubsurfaceState {
|
||||||
wl_buffer: None,
|
wl_buffer: None,
|
||||||
bounds: None,
|
bounds: None,
|
||||||
transform: wl_output::Transform::Normal,
|
transform: wl_output::Transform::Normal,
|
||||||
|
z: 0,
|
||||||
|
parent: parent.id(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -461,6 +473,14 @@ impl SubsurfaceState {
|
||||||
//
|
//
|
||||||
// They should be safe to destroy by the next time `update_subsurfaces`
|
// They should be safe to destroy by the next time `update_subsurfaces`
|
||||||
// is run.
|
// is run.
|
||||||
|
ICED_SUBSURFACES.with_borrow_mut(|surfaces| {
|
||||||
|
surfaces.retain(|s| {
|
||||||
|
!self
|
||||||
|
.unmapped_subsurfaces
|
||||||
|
.iter()
|
||||||
|
.any(|unmapped| unmapped.wl_surface == s.4)
|
||||||
|
})
|
||||||
|
});
|
||||||
self.unmapped_subsurfaces.clear();
|
self.unmapped_subsurfaces.clear();
|
||||||
|
|
||||||
// Remove cached `wl_buffers` for any `BufferSource`s that no longer exist.
|
// Remove cached `wl_buffers` for any `BufferSource`s that no longer exist.
|
||||||
|
|
@ -479,11 +499,61 @@ impl SubsurfaceState {
|
||||||
subsurface.unmap();
|
subsurface.unmap();
|
||||||
self.unmapped_subsurfaces.push(subsurface);
|
self.unmapped_subsurfaces.push(subsurface);
|
||||||
}
|
}
|
||||||
|
let needs_sorting = subsurfaces.len() < view_subsurfaces.len()
|
||||||
|
|| !self.new_iced_subsurfaces.is_empty();
|
||||||
|
|
||||||
// Create new subsurfaces if there aren't enough.
|
// Create new subsurfaces if there aren't enough.
|
||||||
while subsurfaces.len() < view_subsurfaces.len() {
|
while subsurfaces.len() < view_subsurfaces.len() {
|
||||||
subsurfaces.push(self.create_subsurface(parent));
|
subsurfaces.push(self.create_subsurface(parent));
|
||||||
}
|
}
|
||||||
// Attach buffers to subsurfaces, set viewports, and commit.
|
if needs_sorting {
|
||||||
|
let mut sorted_subsurfaces: Vec<_> = view_subsurfaces
|
||||||
|
.iter()
|
||||||
|
.zip(subsurfaces.iter_mut())
|
||||||
|
.map(|(_, instance)| {
|
||||||
|
(
|
||||||
|
instance.parent.clone(),
|
||||||
|
instance.wl_subsurface.clone(),
|
||||||
|
instance.wl_surface.clone(),
|
||||||
|
instance.z,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.chain(self.new_iced_subsurfaces.clone().into_iter().map(
|
||||||
|
|(_, parent, _, wl_subsurface, wl_surface, z)| {
|
||||||
|
(parent.clone(), wl_subsurface, wl_surface, z)
|
||||||
|
},
|
||||||
|
))
|
||||||
|
.chain(ICED_SUBSURFACES.with(|surfaces| {
|
||||||
|
let b = surfaces.borrow();
|
||||||
|
let v: Vec<_> = b
|
||||||
|
.iter()
|
||||||
|
.map(move |s| {
|
||||||
|
(s.1.clone(), s.3.clone(), s.4.clone(), s.5)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
v.into_iter()
|
||||||
|
}))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
sorted_subsurfaces.sort_by(|a, b| a.3.cmp(&b.3));
|
||||||
|
|
||||||
|
// Attach buffers to subsurfaces, set viewports, and commit.
|
||||||
|
for i in 1..sorted_subsurfaces.len() {
|
||||||
|
let prev = &sorted_subsurfaces[0..i];
|
||||||
|
let subsurface = &sorted_subsurfaces[i];
|
||||||
|
for prev in prev.iter().rev() {
|
||||||
|
if prev.0 != subsurface.0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
subsurface.1.place_above(&prev.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !self.new_iced_subsurfaces.is_empty() {
|
||||||
|
ICED_SUBSURFACES.with(|surfaces| {
|
||||||
|
surfaces.borrow_mut().append(&mut self.new_iced_subsurfaces);
|
||||||
|
})
|
||||||
|
};
|
||||||
for (subsurface_data, subsurface) in
|
for (subsurface_data, subsurface) in
|
||||||
view_subsurfaces.iter().zip(subsurfaces.iter_mut())
|
view_subsurfaces.iter().zip(subsurfaces.iter_mut())
|
||||||
{
|
{
|
||||||
|
|
@ -540,12 +610,14 @@ impl Drop for SubsurfaceState {
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub(crate) struct SubsurfaceInstance {
|
pub(crate) struct SubsurfaceInstance {
|
||||||
pub(crate) wl_surface: WlSurface,
|
pub(crate) wl_surface: WlSurface,
|
||||||
wl_subsurface: WlSubsurface,
|
pub(crate) wl_subsurface: WlSubsurface,
|
||||||
wp_viewport: WpViewport,
|
pub(crate) wp_viewport: WpViewport,
|
||||||
wp_alpha_modifier_surface: Option<WpAlphaModifierSurfaceV1>,
|
pub(crate) wp_alpha_modifier_surface: Option<WpAlphaModifierSurfaceV1>,
|
||||||
wl_buffer: Option<WlBuffer>,
|
pub(crate) wl_buffer: Option<WlBuffer>,
|
||||||
bounds: Option<Rectangle<f32>>,
|
pub(crate) bounds: Option<Rectangle<f32>>,
|
||||||
transform: wl_output::Transform,
|
pub(crate) transform: wl_output::Transform,
|
||||||
|
pub(crate) z: u32,
|
||||||
|
pub parent: ObjectId,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SubsurfaceInstance {
|
impl SubsurfaceInstance {
|
||||||
|
|
@ -621,6 +693,7 @@ impl SubsurfaceInstance {
|
||||||
self.wl_buffer = Some(buffer);
|
self.wl_buffer = Some(buffer);
|
||||||
self.bounds = Some(info.bounds);
|
self.bounds = Some(info.bounds);
|
||||||
self.transform = info.transform;
|
self.transform = info.transform;
|
||||||
|
self.z = info.z;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unmap(&self) {
|
pub fn unmap(&self) {
|
||||||
|
|
@ -646,16 +719,46 @@ pub(crate) struct SubsurfaceInfo {
|
||||||
pub bounds: Rectangle<f32>,
|
pub bounds: Rectangle<f32>,
|
||||||
pub alpha: f32,
|
pub alpha: f32,
|
||||||
pub transform: wl_output::Transform,
|
pub transform: wl_output::Transform,
|
||||||
|
pub z: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
thread_local! {
|
thread_local! {
|
||||||
static SUBSURFACES: RefCell<Vec<SubsurfaceInfo>> = RefCell::new(Vec::new());
|
static SUBSURFACES: RefCell<Vec<SubsurfaceInfo>> = RefCell::new(Vec::new());
|
||||||
|
static ICED_SUBSURFACES: RefCell<Vec<(window::Id, ObjectId, window::Id, WlSubsurface, WlSurface, u32)>> = RefCell::new(Vec::new());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn take_subsurfaces() -> Vec<SubsurfaceInfo> {
|
pub(crate) fn take_subsurfaces() -> Vec<SubsurfaceInfo> {
|
||||||
SUBSURFACES.with(|subsurfaces| mem::take(&mut *subsurfaces.borrow_mut()))
|
SUBSURFACES.with(|subsurfaces| mem::take(&mut *subsurfaces.borrow_mut()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn subsurface_ids(parent: WindowId) -> Vec<WindowId> {
|
||||||
|
ICED_SUBSURFACES.with(|subsurfaces| {
|
||||||
|
subsurfaces
|
||||||
|
.borrow_mut()
|
||||||
|
.iter()
|
||||||
|
.filter_map(|s| {
|
||||||
|
if winit::window::WindowId::from_raw(s.1.as_ptr() as usize)
|
||||||
|
== parent
|
||||||
|
{
|
||||||
|
Some(winit::window::WindowId::from_raw(
|
||||||
|
s.4.id().as_ptr() as usize
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn remove_iced_subsurface(surface: &WlSurface) {
|
||||||
|
ICED_SUBSURFACES.with(|surfaces| {
|
||||||
|
surfaces
|
||||||
|
.borrow_mut()
|
||||||
|
.retain(|(_, _, _, _, s, _)| s != surface)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub struct Subsurface {
|
pub struct Subsurface {
|
||||||
buffer: SubsurfaceBuffer,
|
buffer: SubsurfaceBuffer,
|
||||||
|
|
@ -664,6 +767,7 @@ pub struct Subsurface {
|
||||||
content_fit: ContentFit,
|
content_fit: ContentFit,
|
||||||
alpha: f32,
|
alpha: f32,
|
||||||
transform: wl_output::Transform,
|
transform: wl_output::Transform,
|
||||||
|
pub z: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Message, Theme, Renderer> Widget<Message, Theme, Renderer> for Subsurface
|
impl<Message, Theme, Renderer> Widget<Message, Theme, Renderer> for Subsurface
|
||||||
|
|
@ -729,6 +833,7 @@ where
|
||||||
bounds: layout.bounds(),
|
bounds: layout.bounds(),
|
||||||
alpha: self.alpha,
|
alpha: self.alpha,
|
||||||
transform: self.transform,
|
transform: self.transform,
|
||||||
|
z: self.z,
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -744,6 +849,7 @@ impl Subsurface {
|
||||||
content_fit: ContentFit::Contain,
|
content_fit: ContentFit::Contain,
|
||||||
alpha: 1.,
|
alpha: 1.,
|
||||||
transform: wl_output::Transform::Normal,
|
transform: wl_output::Transform::Normal,
|
||||||
|
z: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -767,6 +873,11 @@ impl Subsurface {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn z(mut self, z: u32) -> Self {
|
||||||
|
self.z = z;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn transform(mut self, transform: wl_output::Transform) -> Self {
|
pub fn transform(mut self, transform: wl_output::Transform) -> Self {
|
||||||
self.transform = transform;
|
self.transform = transform;
|
||||||
self
|
self
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::platform_specific::wayland::Action;
|
use crate::platform_specific::wayland::Action;
|
||||||
use cctk::sctk::reexports::{
|
use cctk::sctk::reexports::{
|
||||||
calloop::channel,
|
calloop::{LoopHandle, channel},
|
||||||
client::{
|
client::{
|
||||||
Proxy, QueueHandle,
|
Proxy, QueueHandle,
|
||||||
protocol::{wl_display::WlDisplay, wl_surface::WlSurface},
|
protocol::{wl_display::WlDisplay, wl_surface::WlSurface},
|
||||||
|
|
@ -103,14 +103,14 @@ impl winit::window::Window for SctkWinitWindow {
|
||||||
) -> Option<winit::dpi::PhysicalSize<u32>> {
|
) -> Option<winit::dpi::PhysicalSize<u32>> {
|
||||||
let mut guard = self.common.lock().unwrap();
|
let mut guard = self.common.lock().unwrap();
|
||||||
self.request_redraw();
|
self.request_redraw();
|
||||||
let size: LogicalSize<u32> =
|
let logical_size: LogicalSize<u32> =
|
||||||
size.to_logical(guard.fractional_scale.unwrap_or(1.));
|
size.to_logical(guard.fractional_scale.unwrap_or(1.));
|
||||||
match &self.surface {
|
match &self.surface {
|
||||||
CommonSurface::Popup(popup, positioner) => {
|
CommonSurface::Popup(popup, positioner) => {
|
||||||
if size.width == 0 || size.height == 0 {
|
if logical_size.width == 0 || logical_size.height == 0 {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
guard.size = size;
|
guard.size = logical_size;
|
||||||
guard.requested_size.0 = Some(guard.size.width);
|
guard.requested_size.0 = Some(guard.size.width);
|
||||||
guard.requested_size.1 = Some(guard.size.height);
|
guard.requested_size.1 = Some(guard.size.height);
|
||||||
positioner.set_size(
|
positioner.set_size(
|
||||||
|
|
@ -138,16 +138,16 @@ impl winit::window::Window for SctkWinitWindow {
|
||||||
}
|
}
|
||||||
CommonSurface::Layer(layer_surface) => {
|
CommonSurface::Layer(layer_surface) => {
|
||||||
guard.requested_size = (
|
guard.requested_size = (
|
||||||
(size.width > 0).then_some(size.width),
|
(logical_size.width > 0).then_some(logical_size.width),
|
||||||
(size.height > 0).then_some(size.height),
|
(logical_size.height > 0).then_some(logical_size.height),
|
||||||
);
|
);
|
||||||
if size.width > 0 {
|
if logical_size.width > 0 {
|
||||||
guard.size.width = size.width;
|
guard.size.width = logical_size.width;
|
||||||
}
|
}
|
||||||
if size.height > 0 {
|
if logical_size.height > 0 {
|
||||||
guard.size.height = size.height;
|
guard.size.height = logical_size.height;
|
||||||
}
|
}
|
||||||
layer_surface.set_size(size.width, size.height);
|
layer_surface.set_size(logical_size.width, logical_size.height);
|
||||||
if let Some(viewport) = guard.wp_viewport.as_ref() {
|
if let Some(viewport) = guard.wp_viewport.as_ref() {
|
||||||
// Set inner size without the borders.
|
// Set inner size without the borders.
|
||||||
viewport.set_destination(
|
viewport.set_destination(
|
||||||
|
|
@ -157,6 +157,23 @@ impl winit::window::Window for SctkWinitWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CommonSurface::Lock(_) => {}
|
CommonSurface::Lock(_) => {}
|
||||||
|
CommonSurface::Subsurface { .. } => {
|
||||||
|
guard.requested_size = (
|
||||||
|
(logical_size.width > 0).then_some(logical_size.width),
|
||||||
|
(logical_size.height > 0).then_some(logical_size.height),
|
||||||
|
);
|
||||||
|
guard.size = logical_size;
|
||||||
|
if let Some(viewport) = guard.wp_viewport.as_ref() {
|
||||||
|
// Set inner size without the borders.
|
||||||
|
viewport.set_destination(
|
||||||
|
guard.size.width as i32,
|
||||||
|
guard.size.height as i32,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ = self
|
||||||
|
.tx
|
||||||
|
.send(Action::SubsurfaceResize(self.id.inner(), size));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
|
||||||
2365
winit/src/program.rs
2365
winit/src/program.rs
File diff suppressed because it is too large
Load diff
|
|
@ -14,7 +14,7 @@ use crate::core::text;
|
||||||
use crate::core::theme;
|
use crate::core::theme;
|
||||||
use crate::core::time::Instant;
|
use crate::core::time::Instant;
|
||||||
use crate::core::{
|
use crate::core::{
|
||||||
Color, InputMethod, Padding, Point, Rectangle, Size, Text, Vector,
|
Color, Element, InputMethod, Padding, Point, Rectangle, Size, Text, Vector,
|
||||||
};
|
};
|
||||||
use crate::graphics::Compositor;
|
use crate::graphics::Compositor;
|
||||||
use crate::program::{self, Program};
|
use crate::program::{self, Program};
|
||||||
|
|
@ -26,13 +26,17 @@ use winit::monitor::MonitorHandle;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
pub(crate) type ViewFn<M, T, R> = Arc<
|
||||||
|
Box<dyn Fn() -> Option<Element<'static, M, T, R>> + Send + Sync + 'static>,
|
||||||
|
>;
|
||||||
|
|
||||||
pub struct WindowManager<P, C>
|
pub struct WindowManager<P, C>
|
||||||
where
|
where
|
||||||
P: Program,
|
P: Program,
|
||||||
C: Compositor<Renderer = P::Renderer>,
|
C: Compositor<Renderer = P::Renderer>,
|
||||||
P::Theme: theme::Base,
|
P::Theme: theme::Base,
|
||||||
{
|
{
|
||||||
aliases: BTreeMap<winit::window::WindowId, Id>,
|
pub(crate) aliases: BTreeMap<winit::window::WindowId, Id>,
|
||||||
entries: BTreeMap<Id, Window<P, C>>,
|
entries: BTreeMap<Id, Window<P, C>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue