use std::{
cell::RefCell,
rc::{self, Rc},
sync::{self, Arc, Mutex},
};
use wayland_client::{
protocol::{
wl_output::{self, Event, WlOutput},
wl_registry,
},
Attached, DispatchData, Main,
};
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 id: u32,
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>,
pub obsolete: bool,
}
impl OutputInfo {
fn new(id: u32) -> OutputInfo {
OutputInfo {
id,
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(),
obsolete: false,
}
}
}
type OutputCallback = dyn Fn(WlOutput, &OutputInfo, DispatchData) + Send + Sync;
enum OutputData {
Ready { info: OutputInfo, callbacks: Vec<sync::Weak<OutputCallback>> },
Pending { id: u32, events: Vec<Event>, callbacks: Vec<sync::Weak<OutputCallback>> },
}
type OutputStatusCallback = dyn FnMut(WlOutput, &OutputInfo, DispatchData) + 'static;
pub struct OutputHandler {
outputs: Vec<(u32, Attached<WlOutput>)>,
status_listeners: Rc<RefCell<Vec<rc::Weak<RefCell<OutputStatusCallback>>>>>,
}
impl OutputHandler {
pub fn new() -> OutputHandler {
OutputHandler { outputs: Vec::new(), status_listeners: Rc::new(RefCell::new(Vec::new())) }
}
}
impl crate::environment::MultiGlobalHandler<WlOutput> for OutputHandler {
fn created(
&mut self,
registry: Attached<wl_registry::WlRegistry>,
id: u32,
version: u32,
_: DispatchData,
) {
let version = std::cmp::min(version, 3);
let output = registry.bind::<WlOutput>(version, id);
if version > 1 {
output.as_ref().user_data().set_threadsafe(|| {
Mutex::new(OutputData::Pending { id, events: vec![], callbacks: vec![] })
});
} else {
output.as_ref().user_data().set_threadsafe(|| {
Mutex::new(OutputData::Ready { info: OutputInfo::new(id), callbacks: vec![] })
});
}
let status_listeners_handle = self.status_listeners.clone();
output.quick_assign(move |output, event, ddata| {
process_output_event(output, event, ddata, &status_listeners_handle)
});
self.outputs.push((id, (*output).clone()));
}
fn removed(&mut self, id: u32, mut ddata: DispatchData) {
let status_listeners_handle = self.status_listeners.clone();
self.outputs.retain(|(i, o)| {
if *i != id {
true
} else {
make_obsolete(o, ddata.reborrow(), &status_listeners_handle);
false
}
});
}
fn get_all(&self) -> Vec<Attached<WlOutput>> {
self.outputs.iter().map(|(_, o)| o.clone()).collect()
}
}
fn process_output_event(
output: Main<WlOutput>,
event: Event,
mut ddata: DispatchData,
listeners: &RefCell<Vec<rc::Weak<RefCell<OutputStatusCallback>>>>,
) {
let udata_mutex = output
.as_ref()
.user_data()
.get::<Mutex<OutputData>>()
.expect("SCTK: wl_output has invalid UserData");
let mut udata = udata_mutex.lock().unwrap();
if let Event::Done = event {
let (id, pending_events, mut callbacks) =
if let OutputData::Pending { id, events: ref mut v, callbacks: ref mut cb } = *udata {
(id, std::mem::replace(v, vec![]), std::mem::replace(cb, vec![]))
} else {
return;
};
let mut info = OutputInfo::new(id);
for evt in pending_events {
merge_event(&mut info, evt);
}
notify(&output, &info, ddata.reborrow(), &mut callbacks);
notify_status_listeners(&output, &info, ddata, listeners);
*udata = OutputData::Ready { info, callbacks };
} else {
match *udata {
OutputData::Pending { events: ref mut v, .. } => v.push(event),
OutputData::Ready { ref mut info, ref mut callbacks } => {
merge_event(info, event);
notify(&output, info, ddata, callbacks);
}
}
}
}
fn make_obsolete(
output: &Attached<WlOutput>,
mut ddata: DispatchData,
listeners: &RefCell<Vec<rc::Weak<RefCell<OutputStatusCallback>>>>,
) {
let udata_mutex = output
.as_ref()
.user_data()
.get::<Mutex<OutputData>>()
.expect("SCTK: wl_output has invalid UserData");
let mut udata = udata_mutex.lock().unwrap();
let (id, mut callbacks) = match *udata {
OutputData::Ready { ref mut info, ref mut callbacks } => {
info.obsolete = true;
notify(output, info, ddata.reborrow(), callbacks);
notify_status_listeners(&output, info, ddata, listeners);
return;
}
OutputData::Pending { id, callbacks: ref mut cb, .. } => {
(id, std::mem::replace(cb, vec![]))
}
};
let mut info = OutputInfo::new(id);
info.obsolete = true;
notify(output, &info, ddata.reborrow(), &mut callbacks);
notify_status_listeners(&output, &info, ddata, listeners);
*udata = OutputData::Ready { info, callbacks };
}
fn merge_event(info: &mut OutputInfo, event: Event) {
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::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),
})
}
}
_ => (),
}
}
fn notify(
output: &WlOutput,
info: &OutputInfo,
mut ddata: DispatchData,
callbacks: &mut Vec<sync::Weak<OutputCallback>>,
) {
callbacks.retain(|weak| {
if let Some(arc) = sync::Weak::upgrade(weak) {
(*arc)(output.clone(), info, ddata.reborrow());
true
} else {
false
}
});
}
fn notify_status_listeners(
output: &Attached<WlOutput>,
info: &OutputInfo,
mut ddata: DispatchData,
listeners: &RefCell<Vec<rc::Weak<RefCell<OutputStatusCallback>>>>,
) {
listeners.borrow_mut().retain(|lst| {
if let Some(cb) = rc::Weak::upgrade(lst) {
(&mut *cb.borrow_mut())(output.detach(), info, ddata.reborrow());
true
} else {
false
}
})
}
pub fn with_output_info<T, F: FnOnce(&OutputInfo) -> T>(output: &WlOutput, f: F) -> Option<T> {
if let Some(ref udata_mutex) = output.as_ref().user_data().get::<Mutex<OutputData>>() {
let udata = udata_mutex.lock().unwrap();
match *udata {
OutputData::Ready { ref info, .. } => Some(f(info)),
OutputData::Pending { .. } => None,
}
} else {
None
}
}
pub fn add_output_listener<F: Fn(WlOutput, &OutputInfo, DispatchData) + Send + Sync + 'static>(
output: &WlOutput,
f: F,
) -> OutputListener {
let arc = Arc::new(f) as Arc<_>;
if let Some(udata_mutex) = output.as_ref().user_data().get::<Mutex<OutputData>>() {
let mut udata = udata_mutex.lock().unwrap();
match *udata {
OutputData::Pending { ref mut callbacks, .. } => {
callbacks.push(Arc::downgrade(&arc));
}
OutputData::Ready { ref mut callbacks, .. } => {
callbacks.push(Arc::downgrade(&arc));
}
}
}
OutputListener { _cb: arc }
}
pub struct OutputListener {
_cb: Arc<dyn Fn(WlOutput, &OutputInfo, DispatchData) + Send + Sync + 'static>,
}
pub struct OutputStatusListener {
_cb: Rc<RefCell<OutputStatusCallback>>,
}
pub trait OutputHandling {
fn listen<F: FnMut(WlOutput, &OutputInfo, DispatchData) + 'static>(
&mut self,
f: F,
) -> OutputStatusListener;
}
impl OutputHandling for OutputHandler {
fn listen<F: FnMut(WlOutput, &OutputInfo, DispatchData) + 'static>(
&mut self,
f: F,
) -> OutputStatusListener {
let rc = Rc::new(RefCell::new(f)) as Rc<_>;
self.status_listeners.borrow_mut().push(Rc::downgrade(&rc));
OutputStatusListener { _cb: rc }
}
}
impl<E: OutputHandling> crate::environment::Environment<E> {
#[must_use = "the returned OutputStatusListener keeps your callback alive, dropping it will disable it"]
pub fn listen_for_outputs<F: FnMut(WlOutput, &OutputInfo, DispatchData) + 'static>(
&self,
f: F,
) -> OutputStatusListener {
self.with_inner(move |inner| OutputHandling::listen(inner, f))
}
}
impl<E: crate::environment::MultiGlobalHandler<WlOutput>> crate::environment::Environment<E> {
pub fn get_all_outputs(&self) -> Vec<WlOutput> {
self.get_all_globals::<WlOutput>().into_iter().map(|o| o.detach()).collect()
}
}