Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add WlOutput and OutputInfo getters #208

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions examples/sctk_custom_output/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "sctk_custom_output"
version = "0.1.0"
edition.workspace = true
license.workspace = true

[dependencies]
iced = { path = "../..", features = [
"debug",
"lazy",
"wayland",
"winit",
"tokio",
"tiny-skia",
"advanced",
], default-features = false }
tokio = { workspace = true, features = ["time"] }
130 changes: 130 additions & 0 deletions examples/sctk_custom_output/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
use std::env;
use std::time::Duration;

use iced::platform_specific::shell::commands::layer_surface::get_layer_surface;
use iced::platform_specific::shell::commands::output::{
get_output, get_output_info,
};
use iced::runtime::platform_specific::wayland::layer_surface::SctkLayerSurfaceSettings;
use iced::widget::text;
use iced::{daemon, stream};
use iced::{
platform_specific::shell::commands::output::OutputInfo,
runtime::platform_specific::wayland::layer_surface::IcedOutput, Element,
Task,
};

use iced::window::Id;
use tokio::time::sleep;

fn main() -> iced::Result {
daemon("Custom Output", App::update, App::view).run_with(App::new)
}

#[derive(Debug)]
enum Message {
Output(Option<IcedOutput>),
OutputInfo(Option<OutputInfo>),
}

#[derive(Debug)]
struct App {
monitor: Option<String>,
output: IcedOutput,
logical_size: Option<(i32, i32)>,
}

impl App {
fn new() -> (App, Task<Message>) {
let app = App {
monitor: env::var("WL_OUTPUT").ok(),
output: IcedOutput::Active,
logical_size: None,
};

let task = match &app.monitor {
Some(_) => app.try_get_output(),
None => app.open(),
};

(app, task)
}

fn try_get_output(&self) -> Task<Message> {
let monitor = self.monitor.clone();
get_output(move |output_state| {
output_state
.outputs()
.find(|o| {
output_state
.info(o)
.map(|info| info.name == monitor)
.unwrap_or(false)
})
.clone()
})
.map(|optn| Message::Output(optn.map(IcedOutput::Output)))
}

fn try_get_output_info(&self) -> Task<Message> {
let monitor = self.monitor.clone();
get_output_info(move |output_state| {
output_state
.outputs()
.find(|o| {
output_state
.info(o)
.map(|info| info.name == monitor)
.unwrap_or(false)
})
.and_then(|o| output_state.info(&o))
.clone()
})
.map(Message::OutputInfo)
}

fn open(&self) -> Task<Message> {
get_layer_surface(SctkLayerSurfaceSettings {
output: self.output.clone(),
size: match self.logical_size {
Some(size) => {
Some((Some((size.0 / 2) as u32), Some((size.1 / 2) as u32)))
}
None => Some((Some(800), Some(500))),
},
..Default::default()
})
}

fn update(&mut self, msg: Message) -> Task<Message> {
match msg {
Message::Output(optn) => match optn {
Some(output) => {
self.output = output;
self.try_get_output_info()
}
None => Task::stream(stream::channel(1, |_| async {
sleep(Duration::from_millis(500)).await;
}))
.chain(self.try_get_output()),
},
Message::OutputInfo(optn) => match optn {
Some(info) => {
self.logical_size = info.logical_size;
self.open()
}
None => Task::stream(stream::channel(1, |_| async {
sleep(Duration::from_millis(500)).await;
}))
.chain(self.try_get_output_info()),
},
}
}

fn view(&self, _window_id: Id) -> Element<Message> {
match &self.monitor {
Some(monitor) => text!("Opened on monitor {monitor}\nSize: {:?}", self.logical_size),
None => text!("No output specified, try setting WL_OUTPUT=YourMonitor\nDefaulting to size 800x500"),
}.into()
}
}
27 changes: 23 additions & 4 deletions runtime/src/platform_specific/wayland/layer_surface.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
use std::fmt;
use std::{fmt, sync::Arc};

use iced_core::layout::Limits;
use cctk::sctk::{
reexports::client::protocol::wl_output::WlOutput,
shell::wlr_layer::{Anchor, KeyboardInteractivity, Layer},
output::OutputState, reexports::client::protocol::wl_output::WlOutput, shell::wlr_layer::{Anchor, KeyboardInteractivity, Layer}
};

use iced_core::window::Id;

/// output for layer surface
#[derive(Debug, Clone)]
#[derive(Clone)]
pub enum IcedOutput {
/// show on all outputs
All,
/// show on active output
Active,
/// show on a specific output
Output(WlOutput),
// decide on which output to show
GetOutput(Arc<dyn Fn(&Vec<WlOutput>, &OutputState) -> Option<WlOutput> + Send + Sync>),
}

impl Default for IcedOutput {
Expand All @@ -25,6 +26,24 @@ impl Default for IcedOutput {
}
}

impl fmt::Debug for IcedOutput {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
IcedOutput::All => write!(f, "IcedOutput::All"),
IcedOutput::Active => write!(f, "IcedOutput::Active"),
IcedOutput::Output(output) => write!(
f,
"IcedOutput::Output({:?})",
output
),
IcedOutput::GetOutput(_) => write!(
f,
"IcedOutput::GetOutput(Arc<dyn Fn(&Vec<WlOutput>, &OutputState) -> Option<WlOutput> + Send + Sync>)"
),
}
}
}

/// margins of the layer surface
#[derive(Debug, Clone, Copy, Default)]
pub struct IcedMargin {
Expand Down
9 changes: 9 additions & 0 deletions runtime/src/platform_specific/wayland/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ use std::fmt::Debug;

use iced_core::window::Id;

use crate::oneshot;

/// activation Actions
pub mod activation;

Expand All @@ -13,6 +15,8 @@ pub mod layer_surface;
pub mod popup;
/// session locks
pub mod session_lock;
/// Output (monitor) getters
pub mod output;

/// Platform specific actions defined for wayland
pub enum Action {
Expand All @@ -26,6 +30,8 @@ pub enum Action {
SessionLock(session_lock::Action),
/// Overlap Notify
OverlapNotify(Id, bool),
/// Output (monitor) getters
Output(output::Action),
}

impl Debug for Action {
Expand All @@ -44,6 +50,9 @@ impl Debug for Action {
Action::OverlapNotify(id, _) => {
f.debug_tuple("OverlapNotify").field(id).finish()
}
Action::Output(arg0) => {
f.debug_tuple("GetOutput").field(arg0).finish()
}
}
}
}
30 changes: 30 additions & 0 deletions runtime/src/platform_specific/wayland/output.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use std::fmt;

use cctk::{
sctk::output::{OutputInfo, OutputState},
wayland_client::protocol::wl_output::WlOutput,
};

use crate::oneshot;

pub enum Action {
// WlOutput getter
GetOutput {
f: Box<dyn Fn(&OutputState) -> Option<WlOutput> + Send + Sync>,
channel: oneshot::Sender<Option<WlOutput>>,
},
// OutputInfo getter
GetOutputInfo {
f: Box<dyn Fn(&OutputState) -> Option<OutputInfo> + Send + Sync>,
channel: oneshot::Sender<Option<OutputInfo>>,
},
}

impl fmt::Debug for Action {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Action::GetOutput { .. } => write!(f, "Action::GetOutput"),
Action::GetOutputInfo { .. } => write!(f, "Action::GetOutputInfo",),
}
}
}
1 change: 1 addition & 0 deletions winit/src/platform_specific/wayland/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ pub mod layer_surface;
pub mod overlap_notify;
pub mod popup;
pub mod session_lock;
pub mod output;
40 changes: 40 additions & 0 deletions winit/src/platform_specific/wayland/commands/output.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
pub use cctk::sctk::output::{OutputInfo, OutputState};
use iced_runtime::{
platform_specific::{self, wayland},
task, Action, Task,
};
pub use wayland_client::protocol::wl_output::WlOutput;

/// Get a
/// [WlOutput](https://docs.rs/wayland-client/latest/wayland_client/protocol/wl_output/struct.WlOutput.html) by calling a closure on a
/// [&OutputState](https://docs.rs/smithay-client-toolkit/latest/smithay_client_toolkit/output/struct.OutputState.html)
pub fn get_output<F>(f: F) -> Task<Option<WlOutput>>
where
F: Fn(&OutputState) -> Option<WlOutput> + Send + Sync + 'static,
{
task::oneshot(|channel| {
Action::PlatformSpecific(platform_specific::Action::Wayland(
wayland::Action::Output(wayland::output::Action::GetOutput {
f: Box::new(f),
channel,
}),
))
})
}

/// Get a
/// [OutputInfo](https://docs.rs/smithay-client-toolkit/latest/smithay_client_toolkit/output/struct.OutputInfo.html) by calling a closure on a
/// [&OutputState](https://docs.rs/smithay-client-toolkit/latest/smithay_client_toolkit/output/struct.OutputState.html)
pub fn get_output_info<F>(f: F) -> Task<Option<OutputInfo>>
where
F: Fn(&OutputState) -> Option<OutputInfo> + Send + Sync + 'static,
{
task::oneshot(|channel| {
Action::PlatformSpecific(platform_specific::Action::Wayland(
wayland::Action::Output(wayland::output::Action::GetOutputInfo {
f: Box::new(f),
channel,
}),
))
})
}
9 changes: 9 additions & 0 deletions winit/src/platform_specific/wayland/event_loop/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -758,6 +758,7 @@ impl SctkState {
IcedOutput::All => None, // TODO
IcedOutput::Active => None,
IcedOutput::Output(output) => Some(output),
IcedOutput::GetOutput(f) => f(&self.outputs, &self.output_state),
};

let layer_shell = self
Expand Down Expand Up @@ -1208,6 +1209,14 @@ impl SctkState {
tracing::error!("Overlap notify subscription cannot be created for surface. No matching layer surface found.");
}
},
Action::Output(action) => match action {
platform_specific::wayland::output::Action::GetOutput { f, channel } => {
let _ = channel.send(f(&self.output_state));
}
platform_specific::wayland::output::Action::GetOutputInfo { f, channel } => {
let _ = channel.send(f(&self.output_state));
}
}
};
Ok(())
}
Expand Down