From 5bcdb566c69523edb1ceb857cbf6d0a676cc318d Mon Sep 17 00:00:00 2001 From: Matias Linares Date: Sun, 4 Sep 2016 14:01:37 -0300 Subject: Add Mouse drag support. This concludes the first 'release' :p. --- src/command.rs | 38 +++++++++++++++++++++++++++++++++++++- src/desktop.rs | 28 ++++++++++++++++++++++++++-- src/dotwm.rs | 10 +++++++++- src/event.rs | 6 +++--- src/main.rs | 11 ++++++++++- src/safe_x11/mod.rs | 8 ++++++++ src/safe_x11/window.rs | 15 +++++++++++++-- src/socket/mod.rs | 22 ++++++++++++++++------ src/socket/parser.rs | 46 ++++++++++++++++++++++++++++++++++++++++++++-- 9 files changed, 166 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/command.rs b/src/command.rs index f5e3e83..eaadf77 100644 --- a/src/command.rs +++ b/src/command.rs @@ -17,7 +17,7 @@ use x11::xlib::{XEvent, GrabModeAsync}; use dotwm::DotWM; use dotwm::NetAtom; -use safe_x11::{grab_key,ungrab_key}; +use safe_x11::{grab_key,ungrab_key,grab_button,ungrab_button}; const WNOHANG: c_int = 0x00000001; @@ -132,6 +132,25 @@ pub fn move_win_sticky(wm: &mut DotWM, _: xlib::XEvent, args: &[String]) -> bool true } +/// Drag the window. for now the dragging is relative to the center of the +/// window because win.cursor_coords is always `None`. Later we should +/// be able to `XGrabButton` on the child windows (not only on the root) and +/// set `win.cursor_coords` respectively. +pub fn move_win_drag(wm: &mut DotWM, e: xlib::XEvent, _: &[String]) -> bool { + let ev = xlib::XMotionEvent::from(e); + if let Some(ref win) = wm.current_window() { + let attr = win.attributes(); + let (x0, y0) = win.cursor_coords.unwrap_or((attr.width/2, attr.height/2)); + // Move around the center of the cursor. + let x = ev.x - x0; + let y = ev.y - y0; + win.move_to(x, y).is_ok() + } else { + println!("move_win_drag - nop"); + false + } +} + /// 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(); @@ -222,6 +241,23 @@ pub fn add_binding(wm: &mut DotWM, bindings: &mut BindingHash, bindings.insert((key, modifiers), (func, v)); } +/// Add a button binding to the WM. +pub fn add_button_binding(wm: &mut DotWM, bindings: &mut BindingHash, + button: u32, modifiers: u32, func: ExecFn, args: &[&str]) { + ungrab_button(wm.display, button, modifiers); + grab_button(wm.display, button, modifiers, true, + xlib::ButtonPressMask|xlib::ButtonReleaseMask|xlib::PointerMotionMask, + GrabModeAsync, GrabModeAsync, 0, 0); + let mut v = vec![]; + for arg in args { + v.push(arg.to_string()); + } + // for now we need the button1Mask here + bindings.insert((button, modifiers | xlib::Button1Mask), (func, v)); +} + +/// Change the desktop, args parameter only have one usize on it that is the +/// index of the desktop (i.e.: 0 for the first, 1 for the second, etc). pub fn change_desktop(wm: &mut DotWM, _: xlib::XEvent, args: &[String]) -> bool { let num = args[0].parse::().unwrap(); wm.change_desktop(num); diff --git a/src/desktop.rs b/src/desktop.rs index 454c05b..20502e0 100644 --- a/src/desktop.rs +++ b/src/desktop.rs @@ -35,6 +35,7 @@ impl Desktop { self.cw_idx != usize::max_value() } + /// Get the current window focused on this desktop. pub fn current_window(&self) -> Option<&XWindow> { if self.cw_idx < self.window_list.len() { self.window_list.get(self.cw_idx) @@ -43,6 +44,7 @@ impl Desktop { } } + /// Same as `current_window` but getting a mut reference. 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) @@ -53,11 +55,16 @@ impl Desktop { /// Add a window to the current desktop pub fn add_window(&mut self, w: xlib::Window) { - if self.find_window(w).is_some() { + if self.window_idx(w).is_some() { return; } self.unfocus_current_window(); if let Some(w) = XWindow::new(self.display, w) { + // Throw an event when: + // * We enter a window + // * The window is exposed + // * We move around the window + //w.select_input(xlib::EnterWindowMask | xlib::ExposureMask | xlib::PointerMotionMask); w.select_input(xlib::EnterWindowMask | xlib::ExposureMask); self.window_list.push(w); // Last windows get focus. @@ -100,10 +107,27 @@ impl Desktop { } /// Find a given window and returns it's position on the window_list. - pub fn find_window(&self, w: xlib::Window) -> Option { + pub fn window_idx(&self, w: xlib::Window) -> Option { self.window_list.iter().position(|xw| xw.inner == w) } + /// Find a `XWindow` given a `xlib::Window`. + pub fn find_window(&self, w: xlib::Window) -> Option<&XWindow> { + if let Some(idx) = self.window_idx(w) { + self.window_list.get(idx) + } else { + None + } + } + /// Same as `find_window` but returning a mut reference. + pub fn find_window_mut(&mut self, w: xlib::Window) -> Option<&mut XWindow> { + if let Some(idx) = self.window_idx(w) { + self.window_list.get_mut(idx) + } else { + None + } + } + /// Focus the next window. /// /// There're 3 posibilities. There's no window, there's one window or there diff --git a/src/dotwm.rs b/src/dotwm.rs index fd28765..248e969 100644 --- a/src/dotwm.rs +++ b/src/dotwm.rs @@ -171,10 +171,18 @@ impl DotWM { } /// Find a given window and returns it's position on the window_list. - pub fn find_window(&self, w: xlib::Window) -> Option { + pub fn window_idx(&self, w: xlib::Window) -> Option { + self.current_desktop().window_idx(w) + } + + pub fn find_window(&self, w: xlib::Window) -> Option<&XWindow> { self.current_desktop().find_window(w) } + pub fn find_window_mut(&mut self, w: xlib::Window) -> Option<&mut XWindow> { + self.current_desktop_mut().find_window_mut(w) + } + /// Focus the next window. /// /// There're 3 posibilities. There's no window, there's one window or there diff --git a/src/event.rs b/src/event.rs index 768e37a..055a94b 100644 --- a/src/event.rs +++ b/src/event.rs @@ -62,7 +62,7 @@ impl fmt::Debug for Event { Event::Drag(ev) => format!("Drag({})", XEvent::from(ev).get_type()), Event::Generic(ev) => format!("Generic({})", XEvent::from(ev).get_type()), Event::Enter(ev) => format!("Enter({})", XEvent::from(ev).get_type()), - Event::Leave(ev) => format!("Enter({})", XEvent::from(ev).get_type()), + Event::Leave(ev) => format!("Leave({})", XEvent::from(ev).get_type()), Event::Create(ev) => format!("Create({})", XEvent::from(ev).get_type()), Event::Destroy(ev) => format!("Destroy({})", XEvent::from(ev).get_type()), Event::Unmap(ev) => format!("Unmap({})", XEvent::from(ev).get_type()), @@ -125,8 +125,8 @@ pub fn next_event(display: *mut Display) -> Event { 18 => Event::Unmap(XUnmapEvent::from(ev)), 19 => Event::Map(XMapEvent::from(ev)), 22 => Event::Configure(XConfigureEvent::from(ev)), - _ => { - // println!("Unknown event {}", e); + e => { + println!("Unknown event {}", e); Event::NoEvent }, } diff --git a/src/main.rs b/src/main.rs index dcef752..f745e92 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,6 +23,7 @@ use event::{Event,select_event}; use socket::parser; use std::collections::HashMap; +use std::mem::uninitialized; use x11::xlib; use x11::keysym; @@ -77,12 +78,20 @@ fn main() { dotwm.remove_window(e.window); }, Event::Enter(e) => { - if let Some(idx) = dotwm.find_window(e.window) { + if let Some(idx) = dotwm.window_idx(e.window) { dotwm.change_focus_of(idx); } }, Event::Expose(_) => { }, + Event::Drag(e) => { + // We can "compress" the drag notify here. + unsafe{ + let mut new_ev = uninitialized(); + while xlib::XCheckTypedEvent(dotwm.display, xlib::MotionNotify, &mut new_ev) != 0 {}; + }; + exec_func(&mut dotwm, &mut bindings, 1, e.state, xlib::XEvent::from(e)); + }, Event::Socket(stream) => { let mut s = stream.try_clone().unwrap(); let mut buf = String::new(); diff --git a/src/safe_x11/mod.rs b/src/safe_x11/mod.rs index 613424a..4a1e029 100644 --- a/src/safe_x11/mod.rs +++ b/src/safe_x11/mod.rs @@ -137,6 +137,14 @@ pub fn grab_button(display: *mut Display, button: u32, modifiers: u32, } } +/// Unregister a combination of keys that will send a `XEvent` +pub fn ungrab_button(display: *mut Display, button: u32, modifiers: u32) { + unsafe { + let default_win = xlib::XDefaultRootWindow(display); + xlib::XUngrabButton(display, button, modifiers, default_win); + } +} + /// Register a combination of keys that will send a `XEvent` /// pub fn grab_key(display: *mut Display, key: u32, modifiers: u32, owner_events: bool, diff --git a/src/safe_x11/window.rs b/src/safe_x11/window.rs index eac4dbd..6c483d9 100644 --- a/src/safe_x11/window.rs +++ b/src/safe_x11/window.rs @@ -41,6 +41,8 @@ pub struct XWindow { // The Application window. pub inner: Window, fullscreen: bool, + // Coordinates relative to the window where the cursor clicked on it. + pub cursor_coords: Option<(i32, i32)>, prev_attribs: XWindowAttributes, } @@ -64,11 +66,11 @@ impl XWindow { 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, + cursor_coords: None, prev_attribs: attrs, }) } else { @@ -155,8 +157,9 @@ impl XWindow { let s = XDefaultScreenOfDisplay(self.display); ptr::read(s) }; + let attrs = self.attributes(); - if 0 > x && x <= screen.width || 0 > y && y <= screen.height { + if x < -attrs.width && x <= screen.width || -attrs.height > y && y <= screen.height { Err("Cannot move the window outside the screen!") } else { unsafe { @@ -381,6 +384,14 @@ impl XWindow { } } +use std::fmt; + +impl fmt::Display for XWindow { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Window({})", self.inner) + } +} + pub fn change_window_attributes(display: *mut xlib::Display, win: Window, mask: u64, window_attribs: *mut XSetWindowAttributes) { unsafe { diff --git a/src/socket/mod.rs b/src/socket/mod.rs index c29db2b..6ee1c09 100644 --- a/src/socket/mod.rs +++ b/src/socket/mod.rs @@ -11,7 +11,8 @@ use command::*; #[derive(Debug,PartialEq)] pub enum FnType { - Bind, + BindKey, + BindButton, Exec, } @@ -25,11 +26,20 @@ pub struct ParsedCmd<'a> { impl<'a> ParsedCmd<'a> { pub fn handle(self, wm: &mut DotWM, bindings: &mut BindingHash) { - if self.f == FnType::Bind { - let modifier: u32 = self.modifiers.iter() - .fold(0, |acc, x| acc | x ); - add_binding(wm, bindings, - self.key, modifier, self.func, &self.args); + match self.f { + FnType::BindKey => { + let modifier: u32 = self.modifiers.iter() + .fold(0, |acc, x| acc | x ); + add_binding(wm, bindings, + self.key, modifier, self.func, &self.args); + }, + FnType::BindButton => { + let modifier: u32 = self.modifiers.iter() + .fold(0, |acc, x| acc | x); + add_button_binding(wm, bindings, self.key, + modifier, self.func, &self.args); + }, + _ => {}, } } } diff --git a/src/socket/parser.rs b/src/socket/parser.rs index e475962..0989aec 100644 --- a/src/socket/parser.rs +++ b/src/socket/parser.rs @@ -44,6 +44,15 @@ fn modifiers<'a>(s: &'a str) -> Result, &'static str> { Ok(result) } +fn button<'a>(s: &'a str) -> Result { + match s { + "button1" => Ok(xlib::Button1), + "button2" => Ok(xlib::Button2), + "button3" => Ok(xlib::Button3), + _ => Err("unknown button") + } +} + fn key<'a>(s: &'a str) -> Result { match s { "a" => Ok(keysym::XK_a), @@ -102,7 +111,10 @@ fn key<'a>(s: &'a str) -> Result { "2" => Ok(keysym::XK_2), "Tab" => Ok(keysym::XK_Tab), "Return" => Ok(keysym::XK_Return), - _ => Err("unknown key"), + e => { + println!("Unknown Key {}", e); + Err("Unknown key") + }, } } @@ -112,6 +124,7 @@ fn func<'a>(s: &'a str) -> Result { "move-win" => Ok(move_win), "move-win-to" => Ok(move_win_to), "move-win-sticky" => Ok(move_win_sticky), + "move-win-drag" => Ok(move_win_drag), "resize-win" => Ok(resize_win), "resize-win-sticky" => Ok(resize_win_sticky), "focus-next" => Ok(focus_next), @@ -137,7 +150,25 @@ pub fn parse<'a>(input: &'a str) -> Result, &'static str> { let arguments: &[&'a str]= &args[4..]; Ok(ParsedCmd { - f: FnType::Bind, + f: FnType::BindKey, + modifiers: modifiers, + key: key, + args: arguments.to_vec(), + func: func, + }) + } else { + Err("missing arguments") + } + }, + "bind-button" => { + if args.len() > 2 { + let modifiers = simple_try!(modifiers(args[1])); + let key = simple_try!(button(args[2])); + let func = simple_try!(func(args[3])); + let arguments: &[&'a str] = &args[4..]; + + Ok(ParsedCmd { + f: FnType::BindButton, modifiers: modifiers, key: key, args: arguments.to_vec(), @@ -218,3 +249,14 @@ fn parse_exec() { assert!(false); } } + +#[test] +fn parse_button() { + let res = parse("bind-button Mod4 button1 move-win-drag"); + + if let Ok(pcmd) = res { + assert_eq!(pcmd.f, FnType::BindButton); + } else { + assert!(false); + } +} -- cgit v1.2.3-70-g09d2