extern crate x11_dl;
use std::ffi::CString;
use std::mem;
use std::os::raw::*;
use std::ptr;
use self::x11_dl::xlib;
use self::x11_dl::xlib::XRectangle;
use self::x11_dl::xlib::Xlib;
const TEXT_MARGIN: i32 = 10;
pub(crate) fn create_message_box_impl(message: &str, title: &str) {
let mut window_width = 600;
let mut window_height = 400;
let xlib = match Xlib::open() {
Ok(xlib) => xlib,
Err(err) => {
eprintln!("Failed to open xlib: {}", err);
return;
}
};
let display = unsafe { (xlib.XOpenDisplay)(ptr::null()) };
if display.is_null() {
eprintln!("XOpenDisplay failed");
return;
}
let window = unsafe {
let screen = (xlib.XDefaultScreen)(display);
let root = (xlib.XRootWindow)(display, screen);
let mut attributes: xlib::XSetWindowAttributes = mem::zeroed();
attributes.background_pixel = (xlib.XWhitePixel)(display, screen);
(xlib.XCreateWindow)(
display,
root,
0,
0,
window_width,
window_height,
0,
0,
xlib::InputOutput as c_uint,
ptr::null_mut(),
xlib::CWBackPixel,
&mut attributes,
)
};
let title_str = CString::new(title).unwrap();
unsafe { (xlib.XStoreName)(display, window, title_str.as_ptr() as *const c_char) };
let wm_protocols_str = CString::new("WM_PROTOCOLS").unwrap();
let wm_delete_window_str = CString::new("WM_DELETE_WINDOW").unwrap();
let wm_protocols = unsafe { (xlib.XInternAtom)(display, wm_protocols_str.as_ptr(), xlib::False) };
let wm_delete_window = unsafe { (xlib.XInternAtom)(display, wm_delete_window_str.as_ptr(), xlib::False) };
let mut protocols = [wm_delete_window];
unsafe {
(xlib.XSetWMProtocols)(
display,
window,
protocols.as_mut_ptr(),
protocols.len() as c_int,
)
};
let wm_window_type_str = CString::new("_NET_WM_WINDOW_TYPE").unwrap();
let wm_window_type_dialog_str = CString::new("_NET_WM_WINDOW_TYPE_DIALOG").unwrap();
let wm_window_type = unsafe { (xlib.XInternAtom)(display, wm_window_type_str.as_ptr(), xlib::False) };
let wm_window_type_dialog = unsafe { (xlib.XInternAtom)(display, wm_window_type_dialog_str.as_ptr(), xlib::False) };
let wm_window_type_dialog = &wm_window_type_dialog as *const u64 as *const u8;
unsafe {
(xlib.XChangeProperty)(
display,
window,
wm_window_type,
xlib::XA_ATOM,
32,
xlib::PropModeReplace,
wm_window_type_dialog,
1,
)
};
unsafe { (xlib.XSelectInput)(display, window, xlib::ExposureMask | xlib::StructureNotifyMask) };
let gc = unsafe {
let mut values: xlib::XGCValues = mem::zeroed();
(xlib.XCreateGC)(display, window, 0, &mut values)
};
let font_list = CString::new("-*-*-medium-r-normal--*-120-*-*-*-*-*-*").unwrap();
let mut missing = ptr::null_mut();
let mut num_missing = 0;
let mut foo = ptr::null_mut();
let font_set = unsafe {
(xlib.XCreateFontSet)(
display,
font_list.as_ptr() as *const c_char,
&mut missing,
&mut num_missing,
&mut foo,
)
};
unsafe { (xlib.XMapWindow)(display, window) };
let max_line_height = if let Ok(message) = CString::new(message.clone()) {
line_width_height(&xlib, font_set, &message).1 as i32 + 2
} else {
15
};
let mut message_lines = split_message(&xlib, font_set, &message, window_width as i32);
window_height = (message_lines.len() as u32 + 1) * max_line_height as u32;
unsafe { (xlib.XResizeWindow)(display, window, window_width, window_height) };
loop {
let event = unsafe {
let mut event: xlib::XEvent = mem::zeroed();
(xlib.XNextEvent)(display, &mut event);
event
};
match event.get_type() {
xlib::ClientMessage => {
let xclient = xlib::XClientMessageEvent::from(event);
if xclient.message_type == wm_protocols && xclient.format == 32 {
let protocol = xclient.data.get_long(0) as xlib::Atom;
if protocol == wm_delete_window {
break;
}
}
}
xlib::ConfigureNotify => {
let configure_event: &xlib::XConfigureEvent = event.as_ref();
window_width = configure_event.width as u32;
#[allow(unused_assignments)]
{
window_height = configure_event.height as u32;
}
}
xlib::Expose => {
message_lines = split_message(&xlib, font_set, &message, window_width as i32);
for (i, line) in message_lines.iter().enumerate() {
unsafe {
(xlib.Xutf8DrawString)(
display,
window,
font_set,
gc,
TEXT_MARGIN,
(i as i32 + 1) * max_line_height,
line.as_ptr() as *const c_char,
line.to_bytes().len() as i32,
)
};
}
}
_ => (),
}
}
unsafe { (xlib.XCloseDisplay)(display) };
}
fn line_width_height(
xlib: &Xlib,
font_set: *mut xlib::_XOC,
text: &CString,
) -> (u16, u16) {
let mut overall_ink = XRectangle {
x: 0,
y: 0,
width: 0,
height: 0,
};
let mut overall_logical = overall_ink.clone();
unsafe {
(xlib.Xutf8TextExtents)(
font_set,
text.as_ptr() as *const c_char,
text.to_bytes().len() as i32,
&mut overall_ink as *mut XRectangle,
&mut overall_logical as *mut XRectangle,
)
};
(overall_logical.width, overall_logical.height)
}
fn split_message(xlib: &Xlib, font_set: *mut xlib::_XOC, message: &str, window_width: i32) -> Vec<CString> {
let mut processed_lines = vec![];
for line in message.lines() {
if line.is_empty() {
processed_lines.push(CString::new("").unwrap());
}
else {
let mut current_line = String::new();
let mut new_line = String::new();
for word in line.split_whitespace() {
new_line.push_str(word);
new_line.push(' ');
if let Ok(line_cstring) = CString::new(new_line.clone()) {
let (line_width, _) = line_width_height(&xlib, font_set, &line_cstring);
if TEXT_MARGIN + line_width as i32 >= window_width {
if let Ok(line_cstring) = CString::new(current_line.clone()) {
processed_lines.push(line_cstring);
}
current_line.clear();
new_line.clear();
current_line.push_str(word);
current_line.push(' ');
new_line.push_str(word);
new_line.push(' ');
} else {
current_line = new_line.clone();
}
} else {
current_line.clear();
new_line.clear();
}
}
if !current_line.is_empty() {
if let Ok(line_cstring) = CString::new(current_line) {
processed_lines.push(line_cstring);
}
}
}
}
processed_lines
}