// 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, }; use std::ptr; use std::mem::uninitialized; use std::mem::transmute; use std::cmp::{max}; use safe_x11::{screen_ratio,get_color}; /// Representation of [`xlib::Window`](../../x11/xlib/type.Window.html) pub struct XWindow { display: *mut xlib::Display, /// xlib::Window that wraps this struct. pub inner: Window, fullscreen: bool, } fn fixed_with_ratio(x: i32, ratio: f32) -> i32 { let fx = x as f32; let fixed = fx * ratio; fixed as i32 } 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 { Some(XWindow { display: d, inner: w, fullscreen: false, }) } 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) { 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 { unsafe { let mut attrptr: XWindowAttributes = uninitialized(); XGetWindowAttributes(self.display, self.inner, &mut attrptr); attrptr } } /// 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(()) } } /// 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); } } /// 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); } } /// 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); } } /// 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 toggle_fullscreen(&mut self, wm_state_atom: Atom, fs_atom: Atom, fullscreen: bool) { 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 fullscreen != self.fullscreen { self.fullscreen = fullscreen; let fs_atom_slice: &[Atom; 1] = &[fs_atom]; let fs_atom_ptr: *const u8 = unsafe { transmute(fs_atom_slice) }; println!("Changing property!"); unsafe { XChangeProperty(self.display, self.inner, wm_state_atom, XA_ATOM, 32, PropModeReplace, fs_atom_ptr, fullscreen as i32); } } if fullscreen { println!("resizing!"); self.move_resize(0, 0, dw, dh); self.set_border_width(0); } else { self.set_border_width(1); } } }