use super::{Screen, WindowHandle, XlibError, MAX_PROPERTY_VALUE_LEN, MOUSEMASK};
use crate::XWrap;
use leftwm_core::models::{DockArea, WindowState, WindowType, XyhwChange};
use std::ffi::{CStr, CString};
use std::os::raw::{c_char, c_int, c_long, c_uchar, c_uint, c_ulong};
use std::slice;
use x11_dl::xlib;
impl XWrap {
pub fn get_all_windows(&self) -> Result<Vec<xlib::Window>, String> {
let mut all = Vec::new();
for root in self.get_roots() {
match self.get_windows_for_root(root) {
Ok(some_windows) => {
for w in some_windows {
all.push(*w);
}
}
Err(err) => return Err(err),
}
}
Ok(all)
}
pub fn get_color(&self, color: String) -> c_ulong {
unsafe {
let screen = (self.xlib.XDefaultScreen)(self.display);
let cmap: xlib::Colormap = (self.xlib.XDefaultColormap)(self.display, screen);
let color_cstr = CString::new(color).unwrap_or_default().into_raw();
let mut color: xlib::XColor = std::mem::zeroed();
(self.xlib.XAllocNamedColor)(self.display, cmap, color_cstr, &mut color, &mut color);
color.pixel
}
}
pub fn get_cursor_point(&self) -> Result<(i32, i32), XlibError> {
let roots = self.get_roots();
for w in roots {
let mut root_return: xlib::Window = 0;
let mut child_return: xlib::Window = 0;
let mut root_x_return: c_int = 0;
let mut root_y_return: c_int = 0;
let mut win_x_return: c_int = 0;
let mut win_y_return: c_int = 0;
let mut mask_return: c_uint = 0;
let success = unsafe {
(self.xlib.XQueryPointer)(
self.display,
w,
&mut root_return,
&mut child_return,
&mut root_x_return,
&mut root_y_return,
&mut win_x_return,
&mut win_y_return,
&mut mask_return,
)
};
if success > 0 {
return Ok((win_x_return, win_y_return));
}
}
Err(XlibError::RootWindowNotFound)
}
pub fn get_cursor_window(&self) -> Result<WindowHandle, XlibError> {
let roots = self.get_roots();
for w in roots {
let mut root_return: xlib::Window = 0;
let mut child_return: xlib::Window = 0;
let mut root_x_return: c_int = 0;
let mut root_y_return: c_int = 0;
let mut win_x_return: c_int = 0;
let mut win_y_return: c_int = 0;
let mut mask_return: c_uint = 0;
let success = unsafe {
(self.xlib.XQueryPointer)(
self.display,
w,
&mut root_return,
&mut child_return,
&mut root_x_return,
&mut root_y_return,
&mut win_x_return,
&mut win_y_return,
&mut mask_return,
)
};
if success > 0 {
return Ok(child_return.into());
}
}
Err(XlibError::RootWindowNotFound)
}
#[must_use]
pub const fn get_default_root_handle(&self) -> WindowHandle {
WindowHandle::XlibHandle(self.root)
}
#[must_use]
pub const fn get_default_root(&self) -> xlib::Window {
self.root
}
#[must_use]
pub fn get_hint_sizing_as_xyhw(&self, window: xlib::Window) -> Option<XyhwChange> {
let hint = self.get_hint_sizing(window);
if let Some(size) = hint {
let mut xyhw = XyhwChange::default();
if (size.flags & xlib::PSize) != 0 || (size.flags & xlib::USSize) != 0 {
xyhw.w = Some(size.width);
xyhw.h = Some(size.height);
} else if (size.flags & xlib::PBaseSize) != 0 {
xyhw.w = Some(size.base_width);
xyhw.h = Some(size.base_height);
}
if (size.flags & xlib::PResizeInc) != 0 {
xyhw.w = Some(size.width_inc);
xyhw.h = Some(size.height_inc);
}
if (size.flags & xlib::PMaxSize) != 0 {
xyhw.maxw = Some(size.max_width);
xyhw.maxh = Some(size.max_height);
}
if (size.flags & xlib::PMinSize) != 0 {
xyhw.minw = Some(size.min_width);
xyhw.minh = Some(size.min_height);
}
xyhw.w = std::cmp::max(xyhw.w, xyhw.minw);
xyhw.h = std::cmp::max(xyhw.h, xyhw.minh);
xyhw.w = xyhw.w.filter(|&w| w != 0);
xyhw.h = xyhw.h.filter(|&h| h != 0);
if (size.flags & xlib::PPosition) != 0 || (size.flags & xlib::USPosition) != 0 {
xyhw.x = Some(size.x);
xyhw.y = Some(size.y);
}
return Some(xyhw);
}
None
}
pub fn get_mask_event(&self) -> xlib::XEvent {
unsafe {
let mut event: xlib::XEvent = std::mem::zeroed();
(self.xlib.XMaskEvent)(
self.display,
MOUSEMASK | xlib::SubstructureRedirectMask | xlib::ExposureMask,
&mut event,
);
event
}
}
#[must_use]
pub fn get_next_event(&self) -> xlib::XEvent {
unsafe {
let mut event: xlib::XEvent = std::mem::zeroed();
(self.xlib.XNextEvent)(self.display, &mut event);
event
}
}
#[must_use]
pub fn get_screens(&self) -> Vec<Screen> {
use x11_dl::xinerama::XineramaScreenInfo;
use x11_dl::xinerama::Xlib;
use x11_dl::xrandr::Xrandr;
let xlib = Xlib::open().expect("Couldn't not connect to Xorg Server");
if let Ok(xrandr) = Xrandr::open() {
unsafe {
let screen_resources = (xrandr.XRRGetScreenResources)(self.display, self.root);
let outputs = slice::from_raw_parts(
(*screen_resources).outputs,
(*screen_resources).noutput as usize,
);
return outputs
.iter()
.map(|output| {
(xrandr.XRRGetOutputInfo)(self.display, screen_resources, *output)
})
.filter(|&output_info| (*output_info).crtc != 0)
.map(|output_info| {
let crtc_info = (xrandr.XRRGetCrtcInfo)(
self.display,
screen_resources,
(*output_info).crtc,
);
let mut s = Screen::from(*crtc_info);
s.root = self.get_default_root_handle();
s.output = CStr::from_ptr((*output_info).name)
.to_string_lossy()
.into_owned();
s
})
.collect();
}
}
let xinerama = unsafe { (xlib.XineramaIsActive)(self.display) } > 0;
if xinerama {
let root = self.get_default_root_handle();
let mut screen_count = 0;
let info_array_raw =
unsafe { (xlib.XineramaQueryScreens)(self.display, &mut screen_count) };
let xinerama_infos: &[XineramaScreenInfo] =
unsafe { slice::from_raw_parts(info_array_raw, screen_count as usize) };
xinerama_infos
.iter()
.map(|i| {
let mut s = Screen::from(i);
s.root = root;
s
})
.collect()
} else {
let roots: Result<Vec<xlib::XWindowAttributes>, _> = self
.get_roots()
.iter()
.map(|w| self.get_window_attrs(*w))
.collect();
let roots = roots.expect("Error: No screen were detected");
roots.iter().map(Screen::from).collect()
}
}
#[must_use]
pub fn get_screens_area_dimensions(&self) -> (i32, i32) {
let mut height = 0;
let mut width = 0;
for s in self.get_screens() {
height = std::cmp::max(height, s.bbox.height + s.bbox.y);
width = std::cmp::max(width, s.bbox.width + s.bbox.x);
}
(height, width)
}
#[must_use]
pub fn get_transient_for(&self, window: xlib::Window) -> Option<xlib::Window> {
unsafe {
let mut transient: xlib::Window = std::mem::zeroed();
let status: c_int =
(self.xlib.XGetTransientForHint)(self.display, window, &mut transient);
if status > 0 {
Some(transient)
} else {
None
}
}
}
#[must_use]
pub fn get_window_actions_atoms(&self, window: xlib::Window) -> Vec<xlib::Atom> {
let mut format_return: i32 = 0;
let mut nitems_return: c_ulong = 0;
let mut bytes_remaining: c_ulong = 0;
let mut type_return: xlib::Atom = 0;
let mut prop_return: *mut c_uchar = unsafe { std::mem::zeroed() };
unsafe {
let status = (self.xlib.XGetWindowProperty)(
self.display,
window,
self.atoms.NetWMAction,
0,
MAX_PROPERTY_VALUE_LEN / 4,
xlib::False,
xlib::XA_ATOM,
&mut type_return,
&mut format_return,
&mut nitems_return,
&mut bytes_remaining,
&mut prop_return,
);
if status == i32::from(xlib::Success) && !prop_return.is_null() {
#[allow(clippy::cast_lossless, clippy::cast_ptr_alignment)]
let ptr = prop_return as *const c_ulong;
let results: &[xlib::Atom] = slice::from_raw_parts(ptr, nitems_return as usize);
return results.to_vec();
}
vec![]
}
}
pub fn get_window_attrs(
&self,
window: xlib::Window,
) -> Result<xlib::XWindowAttributes, XlibError> {
let mut attrs: xlib::XWindowAttributes = unsafe { std::mem::zeroed() };
let status = unsafe { (self.xlib.XGetWindowAttributes)(self.display, window, &mut attrs) };
if status == 0 {
return Err(XlibError::FailedStatus);
}
Ok(attrs)
}
#[must_use]
pub fn get_window_class(&self, window: xlib::Window) -> Option<(String, String)> {
unsafe {
let mut class_return: xlib::XClassHint = std::mem::zeroed();
let status = (self.xlib.XGetClassHint)(self.display, window, &mut class_return);
if status == 0 {
return None;
}
let res_name =
match CString::from_raw(class_return.res_name.cast::<c_char>()).into_string() {
Ok(s) => s,
Err(_) => return None,
};
let res_class =
match CString::from_raw(class_return.res_class.cast::<c_char>()).into_string() {
Ok(s) => s,
Err(_) => return None,
};
Some((res_name, res_class))
}
}
pub fn get_window_geometry(&self, window: xlib::Window) -> Result<XyhwChange, XlibError> {
let mut root_return: xlib::Window = 0;
let mut x_return: c_int = 0;
let mut y_return: c_int = 0;
let mut width_return: c_uint = 0;
let mut height_return: c_uint = 0;
let mut border_width_return: c_uint = 0;
let mut depth_return: c_uint = 0;
unsafe {
let status = (self.xlib.XGetGeometry)(
self.display,
window,
&mut root_return,
&mut x_return,
&mut y_return,
&mut width_return,
&mut height_return,
&mut border_width_return,
&mut depth_return,
);
if status == 0 {
return Err(XlibError::FailedStatus);
}
}
Ok(XyhwChange {
x: Some(x_return),
y: Some(y_return),
w: Some(width_return as i32),
h: Some(height_return as i32),
..XyhwChange::default()
})
}
#[must_use]
pub fn get_window_name(&self, window: xlib::Window) -> Option<String> {
if let Ok(text) = self.get_text_prop(window, self.atoms.NetWMName) {
return Some(text);
}
if let Ok(text) = self.get_text_prop(window, xlib::XA_WM_NAME) {
return Some(text);
}
None
}
#[must_use]
pub fn get_window_legacy_name(&self, window: xlib::Window) -> Option<String> {
if let Ok(text) = self.get_text_prop(window, xlib::XA_WM_NAME) {
return Some(text);
}
None
}
#[must_use]
pub fn get_window_pid(&self, window: xlib::Window) -> Option<u32> {
let (prop_return, _) = self
.get_property(window, self.atoms.NetWMPid, xlib::XA_CARDINAL)
.ok()?;
unsafe {
#[allow(clippy::cast_lossless, clippy::cast_ptr_alignment)]
let pid = *prop_return.cast::<u32>();
Some(pid)
}
}
#[must_use]
pub fn get_window_states(&self, window: xlib::Window) -> Vec<WindowState> {
self.get_window_states_atoms(window)
.iter()
.map(|a| match a {
x if x == &self.atoms.NetWMStateModal => WindowState::Modal,
x if x == &self.atoms.NetWMStateSticky => WindowState::Sticky,
x if x == &self.atoms.NetWMStateMaximizedVert => WindowState::MaximizedVert,
x if x == &self.atoms.NetWMStateMaximizedHorz => WindowState::MaximizedHorz,
x if x == &self.atoms.NetWMStateShaded => WindowState::Shaded,
x if x == &self.atoms.NetWMStateSkipTaskbar => WindowState::SkipTaskbar,
x if x == &self.atoms.NetWMStateSkipPager => WindowState::SkipPager,
x if x == &self.atoms.NetWMStateHidden => WindowState::Hidden,
x if x == &self.atoms.NetWMStateFullscreen => WindowState::Fullscreen,
x if x == &self.atoms.NetWMStateAbove => WindowState::Above,
x if x == &self.atoms.NetWMStateBelow => WindowState::Below,
_ => WindowState::Modal,
})
.collect()
}
#[must_use]
pub fn get_window_states_atoms(&self, window: xlib::Window) -> Vec<xlib::Atom> {
let mut format_return: i32 = 0;
let mut nitems_return: c_ulong = 0;
let mut bytes_remaining: c_ulong = 0;
let mut type_return: xlib::Atom = 0;
let mut prop_return: *mut c_uchar = unsafe { std::mem::zeroed() };
unsafe {
let status = (self.xlib.XGetWindowProperty)(
self.display,
window,
self.atoms.NetWMState,
0,
MAX_PROPERTY_VALUE_LEN / 4,
xlib::False,
xlib::XA_ATOM,
&mut type_return,
&mut format_return,
&mut nitems_return,
&mut bytes_remaining,
&mut prop_return,
);
if status == i32::from(xlib::Success) && !prop_return.is_null() {
#[allow(clippy::cast_lossless, clippy::cast_ptr_alignment)]
let ptr = prop_return as *const c_ulong;
let results: &[xlib::Atom] = slice::from_raw_parts(ptr, nitems_return as usize);
return results.to_vec();
}
vec![]
}
}
#[must_use]
pub fn get_window_strut_array(&self, window: xlib::Window) -> Option<DockArea> {
if let Some(d) = self.get_window_strut_array_strut_partial(window) {
tracing::debug!("STRUT:[{:?}] {:?}", window, d);
return Some(d);
}
if let Some(d) = self.get_window_strut_array_strut(window) {
tracing::debug!("STRUT:[{:?}] {:?}", window, d);
return Some(d);
}
None
}
#[must_use]
pub fn get_window_type(&self, window: xlib::Window) -> WindowType {
let mut atom = None;
if let Ok((prop_return, _)) =
self.get_property(window, self.atoms.NetWMWindowType, xlib::XA_ATOM)
{
#[allow(clippy::cast_lossless, clippy::cast_ptr_alignment)]
let atom_ = unsafe { *prop_return.cast::<xlib::Atom>() };
atom = Some(atom_);
}
match atom {
x if x == Some(self.atoms.NetWMWindowTypeDesktop) => WindowType::Desktop,
x if x == Some(self.atoms.NetWMWindowTypeDock) => WindowType::Dock,
x if x == Some(self.atoms.NetWMWindowTypeToolbar) => WindowType::Toolbar,
x if x == Some(self.atoms.NetWMWindowTypeMenu) => WindowType::Menu,
x if x == Some(self.atoms.NetWMWindowTypeUtility) => WindowType::Utility,
x if x == Some(self.atoms.NetWMWindowTypeSplash) => WindowType::Splash,
x if x == Some(self.atoms.NetWMWindowTypeDialog) => WindowType::Dialog,
_ => WindowType::Normal,
}
}
#[must_use]
pub fn get_wmhints(&self, window: xlib::Window) -> Option<xlib::XWMHints> {
unsafe {
let hints_ptr: *const xlib::XWMHints = (self.xlib.XGetWMHints)(self.display, window);
if hints_ptr.is_null() {
return None;
}
let hints: xlib::XWMHints = *hints_ptr;
Some(hints)
}
}
pub fn get_wm_state(&self, window: xlib::Window) -> Option<c_long> {
let (prop_return, nitems_return) = self
.get_property(window, self.atoms.WMState, self.atoms.WMState)
.ok()?;
if nitems_return == 0 {
return None;
}
Some(unsafe { *prop_return.cast::<c_long>() })
}
pub fn get_xatom_name(&self, atom: xlib::Atom) -> Result<String, XlibError> {
unsafe {
let cstring = (self.xlib.XGetAtomName)(self.display, atom);
if let Ok(s) = CString::from_raw(cstring).into_string() {
return Ok(s);
}
};
Err(XlibError::InvalidXAtom)
}
#[must_use]
fn get_hint_sizing(&self, window: xlib::Window) -> Option<xlib::XSizeHints> {
let mut xsize: xlib::XSizeHints = unsafe { std::mem::zeroed() };
let mut msize: c_long = xlib::PSize;
let status =
unsafe { (self.xlib.XGetWMNormalHints)(self.display, window, &mut xsize, &mut msize) };
match status {
0 => None,
_ => Some(xsize),
}
}
fn get_property(
&self,
window: xlib::Window,
property: xlib::Atom,
r#type: xlib::Atom,
) -> Result<(*const c_uchar, c_ulong), XlibError> {
let mut format_return: i32 = 0;
let mut nitems_return: c_ulong = 0;
let mut type_return: xlib::Atom = 0;
let mut bytes_after_return: xlib::Atom = 0;
let mut prop_return: *mut c_uchar = unsafe { std::mem::zeroed() };
unsafe {
let status = (self.xlib.XGetWindowProperty)(
self.display,
window,
property,
0,
MAX_PROPERTY_VALUE_LEN / 4,
xlib::False,
r#type,
&mut type_return,
&mut format_return,
&mut nitems_return,
&mut bytes_after_return,
&mut prop_return,
);
if status == i32::from(xlib::Success) && !prop_return.is_null() {
return Ok((prop_return, nitems_return));
}
};
Err(XlibError::FailedStatus)
}
#[must_use]
fn get_roots(&self) -> Vec<xlib::Window> {
self.get_xscreens()
.into_iter()
.map(|mut s| unsafe { (self.xlib.XRootWindowOfScreen)(&mut s) })
.collect()
}
fn get_text_prop(&self, window: xlib::Window, atom: xlib::Atom) -> Result<String, XlibError> {
unsafe {
let mut text_prop: xlib::XTextProperty = std::mem::zeroed();
let status: c_int =
(self.xlib.XGetTextProperty)(self.display, window, &mut text_prop, atom);
if status == 0 {
return Err(XlibError::FailedStatus);
}
if let Ok(s) = CString::from_raw(text_prop.value.cast::<c_char>()).into_string() {
return Ok(s);
}
};
Err(XlibError::FailedStatus)
}
fn get_windows_for_root<'w>(&self, root: xlib::Window) -> Result<&'w [xlib::Window], String> {
unsafe {
let mut root_return: xlib::Window = std::mem::zeroed();
let mut parent_return: xlib::Window = std::mem::zeroed();
let mut array: *mut xlib::Window = std::mem::zeroed();
let mut length: c_uint = std::mem::zeroed();
let status: xlib::Status = (self.xlib.XQueryTree)(
self.display,
root,
&mut root_return,
&mut parent_return,
&mut array,
&mut length,
);
let windows: &[xlib::Window] = slice::from_raw_parts(array, length as usize);
match status {
0 => { Err("Could not load list of windows".to_string() ) }
1 | 2 => { Ok(windows) }
_ => { Err("Unknown return status".to_string() ) }
}
}
}
fn get_window_strut_array_strut(&self, window: xlib::Window) -> Option<DockArea> {
let (prop_return, nitems_return) = self
.get_property(window, self.atoms.NetWMStrut, xlib::XA_CARDINAL)
.ok()?;
unsafe {
#[allow(clippy::cast_ptr_alignment)]
let array_ptr = prop_return.cast::<c_long>();
let slice = slice::from_raw_parts(array_ptr, nitems_return as usize);
if slice.len() == 12 {
return Some(DockArea::from(slice));
}
None
}
}
fn get_window_strut_array_strut_partial(&self, window: xlib::Window) -> Option<DockArea> {
let (prop_return, nitems_return) = self
.get_property(window, self.atoms.NetWMStrutPartial, xlib::XA_CARDINAL)
.ok()?;
unsafe {
#[allow(clippy::cast_ptr_alignment)]
let array_ptr = prop_return.cast::<c_long>();
let slice = slice::from_raw_parts(array_ptr, nitems_return as usize);
if slice.len() == 12 {
return Some(DockArea::from(slice));
}
None
}
}
#[must_use]
fn get_xscreens(&self) -> Vec<xlib::Screen> {
let mut screens = Vec::new();
let screen_count = unsafe { (self.xlib.XScreenCount)(self.display) };
for screen_id in 0..(screen_count) {
let screen = unsafe { *(self.xlib.XScreenOfDisplay)(self.display, screen_id) };
screens.push(screen);
}
screens
}
}