use std::{cell::Cell, ffi::CStr, mem::ManuallyDrop, ptr::NonNull,
rc::{Rc, Weak}, time::Duration, panic, ptr};
use libc::{c_float, c_int, clock_t};
use wayland_sys::server::WAYLAND_SERVER_HANDLE;
use wlroots_sys::{timespec, wl_list, wl_output_subpixel, wl_output_transform, wlr_output,
wlr_output_damage, wlr_output_effective_resolution, wlr_output_enable,
wlr_output_get_gamma_size, wlr_output_make_current, wlr_output_mode,
wlr_output_schedule_frame, wlr_output_set_custom_mode,
wlr_output_set_gamma, wlr_output_set_mode,
wlr_output_render_software_cursors,
wlr_output_set_position, wlr_output_set_scale, wlr_output_set_transform,
wlr_output_swap_buffers, wlr_output_transformed_resolution};
use {area::{Origin, Size},
utils::{self, HandleErr, HandleResult, Handleable, c_to_rust_string},
output::{self, layout},
render::PixmanRegion};
pub use manager::output_handler::*;
pub use manager::output_manager::{OutputBuilder as Builder, BuilderResult};
pub(crate) use manager::output_manager::Manager;
pub type Subpixel = wl_output_subpixel;
pub type Transform = wl_output_transform;
pub(crate) struct OutputState {
pub(crate) output: Option<NonNull<UserOutput>>,
handle: Weak<Cell<bool>>,
damage: NonNull<wlr_output_damage>,
layout_handle: Option<layout::Handle>
}
#[derive(Debug)]
pub struct Output {
liveliness: Rc<Cell<bool>>,
damage: ManuallyDrop<output::Damage>,
output: NonNull<wlr_output>
}
pub type Handle = utils::Handle<NonNull<wlr_output_damage>, wlr_output, Output>;
impl Output {
pub(crate) unsafe fn clone(&self) -> Output {
Output { liveliness: self.liveliness.clone(),
damage: ManuallyDrop::new(self.damage.clone()),
output: self.output }
}
pub(crate) unsafe fn new(output: *mut wlr_output) -> Self {
let output = NonNull::new(output)
.expect("Output pointer was null");
(*output.as_ptr()).data = ptr::null_mut();
let liveliness = Rc::new(Cell::new(false));
let handle = Rc::downgrade(&liveliness);
let damage = ManuallyDrop::new(output::Damage::new(output.as_ptr()));
let damage_ptr = NonNull::new(damage.as_ptr()).unwrap();
let state = Box::new(OutputState { output: None,
handle,
damage: damage_ptr,
layout_handle: None });
(*output.as_ptr()).data = Box::into_raw(state) as *mut _;
Output { liveliness,
damage,
output }
}
pub(crate) unsafe fn set_output_layout<T>(&mut self, layout_handle: T)
where T: Into<Option<layout::Handle>>
{
self.remove_from_output_layout();
let user_data = self.user_data();
if user_data.is_null() {
return
}
let mut data = Box::from_raw(user_data);
data.layout_handle = layout_handle.into();
(*self.output.as_ptr()).data = Box::into_raw(data) as *mut _;
}
unsafe fn user_data(&mut self) -> *mut OutputState {
(*self.output.as_ptr()).data as *mut _
}
pub(crate) unsafe fn clear_output_layout_data(&mut self) {
let user_data = self.user_data();
if user_data.is_null() {
return
}
let mut data = Box::from_raw(user_data);
data.layout_handle = None;
(*self.output.as_ptr()).data = Box::into_raw(data) as *mut _;
}
pub(crate) unsafe fn remove_from_output_layout(&mut self) {
let output_data = self.user_data();
if output_data.is_null() {
return
}
if let Some(layout_handle) = (*output_data).layout_handle.take() {
match layout_handle.run(|layout| layout.remove(self)) {
Ok(_) | Err(HandleErr::AlreadyDropped) => self.clear_output_layout_data(),
Err(HandleErr::AlreadyBorrowed) => {
panic!("Could not add OutputLayout to Output user data!")
}
}
}
}
pub(crate) unsafe fn layout(&mut self) -> Option<layout::Handle> {
let data = self.user_data();
if data.is_null() {
None
} else {
(*data).layout_handle.clone()
}
}
pub fn choose_best_mode(&mut self) {
unsafe {
let modes = &mut (*self.output.as_ptr()).modes as *mut wl_list;
let length = ffi_dispatch!(WAYLAND_SERVER_HANDLE, wl_list_length, modes as _);
if length > 0 {
wlr_log!(WLR_DEBUG, "output added {:?}", self);
let first_mode_ptr: *mut wlr_output_mode;
first_mode_ptr =
container_of!(&mut (*(*modes).prev) as *mut _, wlr_output_mode, link);
wlr_output_set_mode(self.as_ptr(), first_mode_ptr);
}
}
}
pub fn set_mode(&mut self, mode: output::Mode) -> bool {
unsafe { wlr_output_set_mode(self.output.as_ptr(), mode.as_ptr()) }
}
pub fn set_custom_mode(&mut self, size: Size, refresh: i32) -> bool {
unsafe { wlr_output_set_custom_mode(self.output.as_ptr(), size.width, size.height, refresh) }
}
pub fn name(&self) -> String {
unsafe {
CStr::from_ptr((*self.output.as_ptr()).name.as_ptr()).to_string_lossy()
.into_owned()
}
}
pub fn make(&self) -> String {
unsafe {
c_to_rust_string((*self.output.as_ptr()).make.as_ptr()).expect("Could not parse make as UTF-8")
}
}
pub fn model(&self) -> String {
unsafe {
c_to_rust_string((*self.output.as_ptr()).model.as_ptr()).expect("Could not parse model as UTF-8")
}
}
pub fn serial(&self) -> String {
unsafe {
c_to_rust_string((*self.output.as_ptr()).serial.as_ptr()).expect("Could not parse serial as \
UTF-8")
}
}
pub fn enabled(&self) -> bool {
unsafe { (*self.output.as_ptr()).enabled }
}
pub fn scale(&self) -> c_float {
unsafe { (*self.output.as_ptr()).scale }
}
pub fn needs_swap(&self) -> bool {
unsafe { (*self.output.as_ptr()).needs_swap }
}
pub fn refresh_rate(&self) -> i32 {
unsafe { (*self.output.as_ptr()).refresh }
}
pub fn current_mode<'output>(&'output self) -> Option<output::Mode<'output>> {
unsafe {
if (*self.output.as_ptr()).current_mode.is_null() {
None
} else {
Some(output::Mode::new((*self.output.as_ptr()).current_mode))
}
}
}
pub fn layout_space_pos(&self) -> (i32, i32) {
unsafe { ((*self.output.as_ptr()).lx, (*self.output.as_ptr()).ly) }
}
pub fn subpixel(&self) -> Subpixel {
unsafe { (*self.output.as_ptr()).subpixel }
}
pub fn get_transform(&self) -> Transform {
unsafe { (*self.output.as_ptr()).transform }
}
pub fn render_software_cursors<'a, U>(&self, damage: U)
where U: Into<Option<&'a mut PixmanRegion>>
{
unsafe {
let mut damage = damage.into();
let damage = match damage {
Some(ref mut region) => &mut region.region as *mut _,
None => ptr::null_mut()
};
wlr_output_render_software_cursors(self.output.as_ptr(), damage)
}
}
pub fn schedule_frame(&mut self) {
unsafe { wlr_output_schedule_frame(self.output.as_ptr()) }
}
pub unsafe fn make_current(&mut self) -> (bool, Option<c_int>) {
let mut buffer_age = -1;
let res = wlr_output_make_current(self.output.as_ptr(), &mut buffer_age);
let buffer_age = if buffer_age == -1 {
None
} else {
Some(buffer_age)
};
(res, buffer_age)
}
pub unsafe fn swap_buffers<'a, T, U>(&mut self, when: T, damage: U) -> bool
where T: Into<Option<Duration>>,
U: Into<Option<&'a mut PixmanRegion>>
{
let when = when.into().map(|duration| {
timespec { tv_sec: duration.as_secs() as clock_t,
tv_nsec: duration.subsec_nanos() as clock_t }
});
let when_ptr =
when.map(|mut duration| &mut duration as *mut _).unwrap_or_else(|| ptr::null_mut());
let damage = match damage.into() {
Some(region) => &mut region.region as *mut _,
None => ptr::null_mut()
};
wlr_output_swap_buffers(self.output.as_ptr(), when_ptr, damage)
}
pub fn frame_pending(&self) -> bool {
unsafe { (*self.output.as_ptr()).frame_pending }
}
pub fn size(&self) -> (i32, i32) {
unsafe { ((*self.output.as_ptr()).width, (*self.output.as_ptr()).height) }
}
pub fn physical_size(&self) -> (i32, i32) {
unsafe { ((*self.output.as_ptr()).phys_width, (*self.output.as_ptr()).phys_height) }
}
pub fn transformed_resolution(&self) -> (c_int, c_int) {
unsafe {
let (mut x, mut y) = (0, 0);
wlr_output_transformed_resolution(self.output.as_ptr(), &mut x, &mut y);
(x, y)
}
}
pub fn effective_resolution(&self) -> (c_int, c_int) {
unsafe {
let (mut x, mut y) = (0, 0);
wlr_output_effective_resolution(self.output.as_ptr(), &mut x, &mut y);
(x, y)
}
}
pub fn transform_matrix(&self) -> [c_float; 9] {
unsafe { (*self.output.as_ptr()).transform_matrix }
}
pub fn transform(&mut self, transform: Transform) {
unsafe {
wlr_output_set_transform(self.output.as_ptr(), transform);
}
}
pub fn modes<'output>(&'output self) -> Vec<output::Mode<'output>> {
unsafe {
let mut result = vec![];
wl_list_for_each!((*self.output.as_ptr()).modes, link, (mode: wlr_output_mode) => {
result.push(output::Mode::new(mode))
});
result
}
}
pub fn enable(&mut self, enable: bool) -> bool {
unsafe { wlr_output_enable(self.output.as_ptr(), enable) }
}
pub fn set_gamma(&mut self, size: usize, mut r: u16, mut g: u16, mut b: u16) -> bool {
unsafe { wlr_output_set_gamma(self.output.as_ptr(), size, &mut r, &mut g, &mut b) }
}
pub fn get_gamma_size(&self) -> usize {
unsafe { wlr_output_get_gamma_size(self.output.as_ptr()) }
}
pub fn set_position(&mut self, origin: Origin) {
unsafe { wlr_output_set_position(self.output.as_ptr(), origin.x, origin.y) }
}
pub fn set_scale(&mut self, scale: c_float) {
unsafe { wlr_output_set_scale(self.output.as_ptr(), scale) }
}
pub fn damage(&mut self) -> &mut output::Damage {
&mut *self.damage
}
}
impl Drop for Output {
fn drop(&mut self) {
if Rc::strong_count(&self.liveliness) == 1 {
wlr_log!(WLR_DEBUG, "Dropped output {:p}", self.output.as_ptr());
let weak_count = Rc::weak_count(&self.liveliness);
if weak_count > 0 {
wlr_log!(WLR_DEBUG,
"Still {} weak pointers to Output {:p}",
weak_count,
self.output.as_ptr());
}
} else {
return
}
unsafe {
self.remove_from_output_layout();
let _ = Box::from_raw((*self.output.as_ptr()).data as *mut OutputState);
}
}
}
impl Handleable<NonNull<wlr_output_damage>, wlr_output> for Output {
#[doc(hidden)]
unsafe fn from_ptr(output: *mut wlr_output) -> Option<Self> {
let output = NonNull::new(output)?;
let data = Box::from_raw((*output.as_ptr()).data as *mut OutputState);
let handle = data.handle.clone();
let damage = data.damage;
let damage = ManuallyDrop::new(output::Damage::from_ptr(damage.as_ptr()));
(*output.as_ptr()).data = Box::into_raw(data) as *mut _;
Some(Output { liveliness: handle.upgrade().unwrap(),
damage,
output })
}
#[doc(hidden)]
unsafe fn as_ptr(&self) -> *mut wlr_output {
self.output.as_ptr()
}
#[doc(hidden)]
unsafe fn from_handle(handle: &Handle) -> HandleResult<Self> where Self: Sized {
let liveliness = handle.handle
.upgrade()
.ok_or_else(|| HandleErr::AlreadyDropped)?;
let damage_ptr = handle.data.ok_or(HandleErr::AlreadyDropped)?;
let damage = ManuallyDrop::new(output::Damage::from_ptr(damage_ptr.as_ptr()));
Ok(Output { liveliness,
damage,
output: handle.as_non_null() })
}
fn weak_reference(&self) -> Handle {
Handle { ptr: self.output,
handle: Rc::downgrade(&self.liveliness),
data: unsafe { Some(NonNull::new(self.damage.as_ptr()).unwrap()) },
_marker: std::marker::PhantomData }
}
}