summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/command.rs47
-rw-r--r--src/dotwm.rs140
-rw-r--r--src/event.rs69
-rw-r--r--src/main.rs75
-rw-r--r--src/safe_x11/mod.rs201
-rw-r--r--src/safe_x11/window.rs144
6 files changed, 676 insertions, 0 deletions
diff --git a/src/command.rs b/src/command.rs
new file mode 100644
index 0000000..ddfa269
--- /dev/null
+++ b/src/command.rs
@@ -0,0 +1,47 @@
+//! Command execution module.
+//!
+use std::ffi::OsStr;
+use std::process::{Command,Child};
+use std::io::{Result, Error, ErrorKind};
+
+use libc::c_int;
+use libc::types::os::arch::posix88::pid_t;
+
+const WNOHANG: c_int = 0x00000001;
+
+extern {
+ /// wait for a child process to stop or terminate.
+ pub fn waitpid(pid: pid_t, stat_loc: *mut c_int, options: c_int) -> pid_t;
+}
+
+/// Extension to the Child struct that allows to check the status without block the
+/// current thread.
+pub trait ChildExt {
+ fn wait_nohang(&mut self) -> Result<bool>;
+}
+
+impl ChildExt for Child {
+ /// Checks if the calling child is a zombie and try to kill it, returning
+ /// if it was killed or not.
+ fn wait_nohang(&mut self) -> Result<bool> {
+ let mut stat = 0i32;
+ let pid = unsafe { waitpid(self.id() as i32, &mut stat, WNOHANG) };
+ if pid < 0 {
+ Err(Error::new(ErrorKind::NotFound, format!("pid not found {}", pid)))
+ } else {
+ if pid > 0 {
+ Ok(self.wait()
+ .map(|x| {
+ x.success()
+ }).unwrap_or(false))
+ } else {
+ Ok(false)
+ }
+ }
+ }
+}
+
+/// Executes the given command.
+pub fn exec_cmd<S: AsRef<OsStr>>(program: S, args: &[S]) -> Result<Child> {
+ Command::new(program).args(args).spawn()
+}
diff --git a/src/dotwm.rs b/src/dotwm.rs
new file mode 100644
index 0000000..2d64584
--- /dev/null
+++ b/src/dotwm.rs
@@ -0,0 +1,140 @@
+use x11::xlib;
+use x11::xlib::{
+ Display,
+ XErrorEvent,
+ XEvent,
+
+ GrabModeAsync,
+};
+
+use safe_x11::{
+ open_display,
+ close_display,
+ grab_key,
+};
+
+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
+}
+
+/// Defines a callback to call no certains events.
+pub type ExecFn = fn(&DotWM, XEvent, &[String]) -> bool;
+
+pub struct DotWM {
+ pub display: *mut Display,
+ // Map with the keys as (key, mod)
+ bindings: HashMap<(u32, u32), (ExecFn, Vec<String>)>,
+ cw_idx: usize,
+ pub window_list: Vec<XWindow>,
+}
+
+/// 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)) };
+ // Some testings.
+ 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);
+ }
+
+ DotWM {
+ display: d,
+ bindings: HashMap::new(),
+ cw_idx: 0,
+ window_list: vec![],
+ }
+ }
+
+ /// Add a binding to the WM.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// fn exec(_: &DotWM, _: xlib::XEvent, args: &[String]) -> bool {
+ /// if let Some(program) = args.first() {
+ /// let mut prog_args = vec![];
+ /// for arg in args[1..].iter() {
+ /// prog_args.push(arg);
+ /// }
+ /// exec_cmd(program, prog_args.deref()).unwrap();
+ /// }
+ /// true
+ /// }
+ ///
+ /// // ...
+ ///
+ /// dotwm.add_binding(keysym::XK_Return, xlib::Mod4Mask, exec,
+ /// &["xterm"]);
+ /// ```
+ pub fn add_binding(&mut self, key: u32, modifiers: u32, func: ExecFn, args: &[&str]) {
+ grab_key(self.display, key, modifiers, true, GrabModeAsync, GrabModeAsync);
+ let mut v = vec![];
+ for arg in args {
+ v.push(arg.to_string());
+ }
+ self.bindings.insert((key, modifiers), (func, v));
+ }
+
+ pub fn exec_func(&mut self, key: u32, modifiers: u32, ev: xlib::XEvent) {
+ if let Some(&(func, ref args)) = self.bindings.get(&(key, modifiers)) {
+ let v = args.clone();
+ func(&self, ev, &v);
+ }
+ }
+
+ pub fn add_window(&mut self, w: xlib::Window) {
+ if let Some(w) = XWindow::new(self.display, w) {
+ self.window_list.push(w);
+ // Last windows get focus.
+ self.cw_idx = self.window_list.len() - 1;
+ }
+ }
+
+ 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 remove_window(&mut self, w: xlib::Window) {
+ let pos = self.window_list.iter().position(|xw| xw.inner == w);
+
+ match pos {
+ Some(idx) => {
+ self.window_list.remove(idx);
+ self.cw_idx = usize::max_value();
+ },
+ None => (),
+ }
+ }
+}
+
+impl Drop for DotWM {
+ fn drop(&mut self) {
+ println!("Closing display");
+ close_display(self.display);
+ }
+}
diff --git a/src/event.rs b/src/event.rs
new file mode 100644
index 0000000..e15ee18
--- /dev/null
+++ b/src/event.rs
@@ -0,0 +1,69 @@
+use x11::xlib::{
+ XEvent,
+ XKeyEvent,
+ XButtonEvent,
+ XMotionEvent,
+ XCrossingEvent,
+ XCreateWindowEvent,
+ XDestroyWindowEvent,
+ XConfigureEvent,
+};
+
+use x11::xlib;
+use x11::xlib::Display;
+use std::fmt;
+
+use safe_x11::next_xevent;
+
+pub enum Event {
+ Key(XKeyEvent, bool),
+ Button(XButtonEvent, bool),
+ Drag(XMotionEvent),
+ Enter(XCrossingEvent),
+ Leave(XCrossingEvent),
+ Generic(XEvent),
+ Create(XCreateWindowEvent),
+ Destroy(XDestroyWindowEvent),
+ Configure(XConfigureEvent),
+ NoEvent
+}
+
+impl fmt::Debug for Event {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let s = match self {
+ &Event::Key(ev, pressed) => format!("Key({}, {})", XEvent::from(ev).get_type(), pressed),
+ &Event::Button(ev, pressed) => format!("Button({}, {})", XEvent::from(ev).get_type(), pressed),
+ &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::Create(ev) => format!("Create({})", XEvent::from(ev).get_type()),
+ &Event::Destroy(ev) => format!("Destroy({})", XEvent::from(ev).get_type()),
+ &Event::Configure(ev) => format!("Configure({})", XEvent::from(ev).get_type()),
+ &Event::NoEvent => format!("NoEvent"),
+ };
+
+ write!(f, "{}", s)
+ }
+}
+
+pub fn next_event(display: *mut Display) -> Event {
+ let ev = next_xevent(display);
+
+ match ev.get_type() {
+ 2 => Event::Key(xlib::XKeyEvent::from(ev), true),
+ 3 => Event::Key(xlib::XKeyEvent::from(ev), false),
+ 4 => Event::Button(xlib::XButtonEvent::from(ev), true),
+ 5 => Event::Button(xlib::XButtonEvent::from(ev), false),
+ 6 => Event::Drag(XMotionEvent::from(ev)),
+ 7 => Event::Enter(XCrossingEvent::from(ev)),
+ 8 => Event::Leave(XCrossingEvent::from(ev)),
+ 16 => Event::Create(XCreateWindowEvent::from(ev)),
+ 17 => Event::Destroy(XDestroyWindowEvent::from(ev)),
+ 22 => Event::Configure(XConfigureEvent::from(ev)),
+ e => {
+ println!("Unknown event {}", e);
+ Event::NoEvent
+ },
+ }
+}
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..c89544d
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,75 @@
+extern crate libc;
+extern crate x11;
+
+pub mod safe_x11;
+pub mod command;
+pub mod dotwm;
+pub mod event;
+
+use dotwm::DotWM;
+use command::exec_cmd;
+use event::{Event,next_event};
+use std::ops::Deref;
+use std::process;
+
+use x11::xlib;
+use x11::keysym;
+
+fn exec(_: &DotWM, _: xlib::XEvent, args: &[String]) -> bool {
+ if let Some(program) = args.first() {
+ let mut prog_args = vec![];
+ for arg in args[1..].iter() {
+ prog_args.push(arg);
+ }
+ exec_cmd(program, prog_args.deref()).unwrap();
+ }
+ true
+}
+
+fn move_win(wm: &DotWM, _: xlib::XEvent, args: &[String]) -> bool {
+ println!("Parsing x");
+ let x = args[0].parse::<i32>().unwrap();
+ println!("Parsing y");
+ let y = args[1].parse::<i32>().unwrap();
+ if let Some(ref win) = wm.current_window() {
+ win.move_offset(x, y);
+ };
+ true
+}
+
+fn quit_dotwm(_: &DotWM, _: xlib::XEvent, _: &[String]) -> bool {
+ process::exit(0);
+}
+
+fn main() {
+ println!("Creating dotwm");
+ let mut dotwm = DotWM::new();
+
+ dotwm.add_binding(keysym::XK_Return, xlib::Mod4Mask, exec,
+ &["xterm"]);
+ dotwm.add_binding(keysym::XK_h, xlib::Mod4Mask, move_win, &["-10", "0"]);
+ dotwm.add_binding(keysym::XK_j, xlib::Mod4Mask, move_win, &["0", "10"]);
+ dotwm.add_binding(keysym::XK_k, xlib::Mod4Mask, move_win, &["0", "-10"]);
+ dotwm.add_binding(keysym::XK_l, xlib::Mod4Mask, move_win, &["10", "0"]);
+
+ dotwm.add_binding(keysym::XK_q, xlib::Mod4Mask | xlib::ShiftMask, quit_dotwm, &[]);
+
+ // Main loop
+ loop {
+ let event = next_event(dotwm.display);
+ match event {
+ Event::Key(mut e, true) => {
+ let keysym = unsafe { xlib::XLookupKeysym(&mut e, 0) as u32 };
+ let mask = e.state;
+
+ dotwm.exec_func(keysym, mask, xlib::XEvent::from(e));
+ },
+ Event::Create(e) => {
+ // XCreateWindowEvent
+ let create_event = xlib::XCreateWindowEvent::from(e);
+ dotwm.add_window(create_event.window);
+ }
+ _ => println!("Catched event! {:?}", event),
+ }
+ }
+}
diff --git a/src/safe_x11/mod.rs b/src/safe_x11/mod.rs
new file mode 100644
index 0000000..199c402
--- /dev/null
+++ b/src/safe_x11/mod.rs
@@ -0,0 +1,201 @@
+//! Safe wrapper around the `x11-rs` crate. This should be as much safe as possible.
+
+#![allow(dead_code)]
+use x11::xlib;
+use x11::xlib::{
+ Display,
+ Screen,
+ Cursor,
+ XDefaultRootWindow,
+ XQueryTree,
+
+ // Windows
+ Window,
+
+ // Events
+ XEvent,
+};
+use x11::xlib::XKeyEvent;
+
+use std::ffi::CString;
+use std::ffi::NulError;
+use std::ptr;
+use std::error;
+use std::mem::uninitialized;
+use std::slice;
+use std::fmt;
+
+pub mod window;
+
+#[derive(Debug)]
+pub struct XSafeError<'a> {
+ _description: &'a str,
+}
+
+impl<'a> XSafeError<'a> {
+ fn new(desc: &'static str) -> XSafeError<'a> {
+ XSafeError { _description: desc }
+ }
+}
+
+impl<'a> From<NulError> for XSafeError<'a> {
+ fn from(_: NulError) -> XSafeError<'a> {
+ XSafeError::new("Null ponter error!")
+ }
+}
+
+impl<'a> error::Error for XSafeError<'a> {
+ fn description(&self) -> &str {
+ self._description
+ }
+
+ fn cause(&self) -> Option<&error::Error> {
+ None
+ }
+}
+
+impl<'a> fmt::Display for XSafeError<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+ write!(f, "{}", self._description)
+ }
+}
+
+/// Open a display. If the string passed by parameter is `""`, it will open the
+/// default display.
+///
+/// # Failures
+///
+/// This function can raise a NulError when the bytes yielded contains an internal
+/// 0 byte.
+///
+pub fn open_display(display: &str) -> Result<*mut Display, XSafeError> {
+ let d = if display == "" {
+ println!("Opening default display");
+ unsafe { xlib::XOpenDisplay(0x0 as *const i8) }
+ } else {
+ println!("Opening {}", display);
+ let cstr = try!(CString::new(display));
+ unsafe { xlib::XOpenDisplay(cstr.as_ptr()) }
+ };
+
+ if d.is_null() {
+ Err(XSafeError::new("Cannot open display!"))
+ } else {
+ Ok(d)
+ }
+}
+
+pub fn close_display(display: *mut Display) {
+ unsafe { xlib::XCloseDisplay(display); }
+}
+
+/// Grab pointer buttons.
+///
+/// # Example
+///
+/// ```
+/// grab_button(
+/// // Display
+/// display,
+/// // Button to press
+/// xlib::Button1 as i32,
+/// // Modifiers (can be an or of modifiers).
+/// xlib::Mod1Mask,
+/// // owner events.
+/// true,
+/// // Which motions we'll handle
+/// ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
+/// // Keyboard and pointer sync moddes
+/// GrabModeAsync, GrabModeAsync,
+/// // Confine to and cursor. (Can be 0 :) )
+/// 0, 0
+/// );
+/// ```
+pub fn grab_button(display: *mut Display, button: u32, modifiers: u32,
+ owner_events: bool, event_mask: i64, pointer_mode: i32,
+ keyboard_mode: i32, confine_to: Window,
+ cursor: Cursor) {
+ unsafe {
+ let default_win = xlib::XDefaultRootWindow(display);
+ xlib::XGrabButton(display, button, modifiers , default_win,
+ owner_events as i32, event_mask as u32, pointer_mode,
+ keyboard_mode, confine_to, cursor);
+ }
+}
+
+/// Register a combination of keys that will send a `XEvent`
+///
+pub fn grab_key(display: *mut Display, key: u32, modifiers: u32, owner_events: bool,
+ pointer_mode: i32, keyboard_mode: i32) {
+ unsafe {
+ let default_win = xlib::XDefaultRootWindow(display);
+ let keycode: i32 = xlib::XKeysymToKeycode(display, key as u64) as i32;
+ xlib::XGrabKey(display, keycode, modifiers, default_win,
+ owner_events as i32, pointer_mode, keyboard_mode);
+ }
+}
+
+/// Get the next event from the xserver. This call will block until a
+/// event spawns.
+pub fn next_xevent(display: *mut Display) -> XEvent {
+ unsafe {
+ let mut last_event: XEvent = uninitialized();
+ println!("xevent: getting next event!");
+ xlib::XNextEvent(display, &mut last_event);
+ last_event
+ }
+}
+
+// Window code.
+
+/// Get a list from the active windows. This doesn't generate a hierarchical
+/// struture, so it may change on the future.
+pub fn window_list(display: *mut Display) -> &'static [Window] {
+ let mut root: Window = 0;
+ let mut parent: Window = 0;
+ let mut children_count: u32 = 0;
+ unsafe {
+ let root_window = XDefaultRootWindow(display);
+ let mut children: *mut Window = uninitialized();
+ XQueryTree(display, root_window,
+ &mut root, &mut parent,
+ &mut children, &mut children_count);
+ slice::from_raw_parts(children as *const Window, children_count as usize)
+ }
+}
+
+fn screen_ratio(d: *mut Display) -> f32 {
+ let screen: xlib::Screen = unsafe {
+ let scrptr = xlib::XDefaultScreenOfDisplay(d);
+ ptr::read(scrptr)
+ };
+
+ screen.height as f32 / screen.width as f32
+}
+
+/// Get certain color from the display.
+///
+/// # Example.
+///
+/// This function can be called with a &str
+///
+/// ```
+/// get_color(display, "red");
+/// ```
+fn get_color<T: Into<Vec<u8>>>(display: *mut Display, color: T) -> u64{
+ let screen_num = unsafe { xlib::XDefaultScreen(display) };
+ let colormap = unsafe { xlib::XDefaultColormap(display, screen_num) };
+ unsafe {
+ let mut xcolor: xlib::XColor = uninitialized();
+ let cstr = CString::new(color).unwrap();
+ xlib::XAllocNamedColor(display, colormap, cstr.as_ptr(),
+ &mut xcolor, &mut xcolor);
+
+ xcolor.pixel
+ }
+}
+
+/// Get the keysym for the given event.
+pub fn lookup_keysym(ev: &mut XKeyEvent) -> xlib::KeySym {
+ unsafe { xlib::XLookupKeysym(ev, 0) }
+}
diff --git a/src/safe_x11/window.rs b/src/safe_x11/window.rs
new file mode 100644
index 0000000..624fea4
--- /dev/null
+++ b/src/safe_x11/window.rs
@@ -0,0 +1,144 @@
+use x11::xlib;
+use x11::xlib::{
+ Window,
+ XWindowAttributes,
+ XGetWindowAttributes,
+ XDefaultScreenOfDisplay,
+ XMoveWindow,
+
+ XSetInputFocus,
+ XSetWindowBorder,
+
+ IsViewable, RevertToParent, CurrentTime,
+};
+
+use std::ptr;
+use std::mem::uninitialized;
+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,
+}
+
+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<XWindow> {
+ if w != 0 {
+ Some(XWindow {
+ display: d,
+ inner: w,
+ })
+ } 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) };
+ }
+
+ pub fn set_boder_color<T: Into<Vec<u8>>>(&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 attrptr: *mut XWindowAttributes = uninitialized();
+ XGetWindowAttributes(self.display, self.inner, attrptr);
+ ptr::read(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);
+ println!("Moving window({:x}), {} + {} = {}, {} + {} = {}",
+ self.inner as u64,
+ attributes.x, xoffset, attributes.x + xoffset,
+ attributes.y, yoffset, attributes.y + yoffset);
+ 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(())
+ }
+ }
+
+ /// 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);
+ }
+ }
+
+ /// Raise the focus of the window.
+ pub fn focus(&self) {
+ let attrs = self.attributes();
+ if attrs.map_state == IsViewable {
+ unsafe {
+ XSetInputFocus(self.display, self.inner,
+ RevertToParent, CurrentTime)
+ };
+ }
+ }
+}