From e1ec354306742a5804a20e2651cf49945cd17287 Mon Sep 17 00:00:00 2001 From: Matias Linares Date: Sat, 28 Nov 2015 22:16:43 -0300 Subject: Better socket handling The socket interface now allows almost everything that vould be done by the internals of the window manager. Also the Window manager now closes fine (on a success exit). --- autostart | 24 ++++-- src/command.rs | 29 ++++++- src/dotwm.rs | 3 + src/main.rs | 33 ++++---- src/socket/mod.rs | 49 +++++++----- src/socket/parser.rs | 215 ++++++++++++++++++++++++++++++++++++++------------- 6 files changed, 255 insertions(+), 98 deletions(-) diff --git a/autostart b/autostart index 1e98405..a0b4919 100755 --- a/autostart +++ b/autostart @@ -1,10 +1,22 @@ #!/bin/mksh netcat -U dotwm.sock < bool; /// Map for keys => functions pub type BindingHash = HashMap<(u32, u32), (ExecFn, Vec)>; +/// Exec a binding function. pub fn exec_func(wm: &mut DotWM, bindings: &mut BindingHash, key: u32, modifiers: u32, ev: xlib::XEvent) { if let Some(&(func, ref args)) = bindings.get(&(key, modifiers)) { let v = args.clone(); @@ -54,6 +54,7 @@ pub fn exec_func(wm: &mut DotWM, bindings: &mut BindingHash, key: u32, modifiers } } +/// Exec a external function pub fn exec(_: &mut DotWM, _: xlib::XEvent, args: &[String]) -> bool { if let Some(program) = args.first() { let mut prog_args = vec![]; @@ -65,6 +66,7 @@ pub fn exec(_: &mut DotWM, _: xlib::XEvent, args: &[String]) -> bool { 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(); let y = args[1].parse::().unwrap(); @@ -74,6 +76,24 @@ pub fn move_win(wm: &mut DotWM, _: xlib::XEvent, args: &[String]) -> bool { true } +/// Move the window to an absolute position. +pub fn move_win_to(wm: &mut DotWM, _: xlib::XEvent, args: &[String]) -> bool { + let x = args[0].parse::().unwrap(); + let y = args[1].parse::().unwrap(); + if let Some(ref win) = wm.current_window() { + match win.move_to(x, y) { + Ok(()) => true, + Err(e) => { + println!("{}", e); + false + } + } + } else { + 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(); let h = args[1].parse::().unwrap(); @@ -85,13 +105,16 @@ pub fn resize_win(wm: &mut DotWM, _: xlib::XEvent, args: &[String]) -> bool { true } +/// Focus the next window on the list pub fn focus_next(wm: &mut DotWM, _: xlib::XEvent, _: &[String]) -> bool { wm.focus_next(); true } -pub fn quit_dotwm(_: &mut DotWM, _: xlib::XEvent, _: &[String]) -> bool { - process::exit(0); +/// Tells the window manager that is time to exit. +pub fn quit_dotwm(wm: &mut DotWM, _: xlib::XEvent, _: &[String]) -> bool { + wm.finish = true; + true } /// Add a binding to the WM. diff --git a/src/dotwm.rs b/src/dotwm.rs index e531f0a..cd909b5 100644 --- a/src/dotwm.rs +++ b/src/dotwm.rs @@ -35,6 +35,8 @@ pub struct DotWM { pub display: *mut Display, /// References to all the windows. pub window_list: Vec, + /// Check if the window manager needs to end. + pub finish: bool, // Map with the keys as (key, mod) cw_idx: usize, } @@ -56,6 +58,7 @@ impl DotWM { DotWM { display: d, cw_idx: 0, + finish: false, window_list: vec![], } } diff --git a/src/main.rs b/src/main.rs index 21133ef..c9cc11e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,35 +23,26 @@ use x11::xlib; use x11::xlib::XEvent; use x11::keysym; +use std::fs; use std::io::{Read,Write}; use unix_socket::UnixListener; +const SOCKET_PATH: &'static str = "./dotwm.sock"; + fn main() { println!("Creating dotwm"); let mut dotwm = DotWM::new(); let mut bindings: BindingHash = HashMap::new(); let x11_fd = safe_x11::x11_fd(dotwm.display); - // Resize - add_binding(&mut dotwm,&mut bindings, keysym::XK_h, xlib::Mod4Mask | xlib::ControlMask, - resize_win, &["-10", "0"]); - add_binding(&mut dotwm,&mut bindings, keysym::XK_j, xlib::Mod4Mask | xlib::ControlMask, - resize_win, &["0", "10"]); - add_binding(&mut dotwm,&mut bindings, keysym::XK_k, xlib::Mod4Mask | xlib::ControlMask, - resize_win, &["0", "-10"]); - add_binding(&mut dotwm,&mut bindings, keysym::XK_l, xlib::Mod4Mask | xlib::ControlMask, - resize_win, &["10", "0"]); - - add_binding(&mut dotwm,&mut bindings, keysym::XK_Return, xlib::Mod4Mask, exec, &["xterm"]); - add_binding(&mut dotwm,&mut bindings, keysym::XK_Tab, xlib::Mod4Mask, focus_next, &[]); add_binding(&mut dotwm, &mut bindings, keysym::XK_q, xlib::Mod4Mask | xlib::ShiftMask, quit_dotwm, &[]); - let listener = UnixListener::bind("./dotwm.sock").unwrap(); + let listener = UnixListener::bind(SOCKET_PATH).unwrap(); exec_cmd("./autostart", &[]).unwrap(); // Main loop - loop { + while !dotwm.finish { let event = unsafe { select_event(dotwm.display, x11_fd, &listener) }; match event { Event::Key(mut e, true) => { @@ -84,12 +75,22 @@ fn main() { s.read_to_string(&mut buf).unwrap(); for line in buf.lines() { - let result = parser::parse(&mut dotwm, &mut bindings, &line); - let _ = write!(s, "{}", result); + let res = parser::parse(&line); + match res { + Ok(pcmd) => { + pcmd.handle(&mut dotwm, &mut bindings); + let _ = write!(s, "+ok"); + }, + Err(e) => { + let _ = write!(s, "-{}", e); + }, + } } }, _ => println!("Unknown event"), } collect_zombies(); } + + let _ = fs::remove_file(SOCKET_PATH); } diff --git a/src/socket/mod.rs b/src/socket/mod.rs index e260284..667969b 100644 --- a/src/socket/mod.rs +++ b/src/socket/mod.rs @@ -2,7 +2,6 @@ pub mod parser; -use std::io::{Read,Write}; use std::sync::mpsc::{Receiver, TryRecvError}; use unix_socket::UnixStream; @@ -10,6 +9,34 @@ use unix_socket::UnixStream; use dotwm::DotWM; use command::*; +#[derive(Debug,PartialEq)] +pub enum FnType { + Bind, + Exec, +} + +pub struct ParsedCmd<'a> { + pub f: FnType, + pub modifiers: Vec, + pub key: u32, + pub args: Vec<&'a str>, + pub func: ExecFn, +} + +impl<'a> ParsedCmd<'a> { + pub fn handle(self, wm: &mut DotWM, bindings: &mut BindingHash) { + match self.f { + FnType::Bind => { + let modifier = self.modifiers.iter() + .fold(0, |acc, x| acc | x ); + add_binding(wm, bindings, + self.key, modifier, self.func, &self.args); + }, + _ => (), + } + } +} + pub fn next_socket_event(rx: &Receiver) -> Option { match rx.try_recv() { Ok(stream) => Some(stream), @@ -17,23 +44,3 @@ pub fn next_socket_event(rx: &Receiver) -> Option { Err(TryRecvError::Disconnected) => panic!("Socket disconnected"), } } - -/// Listen a socket parsing and executing all the commands. -pub fn listen_socket(dotwm: &mut DotWM, bindings: &mut BindingHash, rx: &Receiver) { - loop { - match rx.try_recv() { - Ok(stream) => { - let mut s = stream.try_clone().unwrap(); - let mut buf = String::new(); - s.read_to_string(&mut buf).unwrap(); - - for line in buf.lines() { - let result = parser::parse(dotwm, bindings, &line); - let _ = write!(s, "{}", result); - } - }, - Err(TryRecvError::Empty) => break, - Err(TryRecvError::Disconnected) => panic!("Socket disconnected"), - } - } -} diff --git a/src/socket/parser.rs b/src/socket/parser.rs index e8dd9ec..0de0248 100644 --- a/src/socket/parser.rs +++ b/src/socket/parser.rs @@ -9,94 +9,205 @@ use x11::xlib; use x11::keysym; use command::*; -use dotwm::DotWM; +use ::socket::ParsedCmd; +use ::socket::FnType; -use nom::{IResult,alpha,multispace}; -use nom::IResult::*; -use std::str; - -named!(token<&str>, - map_res!( - alpha, str::from_utf8)); +macro_rules! simple_try { + ($expr:expr) => (match $expr { + Ok(val) => val, + Err(err) => { + return Err(err) + } + }) +} -fn slice2str(input: Vec<&[u8]>) -> Vec<&str> { - let res: Vec<&str> = input.iter() - .map(|x| str::from_utf8(x).unwrap()) - .collect(); - res +fn modifier_from<'a>(s: &'a str) -> Result { + match s { + "Mod1" => Ok(xlib::Mod4Mask), + "Mod2" => Ok(xlib::Mod4Mask), + "Mod3" => Ok(xlib::Mod4Mask), + "Mod4" => Ok(xlib::Mod4Mask), + "Control" => Ok(xlib::ControlMask), + "Shift" => Ok(xlib::ShiftMask), + _ => Err("unknown modifier"), + } } -named!(args >, - map!( - separated_list!( - multispace, alpha), - slice2str)); +fn modifiers<'a>(s: &'a str) -> Result, &'static str> { + let mut result = vec![]; -fn modifier<'a>(s: &'a str) -> Result { - match s { - "Mod1Mask" => Ok(xlib::Mod4Mask), - "Mod2Mask" => Ok(xlib::Mod4Mask), - "Mod3Mask" => Ok(xlib::Mod4Mask), - "Mod4Mask" => Ok(xlib::Mod4Mask), - "ControlMask" => Ok(xlib::ControlMask), - "ShiftMask" => Ok(xlib::ShiftMask), - _ => Err(()), + for smod in s.split("-") { + let modifier = simple_try!(modifier_from(smod)); + result.push(modifier); } + + Ok(result) } -fn str_to_key<'a>(s: &'a str) -> Result { +fn key<'a>(s: &'a str) -> Result { match s { + "a" => Ok(keysym::XK_a), + "b" => Ok(keysym::XK_b), + "c" => Ok(keysym::XK_c), + "d" => Ok(keysym::XK_d), + "e" => Ok(keysym::XK_e), + "f" => Ok(keysym::XK_f), + "g" => Ok(keysym::XK_g), "h" => Ok(keysym::XK_h), + "i" => Ok(keysym::XK_i), "j" => Ok(keysym::XK_j), "k" => Ok(keysym::XK_k), "l" => Ok(keysym::XK_l), - "Tab" => Ok(keysym::XK_Tab), + "m" => Ok(keysym::XK_m), + "n" => Ok(keysym::XK_n), + "o" => Ok(keysym::XK_o), "p" => Ok(keysym::XK_p), - "Return" => Ok(keysym::XK_Return), "q" => Ok(keysym::XK_q), - _ => Err(()), + "r" => Ok(keysym::XK_r), + "s" => Ok(keysym::XK_s), + "t" => Ok(keysym::XK_t), + "u" => Ok(keysym::XK_u), + "v" => Ok(keysym::XK_v), + "w" => Ok(keysym::XK_w), + "x" => Ok(keysym::XK_x), + "y" => Ok(keysym::XK_y), + "z" => Ok(keysym::XK_z), + "A" => Ok(keysym::XK_A), + "B" => Ok(keysym::XK_B), + "C" => Ok(keysym::XK_C), + "D" => Ok(keysym::XK_D), + "E" => Ok(keysym::XK_E), + "F" => Ok(keysym::XK_F), + "G" => Ok(keysym::XK_G), + "H" => Ok(keysym::XK_H), + "I" => Ok(keysym::XK_I), + "J" => Ok(keysym::XK_J), + "K" => Ok(keysym::XK_K), + "L" => Ok(keysym::XK_L), + "M" => Ok(keysym::XK_M), + "N" => Ok(keysym::XK_N), + "O" => Ok(keysym::XK_O), + "P" => Ok(keysym::XK_P), + "Q" => Ok(keysym::XK_Q), + "R" => Ok(keysym::XK_R), + "S" => Ok(keysym::XK_S), + "T" => Ok(keysym::XK_T), + "U" => Ok(keysym::XK_U), + "V" => Ok(keysym::XK_V), + "W" => Ok(keysym::XK_W), + "X" => Ok(keysym::XK_X), + "Y" => Ok(keysym::XK_Y), + "Z" => Ok(keysym::XK_Z), + "Tab" => Ok(keysym::XK_Tab), + "Return" => Ok(keysym::XK_Return), + _ => Err("unknown key"), } } -fn str_to_func<'a>(s: &'a str) -> Result { +fn func<'a>(s: &'a str) -> Result { match s { "exec" => Ok(exec), - "move_win" => Ok(move_win), - "resize_win" => Ok(resize_win), - _ => Err(()), + "move-win" => Ok(move_win), + "move-win-to" => Ok(move_win_to), + "resize-win" => Ok(resize_win), + "focus-next" => Ok(focus_next), + "quit" => Ok(quit_dotwm), + _ => Err("unknown function"), } } -pub fn parse<'a>(dotwm: &mut DotWM, bindings: &mut BindingHash, input: &'a str) -> &'a str { - let args: Vec<&str> = input.split_whitespace().collect(); +pub fn parse<'a>(input: &'a str) -> Result, &'static str> { + let args: Vec<&'a str> = input.split_whitespace().collect(); match args.first() { Some(cmd) => { match cmd { - &"add-binding" => { - let modifier = modifier(args[1]).unwrap(); - let key = str_to_key(args[2]).unwrap(); - let func = str_to_func(args[3]).unwrap(); - let arguments = &args[4..]; + &"bind" => { + if args.len() > 2 { + let modifiers = simple_try!(modifiers(args[1])); + let key = simple_try!(key(args[2])); + let func = simple_try!(func(args[3])); + let arguments: &[&'a str]= &args[4..]; - add_binding(dotwm, bindings, key, modifier, func, arguments); + Ok(ParsedCmd { + f: FnType::Bind, + modifiers: modifiers, + key: key, + args: arguments.to_vec(), + func: func, + }) + } else { + Err("missing arguments") + } + }, + &"exec" => { + Ok(ParsedCmd { + f: FnType::Exec, + modifiers: vec![], + key: 0, + args: vec![], + func: exec + }) + }, + _ => { + Err("unknown command") }, - &"exec" => { println!("exec"); }, - _ => { println!("anotherthing ._."); }, } }, - None => println!("error"), + None => Err("no input"), + } +} + +#[test] +fn parse_unknown() { + let res = parse("unknown"); + if let Err(e) = res { + assert_eq!(e, "unknown command"); + } else { + assert!(false); } - "ok" } #[test] -fn parse_test() { - assert_eq!(token(&b"exec"[..]), IResult::Done(&b""[..], "exec")); +fn parse_bind_modifier() { + let res = parse("bind modifier1 k exec "); + if let Err(e) = res { + assert_eq!(e, "unknown modifier"); + } else { + assert!(false); + } + + let res2 = parse("bind Mod4 k exec "); + if let Ok(modifier) = res2 { + assert_eq!(modifier.modifiers[0], xlib::Mod4Mask); + } else { + assert!(false); + } } #[test] -fn parse_args() { - assert_eq!(args(&b"hola chau"[..]), - IResult::Done(&b""[..], vec!["hola", "chau"])); +fn parse_bind_key() { + let res = parse("bind Mod4 ' exec "); + if let Err(e) = res { + assert_eq!(e, "unknown key"); + } else { + assert!(false); + } + + let res2 = parse("bind Mod4 k exec "); + if let Ok(modifier) = res2 { + assert_eq!(modifier.key, keysym::XK_k); + } else { + assert!(false); + } +} + +#[test] +fn parse_exec() { + let res = parse("exec Mod4 ' exec "); + if let Ok(pcmd) = res { + assert_eq!(pcmd.f, FnType::Exec); + } else { + assert!(false); + } } -- cgit v1.2.3-70-g09d2