extern crate x11;
use std::collections::VecDeque;
use std::ffi::*;
use std::mem::MaybeUninit;
use std::ptr;
use std::slice;
use x11::xinerama;
use x11::xlib;
use x11::xrandr;
use crate::common::error::*;
use crate::common::*;
use crate::platforms::x11::misc::{
atoms::*,
atoms::X11Atom::*,
window::*,
};
pub mod atoms;
pub mod window;
pub const XLIB_NONE: u64 = 0;
pub const BUTTONMASK: i64 = xlib::ButtonPressMask | xlib::ButtonReleaseMask;
pub const MOUSEMASK: i64 = BUTTONMASK | xlib::PointerMotionMask;
pub const WINDOW_MIN_SIZE: u32 = 40;
pub const CURSOR_NORMAL: u32 = 68;
pub const CURSOR_RESIZE: u32 = 120;
pub const CURSOR_MOVE: u32 = 52;
pub const WITHDRAWN_STATE: i32 = 0;
pub const NORMAL_STATE: i32 = 1;
pub const ICONIC_STATE: i32 = 3;
pub const MWM_HINTS_FLAGS_FIELD: usize = 0;
pub const MWM_HINTS_DECORATIONS_FIELD: usize = 2;
pub const MWM_HINTS_DECORATIONS: u64 = 1 << 1;
pub const MWM_DECOR_ALL: u64 = 1 << 0;
pub const MWM_DECOR_BORDER: u64 = 1 << 1;
pub const MWM_DECOR_TITLE: u64 = 1 << 3;
impl From<xinerama::XineramaScreenInfo> for MonitorConfig {
fn from(info: xinerama::XineramaScreenInfo) -> MonitorConfig {
let area = Dimensions::new(info.x_org.into(), info.y_org.into(),
info.width.try_into().unwrap(), info.height.try_into().unwrap());
MonitorConfig::new( format!("output{}", info.screen_number), area, area)
}
}
impl From<(*mut xlib::Display, &xrandr::XRRMonitorInfo)> for MonitorConfig {
fn from((display, monitor_info): (*mut xlib::Display, &xrandr::XRRMonitorInfo)) -> Self {
let name = match atom_name(display, monitor_info.name) {
Some(name) => name,
None => format!("monitor{}", monitor_info.noutput),
};
let area = Dimensions::new(monitor_info.x, monitor_info.y,
monitor_info.width as u32, monitor_info.height as u32);
MonitorConfig::new(name, area, area)
}
}
impl From<*mut xlib::Screen> for MonitorConfig {
fn from(screen: *mut xlib::Screen) -> Self {
let w = unsafe { xlib::XWidthOfScreen(screen).try_into().unwrap() };
let h = unsafe { xlib::XHeightOfScreen(screen).try_into().unwrap() };
let dims = Dimensions::new(0, 0, w, h);
MonitorConfig::new("output".to_owned(), dims, dims)
}
}
pub fn alternative_modifiers(modifiers: u32) -> Vec<u32> {
vec![
modifiers,
modifiers | xlib::Mod2Mask,
]
}
pub fn await_map_notify(display: *mut xlib::Display, window: xlib::Window) {
loop {
let mut event: MaybeUninit<xlib::XEvent> = MaybeUninit::uninit();
unsafe {
xlib::XNextEvent(display, event.as_mut_ptr());
let event = event.assume_init();
if event.get_type() == xlib::MapNotify
&& event.map.window == window {
break;
}
}
}
}
pub fn close_display(display: *mut xlib::Display) {
if !display.is_null() {
unsafe {
xlib::XCloseDisplay(display);
}
}
}
pub fn create_window(display: *mut xlib::Display, dimensions: Dimensions, class: &str, name: &str,
win_type: Option<X11Atom>) -> Result<xlib::Window> {
let (x, y, width, height) = dimensions.as_tuple();
unsafe {
let screen = xlib::XDefaultScreen(display);
let border_width = 0;
let win = xlib::XCreateSimpleWindow(display, xlib::XDefaultRootWindow(display),
x, y, width, height, border_width,
xlib::XBlackPixel(display, screen),
xlib::XWhitePixel(display, screen));
xlib::XSelectInput(display, win, xlib::StructureNotifyMask | xlib::ExposureMask);
let class_cstring = match CString::new(class) {
Ok(cstring) => cstring,
Err(_) => return Err(MarsError::failed_conversion(class, stringify!(&str), stringify!(CString))),
};
let mut class_bytes = class_cstring.into_bytes_with_nul();
let mut class_hint = xlib::XClassHint {
res_name: class_bytes.as_mut_ptr() as *mut c_char,
res_class: class_bytes.as_mut_ptr() as *mut c_char,
};
xlib::XSetClassHint(display, win, &mut class_hint);
let name_cstring = match CString::new(name) {
Ok(cstring) => cstring,
Err(_) => return Err(MarsError::failed_conversion(name, stringify!(&str), stringify!(CString))),
};
let mut name_property: MaybeUninit<xlib::XTextProperty> = MaybeUninit::uninit();
let mut data = [name_cstring.as_ptr() as *mut c_char];
if xlib::XStringListToTextProperty(data.as_mut_ptr(),
1, name_property.as_mut_ptr()) == 0 {
return Err(MarsError::failed_conversion(name, stringify!(&str), stringify!(xlib::XTextProperty)));
};
xlib::XSetWMName(display, win, name_property.assume_init_mut());
if let Some(win_type) = win_type {
let data = [win_type.to_xlib_atom(display)];
win.x11_replace_property_long(display, NetWMWindowType, xlib::XA_ATOM, &data);
}
xlib::XMapWindow(display, win);
xlib::XFlush(display);
Ok(win)
}
}
pub fn open_display() -> Result<*mut xlib::Display> {
unsafe {
let display = xlib::XOpenDisplay(ptr::null());
if display.is_null() {
Err(MarsError::x11_open_display())
} else {
Ok(display)
}
}
}
pub fn get_keysym(name: &str) -> xlib::KeySym {
unsafe {
let cstring = CString::new(name).unwrap();
xlib::XStringToKeysym(cstring.as_ptr())
}
}
pub extern "C" fn on_error_dummy(_display: *mut xlib::Display, _error: *mut xlib::XErrorEvent) -> c_int {
0
}
pub fn query_monitor_config(display: *mut xlib::Display, ignore_overlapping: bool) -> Vec<MonitorConfig> {
unsafe {
let mut monitors = VecDeque::new();
let mut nmonitors = 0;
let root = xlib::XDefaultRootWindow(display);
let monitors_ptr = xrandr::XRRGetMonitors(display, root, xlib::True, &mut nmonitors);
if !monitors_ptr.is_null() && nmonitors > 0 {
let xrr_monitors = slice::from_raw_parts(monitors_ptr, nmonitors as usize);
for xrr_monitor in xrr_monitors {
if xrr_monitor.primary != 0 {
monitors.push_front(MonitorConfig::from((display, xrr_monitor)));
} else {
monitors.push_back(MonitorConfig::from((display, xrr_monitor)));
}
}
}
xlib::XFree(monitors_ptr as *mut c_void);
if monitors.is_empty() && xinerama::XineramaIsActive(display) != 0 {
let mut screen_count = 0;
let screens_raw = xinerama::XineramaQueryScreens(display, &mut screen_count);
let screens_slice = slice::from_raw_parts_mut(screens_raw, screen_count.try_into().unwrap());
monitors.extend(screens_slice.iter().map(|x| MonitorConfig::from(*x)));
xlib::XFree(screens_slice.as_mut_ptr() as *mut c_void);
}
if monitors.is_empty() {
return vec!(MonitorConfig::from(xlib::XDefaultScreenOfDisplay(display)));
}
if ignore_overlapping {
let no_overlap = |m1: &MonitorConfig, m2: &MonitorConfig| {
m1.dimensions().right() <= m2.dimensions().x() || m1.dimensions().x() >= m2.dimensions().right() || m1.dimensions().bottom() <= m2.dimensions().y() || m1.dimensions().y() >= m2.dimensions().bottom() };
let mut non_overlapping = Vec::new();
for mon in monitors.drain(..) {
if non_overlapping.iter().all(|m| no_overlap(m, &mon)) {
non_overlapping.push(mon);
}
}
non_overlapping
} else {
monitors.into()
}
}
}
pub fn sanitize_modifiers(modifiers: u32) -> u32 {
modifiers & (xlib::ShiftMask | xlib::ControlMask | xlib::Mod1Mask | xlib::Mod3Mask | xlib::Mod4Mask)
}
pub fn send_client_message(display: *mut xlib::Display, atom: X11Atom, window: xlib::Window, data: xlib::ClientMessageData) {
let mut event = xlib::XEvent {
client_message: xlib::XClientMessageEvent {
type_: xlib::ClientMessage,
serial: 0,
send_event: xlib::True,
display,
window,
message_type: atom.to_xlib_atom(display),
format: 32,
data,
}
};
unsafe {
let root = xlib::XDefaultRootWindow(display);
let mask = xlib::SubstructureRedirectMask | xlib::SubstructureNotifyMask;
let propagate = xlib::False;
xlib::XSendEvent(display, root, propagate, mask, &mut event);
xlib::XFlush(display);
}
}