use std::sync::{Arc, Mutex};
use wayland_client::protocol::wl_output::{self, Event, WlOutput};
use wayland_client::protocol::wl_registry;
pub use wayland_client::protocol::wl_output::{Subpixel, Transform};
#[derive(Copy, Clone, Debug)]
pub struct Mode {
pub dimensions: (i32, i32),
pub refresh_rate: i32,
pub is_current: bool,
pub is_preferred: bool,
}
#[derive(Clone, Debug)]
pub struct OutputInfo {
pub model: String,
pub make: String,
pub location: (i32, i32),
pub physical_size: (i32, i32),
pub subpixel: Subpixel,
pub transform: Transform,
pub scale_factor: i32,
pub modes: Vec<Mode>,
}
impl OutputInfo {
fn new() -> OutputInfo {
OutputInfo {
model: String::new(),
make: String::new(),
location: (0, 0),
physical_size: (0, 0),
subpixel: Subpixel::Unknown,
transform: Transform::Normal,
scale_factor: 1,
modes: Vec::new(),
}
}
}
struct Inner {
outputs: Vec<(u32, WlOutput, OutputInfo)>,
pending: Vec<(WlOutput, Event)>,
}
impl Inner {
fn merge(&mut self, output: &WlOutput) {
let info = match self
.outputs
.iter_mut()
.find(|&&mut (_, ref o, _)| o.as_ref().equals(output.as_ref()))
{
Some(&mut (_, _, ref mut info)) => info,
None => {
self.pending.retain(|&(ref o, _)| o.as_ref().is_alive());
return;
}
};
while let Some(idx) = self
.pending
.iter()
.position(|&(ref o, _)| o.as_ref().equals(output.as_ref()))
{
let (_, event) = self.pending.swap_remove(idx);
match event {
Event::Geometry {
x,
y,
physical_width,
physical_height,
subpixel,
model,
make,
transform,
} => {
info.location = (x, y);
info.physical_size = (physical_width, physical_height);
info.subpixel = subpixel;
info.transform = transform;
info.model = model;
info.make = make;
}
Event::Scale { factor } => {
info.scale_factor = factor;
}
Event::Done => {
unreachable!();
}
Event::Mode {
width,
height,
refresh,
flags,
} => {
let mut found = false;
if let Some(mode) = info
.modes
.iter_mut()
.find(|m| m.dimensions == (width, height) && m.refresh_rate == refresh)
{
mode.is_preferred = flags.contains(wl_output::Mode::Preferred);
mode.is_current = flags.contains(wl_output::Mode::Current);
found = true;
}
if !found {
info.modes.push(Mode {
dimensions: (width, height),
refresh_rate: refresh,
is_preferred: flags.contains(wl_output::Mode::Preferred),
is_current: flags.contains(wl_output::Mode::Current),
})
}
}
_ => unreachable!(),
}
}
}
}
#[derive(Clone)]
pub struct OutputMgr {
inner: Arc<Mutex<Inner>>,
}
impl OutputMgr {
pub(crate) fn new() -> OutputMgr {
OutputMgr {
inner: Arc::new(Mutex::new(Inner {
outputs: Vec::new(),
pending: Vec::new(),
})),
}
}
pub(crate) fn new_output(&self, id: u32, version: u32, registry: &wl_registry::WlRegistry) {
let inner = self.inner.clone();
let output = registry
.bind(version, id, |output| {
output.implement_closure(
move |event, output| {
let mut inner = inner.lock().unwrap();
if let Event::Done = event {
inner.merge(&output);
} else {
inner.pending.push((output.clone(), event));
if output.as_ref().version() < 2 {
inner.merge(&output);
}
}
},
(),
)
})
.unwrap();
self.inner
.lock()
.unwrap()
.outputs
.push((id, output, OutputInfo::new()));
}
pub(crate) fn output_removed(&self, id: u32) {
let mut inner = self.inner.lock().unwrap();
if let Some(idx) = inner.outputs.iter().position(|&(i, _, _)| i == id) {
let (_, output, _) = inner.outputs.swap_remove(idx);
inner
.pending
.retain(|&(ref o, _)| !o.as_ref().equals(&output.as_ref()));
if output.as_ref().version() >= 3 {
output.release();
}
}
}
pub fn find_id<F, T>(&self, id: u32, f: F) -> Option<T>
where
F: FnOnce(&wl_output::WlOutput, &OutputInfo) -> T,
{
let inner = self.inner.lock().unwrap();
if let Some(&(_, ref proxy, ref info)) = inner.outputs.iter().find(|&&(i, _, _)| i == id) {
Some(f(proxy, info))
} else {
None
}
}
pub fn with_info<F, T>(&self, output: &WlOutput, f: F) -> Option<T>
where
F: FnOnce(u32, &OutputInfo) -> T,
{
let inner = self.inner.lock().unwrap();
if let Some(&(id, _, ref info)) = inner
.outputs
.iter()
.find(|&&(_, ref o, _)| o.as_ref().equals(output.as_ref()))
{
Some(f(id, info))
} else {
None
}
}
pub fn with_all<F, T>(&self, f: F) -> T
where
F: FnOnce(&[(u32, WlOutput, OutputInfo)]) -> T,
{
let inner = self.inner.lock().unwrap();
f(&inner.outputs)
}
}