use crate::XId;
use crate::XTime;
use crate::CURRENT_TIME;
use crate::XHandle;
use crate::XrandrError;
use crate::screen_resources::ScreenResourcesHandle;
use std::ptr;
use std::slice;
use x11::xrandr;
use std::convert::TryFrom;
#[derive(PartialEq, Eq, Copy, Debug, Clone)]
pub enum Rotation {
Normal = 1,
Left = 2,
Inverted = 4,
Right = 8,
}
impl TryFrom<u16> for Rotation {
type Error = XrandrError;
fn try_from(r: u16) -> Result<Self, Self::Error> {
match r {
1 => Ok(Rotation::Normal),
2 => Ok(Rotation::Left),
4 => Ok(Rotation::Inverted),
8 => Ok(Rotation::Right),
_ => Err(XrandrError::InvalidRotation(r)),
}
}
}
#[derive(Copy, Debug, Clone)]
pub enum Relation {
LeftOf,
RightOf,
Above,
Below,
SameAs,
}
#[derive(PartialEq, Eq, Debug, Clone)]
pub struct Crtc {
pub xid: XId,
pub timestamp: XTime,
pub x: i32,
pub y: i32,
pub width: u32,
pub height: u32,
pub mode: XId,
pub rotation: Rotation,
pub outputs: Vec<XId>,
pub rotations: u16,
pub possible: Vec<XId>,
}
pub(crate) fn normalize_positions(crtcs: &mut Vec<Crtc>) {
if crtcs.is_empty() { return };
let left = crtcs.iter().map(|p| p.x).min().unwrap();
let top = crtcs.iter().map(|p| p.y).min().unwrap();
if (top,left) == (0,0) { return };
for c in crtcs.iter_mut() {
c.offset((-left, -top));
}
}
struct CrtcHandle {
ptr: ptr::NonNull<xrandr::XRRCrtcInfo>
}
impl CrtcHandle {
fn new(handle: &mut XHandle, xid: XId) -> Result<Self, XrandrError> {
let res = ScreenResourcesHandle::new(handle)?;
let raw_ptr = unsafe {
xrandr::XRRGetCrtcInfo(handle.sys.as_ptr(), res.ptr(), xid)
};
let ptr = ptr::NonNull::new(raw_ptr)
.ok_or(XrandrError::GetCrtcInfo(xid))?;
Ok(Self { ptr })
}
}
impl Drop for CrtcHandle {
fn drop(&mut self) {
unsafe { xrandr::XRRFreeCrtcInfo(self.ptr.as_ptr()) };
}
}
impl Crtc {
pub fn from_xid(handle: &mut XHandle, xid: XId)
-> Result<Self,XrandrError>
{
let crtc_info = CrtcHandle::new(handle, xid)?;
let xrandr::XRRCrtcInfo {
timestamp, x, y, width, height, mode, rotation,
noutput, outputs, rotations, npossible, possible
} = unsafe { crtc_info.ptr.as_ref() };
let rotation = Rotation::try_from(*rotation)?;
let outputs = unsafe {
slice::from_raw_parts(*outputs, *noutput as usize) };
let possible = unsafe {
slice::from_raw_parts(*possible, *npossible as usize) };
Ok(Self {
xid,
timestamp: *timestamp,
x: *x,
y: *y,
width: *width,
height: *height,
mode: *mode,
rotation,
outputs: outputs.to_vec(),
rotations: *rotations,
possible: possible.to_vec(),
})
}
pub(crate) fn apply(&mut self, handle: &mut XHandle)
-> Result<(), XrandrError>
{
let outputs = match self.outputs.len() {
0 => std::ptr::null_mut(),
_ => self.outputs.as_mut_ptr(),
};
let res = ScreenResourcesHandle::new(handle)?;
unsafe {
xrandr::XRRSetCrtcConfig(
handle.sys.as_ptr(),
res.ptr(),
self.xid,
CURRENT_TIME,
self.x,
self.y,
self.mode,
self.rotation as u16,
outputs,
i32::try_from(self.outputs.len()).unwrap(),
);
}
Ok(())
}
pub(crate) fn set_disable(&mut self) {
self.x = 0;
self.y = 0;
self.mode = 0;
self.rotation = Rotation::Normal;
self.outputs.clear();
}
#[must_use] pub fn rotated_size(&self, rot: Rotation) -> (u32, u32) {
let (w, h) = (self.width, self.height);
let (old_w, old_h) = match self.rotation {
Rotation::Normal | Rotation::Inverted => (w, h),
Rotation::Left | Rotation::Right => (h, w),
};
match rot {
Rotation::Normal | Rotation::Inverted => (old_w, old_h),
Rotation::Left | Rotation::Right => (old_h, old_w),
}
}
pub(crate) fn max_coordinates(&self) -> (i32, i32) {
assert!(self.x >= 0 && self.y >= 0,
"max_coordinates should be called on normalized crtc");
(self.x + self.width as i32, self.y + self.height as i32)
}
pub(crate) fn offset(&mut self, offset: (i32, i32)) {
let x = i64::from(self.x) + i64::from(offset.0);
let y = i64::from(self.y) + i64::from(offset.1);
assert!(x < i64::from(i32::MAX) && y < i64::from(i32::MAX),
"This offset would cause integer overflow");
assert!(x >= 0 && y >= 0,
"Invalid coordinates after offset");
self.x = i32::try_from(x).unwrap();
self.y = i32::try_from(y).unwrap();
}
}