From d5e55aab6499a89e9dfaf5b976938bfe3e2f6aee Mon Sep 17 00:00:00 2001 From: Matias Linares Date: Sun, 29 Nov 2015 11:57:19 -0300 Subject: Begin ef EWMH support and sticky movement The implementationn of the EWMH and ICCCM support is incomplete. I need to check how to implement on the right way. --- src/command.rs | 70 +++++++++++++++++++++++++++++++ src/dotwm.rs | 112 +++++++++++++++++++++++++++++++++++++++++++++++-- src/safe_x11/mod.rs | 17 ++++++++ src/safe_x11/window.rs | 68 +++++++++++++++++++++++++++++- src/socket/parser.rs | 3 ++ 5 files changed, 266 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/command.rs b/src/command.rs index 0ccee17..78b9e5a 100644 --- a/src/command.rs +++ b/src/command.rs @@ -16,6 +16,7 @@ use x11::xlib; use x11::xlib::{XEvent, GrabModeAsync}; use dotwm::DotWM; +use dotwm::NetAtom; use safe_x11::grab_key; const WNOHANG: c_int = 0x00000001; @@ -66,6 +67,17 @@ pub fn exec(_: &mut DotWM, _: xlib::XEvent, args: &[String]) -> bool { true } +/// Toggles the fullscreen of the current window +pub fn fullscreen(wm: &mut DotWM, _: xlib::XEvent, _: &[String]) -> bool { + let wmstate = wm.netatoms[&NetAtom::NetWmState]; + let fs_atom = wm.netatoms[&NetAtom::NetFullscreen]; + + if let Some(win) = wm.current_window_mut() { + win.toggle_fullscreen(wmstate, fs_atom, true); + }; + true +} + /// Move the window to a relative position pub fn move_win(wm: &mut DotWM, _: xlib::XEvent, args: &[String]) -> bool { let x = args[0].parse::().unwrap(); @@ -93,6 +105,55 @@ pub fn move_win_to(wm: &mut DotWM, _: xlib::XEvent, args: &[String]) -> bool { } } +pub fn move_win_sticky(wm: &mut DotWM, _: xlib::XEvent, args: &[String]) -> bool { + if let Some(ref win) = wm.current_window() { + let attrs = win.attributes(); + + match &*args[0] { + "left" => { + let mut xgeo = wm.x_geometries(); + xgeo.sort_by(|a,b| b.cmp(a)); + + let val = xgeo.iter().skip_while(|&a| *a >= attrs.x).next(); + if let Some(v) = val { + let _ = win.move_to(*v, attrs.y); + } + }, + "right" => { + let mut xgeo = wm.x_geometries(); + let win_right = attrs.x + attrs.width; + xgeo.sort_by(|a,b| a.cmp(b)); + + let val = xgeo.iter().skip_while(|&a| *a <= win_right).next(); + if let Some(v) = val { + let _ = win.move_to(*v - attrs.width, attrs.y); + } + }, + "up" => { + let mut ygeo = wm.y_geometries(); + ygeo.sort_by(|a,b| b.cmp(a)); + + let val = ygeo.iter().skip_while(|&a| *a >= attrs.y).next(); + if let Some(v) = val { + let _ = win.move_to(attrs.x, *v); + } + }, + "down" => { + let mut ygeo = wm.y_geometries(); + let win_bot = attrs.y + attrs.height; + ygeo.sort_by(|a,b| a.cmp(b)); + + let val = ygeo.iter().skip_while(|&a| *a <= win_bot).next(); + if let Some(v) = val { + let _ = win.move_to(attrs.x, *v - attrs.height); + } + }, + _ => (), + } + } + true +} + /// Resize the window certain amount in x and y. pub fn resize_win(wm: &mut DotWM, _: xlib::XEvent, args: &[String]) -> bool { let w = args[0].parse::().unwrap(); @@ -105,6 +166,15 @@ pub fn resize_win(wm: &mut DotWM, _: xlib::XEvent, args: &[String]) -> bool { true } +/// Close the current window +pub fn close_win(wm: &mut DotWM, _: xlib::XEvent, _: &[String]) -> bool { + if let Some(ref win) = wm.current_window() { + win.delete(); + }; + + true +} + /// Focus the next window on the list pub fn focus_next(wm: &mut DotWM, _: xlib::XEvent, _: &[String]) -> bool { wm.focus_next(); diff --git a/src/dotwm.rs b/src/dotwm.rs index cd909b5..b3ed782 100644 --- a/src/dotwm.rs +++ b/src/dotwm.rs @@ -4,11 +4,15 @@ 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; @@ -16,6 +20,7 @@ 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)] @@ -30,6 +35,21 @@ unsafe extern "C" fn error_handler(d: *mut Display, evptr: *mut XErrorEvent) -> 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, @@ -37,7 +57,12 @@ pub struct DotWM { pub window_list: Vec, /// Check if the window manager needs to end. pub finish: bool, - // Map with the keys as (key, mod) + /// 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, } @@ -47,18 +72,45 @@ impl DotWM { pub fn new() -> DotWM { let d = open_display("").unwrap(); unsafe { xlib::XSetErrorHandler(Some(error_handler)) }; - // Some testings. + + // Add substructure notify mask + let root = unsafe { xlib::XDefaultRootWindow(d) }; + unsafe { - let root = xlib::XDefaultRootWindow(d); 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![], } } @@ -82,6 +134,14 @@ impl DotWM { } } + 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); @@ -140,6 +200,52 @@ impl DotWM { 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 { diff --git a/src/safe_x11/mod.rs b/src/safe_x11/mod.rs index 0283966..9cd14fd 100644 --- a/src/safe_x11/mod.rs +++ b/src/safe_x11/mod.rs @@ -6,6 +6,8 @@ use x11::xlib; use x11::xlib::{ Display, + XDisplayWidth, XDisplayHeight, + XDefaultScreen, XConnectionNumber, Screen, Cursor, @@ -17,6 +19,8 @@ use x11::xlib::{ // Windows Window, + + Atom, XInternAtom, }; use x11::xlib::XKeyEvent; @@ -110,6 +114,12 @@ pub fn next_xevent(display: *mut Display) -> XEvent { } } +pub fn screen_size(display: *mut Display) -> (u32, u32) { + let screen = unsafe { XDefaultScreen(display) }; + let dw: u32 = unsafe { XDisplayWidth(display, screen) as u32 }; + let dh: u32 = unsafe { XDisplayHeight(display, screen) as u32 }; + (dw, dh) +} /// Grab pointer buttons. /// @@ -210,3 +220,10 @@ fn get_color>>(display: *mut Display, color: T) -> u64{ pub fn lookup_keysym(ev: &mut XKeyEvent) -> xlib::KeySym { unsafe { xlib::XLookupKeysym(ev, 0) } } + +pub fn atom>>(display: *mut Display, name: T) -> Atom { + unsafe { + let cstr = CString::new(name).unwrap(); + XInternAtom(display, cstr.as_ptr(), 0) + } +} diff --git a/src/safe_x11/window.rs b/src/safe_x11/window.rs index 0e8ab10..6c8e48b 100644 --- a/src/safe_x11/window.rs +++ b/src/safe_x11/window.rs @@ -6,16 +6,25 @@ use x11::xlib::{ XWindowAttributes, XGetWindowAttributes, XDefaultScreenOfDisplay, - XMoveWindow, + 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}; @@ -25,6 +34,7 @@ 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 { @@ -41,6 +51,7 @@ impl XWindow { Some(XWindow { display: d, inner: w, + fullscreen: false, }) } else { None @@ -52,6 +63,25 @@ impl XWindow { 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); } @@ -116,6 +146,13 @@ impl XWindow { 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) { @@ -148,4 +185,33 @@ impl XWindow { 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); + } + } } diff --git a/src/socket/parser.rs b/src/socket/parser.rs index 0de0248..491c315 100644 --- a/src/socket/parser.rs +++ b/src/socket/parser.rs @@ -109,8 +109,11 @@ fn func<'a>(s: &'a str) -> Result { "exec" => Ok(exec), "move-win" => Ok(move_win), "move-win-to" => Ok(move_win_to), + "move-win-sticky" => Ok(move_win_sticky), "resize-win" => Ok(resize_win), "focus-next" => Ok(focus_next), + "close-win" => Ok(close_win), + "fullscreen" => Ok(fullscreen), "quit" => Ok(quit_dotwm), _ => Err("unknown function"), } -- cgit v1.2.3-54-g00ecf