// See LICENSE file for copyright and license details. use x11::xlib; use x11::xlib::{ Display, XErrorEvent, Atom, XChangeProperty, }; use safe_x11::{ open_display, close_display, atom, screen_size, }; use safe_x11::window::XWindow; use std::ptr; use std::process::exit; use std::mem::uninitialized; use std::collections::HashMap; use libc::c_int; #[allow(unused_variables)] unsafe extern "C" fn error_handler(d: *mut Display, evptr: *mut XErrorEvent) -> c_int { println!("ERRORR"); let ev = ptr::read(evptr); if ev.error_code == xlib::BadAccess { println!("Another widnow manager is running :C"); exit(1); } 0 } #[derive(Hash,Eq,PartialEq)] pub enum WmAtom { Protocols, DeleteWindow, } #[derive(Hash,Eq,PartialEq)] pub enum NetAtom { NetSupported, NetFullscreen, NetWmState, NetActive, } /// State of the window manager. pub struct DotWM { /// Reference to the X display. pub display: *mut Display, /// References to all the windows. pub window_list: Vec, /// Check if the window manager needs to end. pub finish: bool, /// Vec holding atoms for ICCCM support pub wmatoms: HashMap, /// Vec holding atoms for EWHM support pub netatoms: HashMap, /// Number of the active desctop current_desktop: usize, cw_idx: usize, } /// DotWM state. impl DotWM { /// Create a state of the Dot Window Manager pub fn new() -> DotWM { let d = open_display("").unwrap(); unsafe { xlib::XSetErrorHandler(Some(error_handler)) }; // Add substructure notify mask let root = unsafe { xlib::XDefaultRootWindow(d) }; unsafe { let mut attrs: xlib::XSetWindowAttributes = uninitialized(); attrs.event_mask = xlib::SubstructureNotifyMask; xlib::XChangeWindowAttributes(d, root, xlib::CWEventMask, &mut attrs); } // Get the wmatoms let mut wmatoms = HashMap::new(); wmatoms.insert(WmAtom::Protocols, atom(d, "WM_PROTOCOLS")); wmatoms.insert(WmAtom::DeleteWindow, atom(d, "WM_DELETE_WINDOW")); // Get the netatoms let mut netatoms = HashMap::new(); netatoms.insert(NetAtom::NetSupported, atom(d, "_NET_SUPPORTED")); netatoms.insert(NetAtom::NetFullscreen, atom(d, "_NET_WM_STATE_FULLSCREEN")); netatoms.insert(NetAtom::NetWmState, atom(d, "_NET_ACTIVE_WINDOW")); netatoms.insert(NetAtom::NetActive, atom(d, "_NET_WM_STATE")); // Propagate EWHM support unsafe { let atomvec: Vec<&Atom> = netatoms.values().collect(); let len: i32 = netatoms.len() as i32; let atomptr = atomvec.as_ptr(); XChangeProperty(d, root, netatoms[&NetAtom::NetSupported], xlib::XA_ATOM, 32, xlib::PropModeReplace, atomptr as *const u8, len); } DotWM { display: d, cw_idx: 0, finish: false, wmatoms: wmatoms, netatoms: netatoms, current_desktop: 0, window_list: vec![], } } pub fn add_window(&mut self, w: xlib::Window) { self.unfocus_current_window(); if let Some(w) = XWindow::new(self.display, w) { w.select_input(xlib::EnterWindowMask); self.window_list.push(w); // Last windows get focus. self.cw_idx = self.window_list.len() - 1; self.focus_current_window(); } } pub fn current_window(&self) -> Option<&XWindow> { if self.cw_idx < self.window_list.len() { self.window_list.get(self.cw_idx) } else { None } } pub fn current_window_mut(&mut self) -> Option<&mut XWindow> { if self.cw_idx < self.window_list.len() { self.window_list.get_mut(self.cw_idx) } else { None } } pub fn remove_window(&mut self, w: xlib::Window) { let pos = self.window_list.iter().position(|xw| xw.inner == w); match pos { Some(idx) => { let new_idx = if idx == 0 { usize::max_value() } else { idx - 1 }; self.window_list.remove(idx); self.cw_idx = new_idx; self.focus_current_window(); }, None => (), } } pub fn change_focus_of(&mut self, idx: usize) { let w = &self.window_list[idx]; self.unfocus_current_window(); self.cw_idx = idx; w.set_border_width(1); w.set_border_color("red"); w.raise(); } /// Find a given window and returns it's position on the window_list. pub fn find_window(&self, w: xlib::Window) -> Option { self.window_list.iter().position(|xw| xw.inner == w) } /// Focus the next window. /// /// There're 3 posibilities. There's no window, there's one window or there /// are more windows. pub fn focus_next(&mut self) { if self.window_list.len() > 0 { // Unfocus the current window. let prev_win = &self.window_list[self.cw_idx]; prev_win.unfocus(); // And give focus to the next one. let next_win_idx = if self.cw_idx < self.window_list.len() - 1 { self.cw_idx + 1 } else { 0 }; let curr_win = &self.window_list[next_win_idx]; curr_win.focus(); self.cw_idx = next_win_idx; } } fn focus_current_window(&self) { self.current_window().map(|x| x.focus()); } fn unfocus_current_window(&self) { self.current_window().map(|x| x.unfocus()); } /// Get a list of the geometries for every window in the screen (including /// the screen borders. The form is (x, y). /// /// # Example /// /// Having a screen of 800x600, and a window on 10(x), 10(y) with dimensions /// 400(width) and 300(height): /// /// ``` /// for (x, y) in dotwm.geometries() { /// println!("x: {} - y: {}", x, y); /// } /// ``` /// /// The output will be: /// /// ``` /// (0,0) /// (800, 600) /// (10, 10) /// (410, 310) /// ``` pub fn geometries(&self) -> Vec<(i32,i32)> { let (screen_width, screen_height) = screen_size(self.display); let mut result: Vec<(i32,i32)> = vec![ (0, 0), (screen_width as i32, screen_height as i32), ]; for w in self.window_list.iter() { let attrs = w.attributes(); result.push((attrs.x, attrs.y)); result.push((attrs.x + attrs.width, attrs.y + attrs.height)); } result } pub fn x_geometries(&self) -> Vec { self.geometries().iter().map(|x| x.0).collect() } pub fn y_geometries(&self) -> Vec { self.geometries().iter().map(|x| x.1).collect() } } impl Drop for DotWM { fn drop(&mut self) { println!("Closing display"); close_display(self.display); } }