// See LICENSE file for copyright and license details. use x11::xlib; use x11::xlib::{ Window, XWindowAttributes, XGetWindowAttributes, XDefaultScreenOfDisplay, XMoveWindow, XMoveResizeWindow, XDisplayHeight, XDisplayWidth, XDefaultScreen, XA_ATOM, Atom, PropModeReplace, XSetInputFocus, XSetWindowBorder, IsViewable, RevertToParent, CurrentTime, XEvent, XSendEvent, XClientMessageEvent, ClientMessage,ClientMessageData, NoEventMask, XChangeProperty, XChangeWindowAttributes, XSetWindowAttributes, XMapWindow, XUnmapWindow, XBlackPixel,XWhitePixel, XDrawString, }; use std::ptr; use std::mem::uninitialized; use std::mem::transmute; use std::cmp::{max}; use std::ffi::CString; use safe_x11::{screen_ratio,get_color}; /// Representation of [`xlib::Window`](../../x11/xlib/type.Window.html) pub struct XWindow { display: *mut xlib::Display, // The Application window. pub inner: Window, fullscreen: bool, prev_attribs: XWindowAttributes, } fn fixed_with_ratio(x: i32, ratio: f32) -> i32 { let fx = x as f32; let fixed = fx * ratio; fixed as i32 } fn window_attribs(display: *mut xlib::Display, w: Window) -> XWindowAttributes { unsafe { let mut attrptr: XWindowAttributes = uninitialized(); XGetWindowAttributes(display, w, &mut attrptr); attrptr } } impl XWindow { /// Generate a reference to the window in certain display. This will return /// `None` if the window is the root window. pub fn new(d: *mut xlib::Display, w: Window) -> Option { if w != 0 { let attrs: XWindowAttributes = unsafe { uninitialized() }; println!("Window created!"); Some(XWindow { display: d, inner: w, fullscreen: false, prev_attribs: attrs, }) } else { None } } /// Set the border width To the window. pub fn set_border_width(&self, size: u32) { unsafe { xlib::XSetWindowBorderWidth(self.display, self.inner, size) }; } /// clients receiving a WM_DELETE_WINDOW message should behave as if /// the user selected "delete window" from a hypothetical menu and /// also perform any confirmation dialog with the user. pub fn delete(&self) { // First delete the border window and then the main window. let mut ev: XClientMessageEvent = unsafe { uninitialized() }; ev.type_ = ClientMessage; ev.window = self.inner; ev.format = 32; ev.message_type = 0; // WM_PROTOCOLS ev.data = ClientMessageData::new(); ev.data.set_long(0, 0); // WM_DELETE_WINDOW ev.data.set_long(1, CurrentTime as i64); let mut event = XEvent::from(ev); unsafe { XSendEvent(self.display, self.inner, 0, NoEventMask, &mut event); } } /// Sets the border color to the selected window pub fn set_border_color>>(&self, color: T) { let c = get_color(self.display, color); unsafe { XSetWindowBorder(self.display, self.inner, c); } } /// Raises a window. pub fn raise(&self) { unsafe { xlib::XRaiseWindow(self.display, self.inner); } } /// Register input events on some [`xlib::Window`](../../x11/xlib/type.Window.html) /// /// # Example /// /// if we want to handle when a pointer enter into a window rectangle, we /// should do something like: /// /// ``` /// use x11::xlib; /// /// let display = open_display("").unwrap(); /// // Raw window /// let window = 0xc00022; /// /// window.select_input(xlib::EnterWindowMask); /// ``` pub fn select_input(&self, mask: i64) { unsafe { xlib::XSelectInput(self.display, self.inner, mask); } } /// Returns the window attributes from certain window. pub fn attributes(&self) -> XWindowAttributes { window_attribs(self.display, self.inner) } /// Moves the window given an offset from where it is. pub fn move_offset(&self, xoffset: i32, yoffset: i32) { unsafe { let mut attributes: XWindowAttributes = uninitialized(); XGetWindowAttributes(self.display, self.inner, &mut attributes); XMoveWindow(self.display, self.inner, attributes.x + xoffset, attributes.y + yoffset); } } /// Moves the window to a particular coord in the screen. pub fn move_to(&self, x: i32, y: i32) -> Result<(), &str> { let screen = unsafe { let s = XDefaultScreenOfDisplay(self.display); ptr::read(s) }; if 0 > x && x <= screen.width || 0 > y && y <= screen.height { Err("Cannot move the window outside the screen!") } else { unsafe { XMoveWindow(self.display, self.inner, x, y); }; Ok(()) } } // Some functions for moving within some boundaries. /// Move sticky to the left. pub fn move_sticky_left(&self, boundaries: &[i32]) { let attrs = self.attributes(); if let Some(v) = boundaries.iter() .skip_while(|&a| *a >= attrs.x) .next() { let _ = self.move_to(*v, attrs.y); } } /// Move sticky to the right. pub fn move_sticky_right(&self, boundaries: &[i32]) { let attrs = self.attributes(); let win_bound = attrs.x + attrs.width; if let Some(v) = boundaries.iter() .skip_while(|&a| *a <= win_bound) .next() { let _ = self.move_to(*v - attrs.width, attrs.y); } } /// Move up sticky. pub fn move_sticky_up(&self, boundaries: &[i32]) { let attrs = self.attributes(); if let Some(v) = boundaries.iter() .skip_while(|&a| *a >= attrs.y) .next() { let _ = self.move_to(attrs.x, *v); } } /// Move down sticky. pub fn move_sticky_down(&self, boundaries: &[i32]) { let attrs = self.attributes(); let win_bound = attrs.y + attrs.height; if let Some(v) = boundaries.iter() .skip_while(|&a| *a <= win_bound) .next() { let _ = self.move_to(attrs.x, *v - attrs.height); } } /// Move and resize the window in one step pub fn move_resize(&self, x:i32, y: i32, w: u32, h: u32) { unsafe { XMoveResizeWindow(self.display, self.inner, x, y, w, h as u32); } } /// Resizes the window a fixed amount within the height and width. pub fn resize(&self, w: i32, h: i32) { let ratio = screen_ratio(self.display); let attrs = self.attributes(); unsafe { let ww: u32 = max(1, (attrs.width + fixed_with_ratio(w, ratio)) as u32); let wh: u32 = max(1, (attrs.height + fixed_with_ratio(h, ratio)) as u32); xlib::XResizeWindow(self.display, self.inner, ww, wh as u32); } } /// Resizes the window an absolute amount within the height and width. pub fn resize_abs(&self, w: i32, h: i32) { let attrs = self.attributes(); unsafe { let ww: u32 = max(1, (attrs.width + w) as u32); let wh: u32 = max(1, (attrs.height + h) as u32); xlib::XResizeWindow(self.display, self.inner, ww, wh); } } pub fn resize_to(&self, w: i32, h: i32) { unsafe { let ww: u32 = max(1, w as u32); let wh: u32 = max(1, h as u32); xlib::XResizeWindow(self.display, self.inner, ww, wh); } } pub fn resize_sticky_left(&self, boundaries: &[i32]) { let attrs = self.attributes(); if let Some(v) = boundaries.iter() .skip_while(|&a| *a >= attrs.x + attrs.width) .next() { let diff = attrs.width - (*v - attrs.x); if *v - attrs.x > 0 { let _ = self.resize_to(attrs.width - diff, attrs.height); } } } pub fn resize_sticky_right(&self, boundaries: &[i32]) { let attrs = self.attributes(); let win_bound = attrs.x + attrs.width; if let Some(v) = boundaries.iter() .skip_while(|&a| *a <= win_bound) .next() { let new_width = *v - attrs.x; let _ = self.resize_to(new_width, attrs.height); } } pub fn resize_sticky_up(&self, boundaries: &[i32]) { let attrs = self.attributes(); if let Some(v) = boundaries.iter() .skip_while(|&a| *a >= attrs.y + attrs.height) .next() { let diff = attrs.height - (*v - attrs.y); if *v - attrs.y > 0 { let _ = self.resize_to(attrs.width, attrs.height - diff); } } } pub fn resize_sticky_down(&self, boundaries: &[i32]) { let attrs = self.attributes(); let win_bound = attrs.y + attrs.height; if let Some(v) = boundaries.iter() .skip_while(|&a| *a <= win_bound) .next() { let new_height = *v - attrs.y; let _ = self.resize_to(attrs.width, new_height); } } /// Raise the focus of the window and set a border. pub fn focus(&self) { let attrs = self.attributes(); if attrs.map_state == IsViewable { unsafe { XSetInputFocus(self.display, self.inner, RevertToParent, CurrentTime) }; } self.raise(); self.set_border_width(1); self.set_border_color("red"); } /// Give a black border. pub fn unfocus(&self) { self.set_border_width(1); self.set_border_color("black"); } /// Fullscreen toggle pub fn fullscreen(&mut self, wm_state_atom: Atom, fs_atom: Atom) { let screen = unsafe { XDefaultScreen(self.display) }; let dh: u32 = unsafe { XDisplayHeight(self.display, screen) as u32 }; let dw: u32 = unsafe { XDisplayWidth(self.display, screen) as u32 }; if !self.fullscreen { // First, store the XWindowAttributes to query later. self.prev_attribs = self.attributes(); self.fullscreen = true; self.move_resize(0, 0, dw, dh); self.set_border_width(0); } else { self.fullscreen = false; self.move_resize(self.prev_attribs.x, self.prev_attribs.y, self.prev_attribs.width as u32, self.prev_attribs.height as u32); self.set_border_width(1); } // EWMH for fullscreen (Don't know how to test it.) let fs_atom_slice: &[Atom; 1] = &[fs_atom]; let fs_atom_ptr: *const u8 = unsafe { transmute(fs_atom_slice) }; unsafe { XChangeProperty(self.display, self.inner, wm_state_atom, XA_ATOM, 32, PropModeReplace, fs_atom_ptr, self.fullscreen as i32); } } pub fn draw_string>>(&self, s: T) { // Title :D println!("Drawing string for {}", self.inner); let cstr = CString::new(s).unwrap(); unsafe { let gc = xlib::XCreateGC(self.display, self.inner, 0, ptr::null_mut()); xlib::XSetForeground(self.display, gc, XWhitePixel(self.display, 0)); xlib::XSetBackground(self.display, gc, XBlackPixel(self.display, 0)); println!("Loading fixed"); let font: xlib::XFontStruct = ptr::read(xlib::XLoadQueryFont(self.display, CString::new("fixed").unwrap().as_ptr())); xlib::XSetFont(self.display, gc, font.fid); xlib::XClearWindow(self.display, self.inner); XDrawString(self.display, self.inner, gc, 3, 10, cstr.as_ptr(), cstr.to_bytes().len() as i32); } } pub fn map(&self) { unsafe { XMapWindow(self.display, self.inner); } } pub fn unmap(&self) { unsafe { XUnmapWindow(self.display, self.inner); } } } pub fn change_window_attributes(display: *mut xlib::Display, win: Window, mask: u64, window_attribs: *mut XSetWindowAttributes) { unsafe { XChangeWindowAttributes(display, win, mask, window_attribs); } }