aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatias Linares <matiaslina@openmailbox.org>2015-11-29 11:57:19 -0300
committerMatias Linares <matiaslina@openmailbox.org>2015-11-29 11:57:48 -0300
commitd5e55aab6499a89e9dfaf5b976938bfe3e2f6aee (patch)
tree52efc28076c31fdd2522ed8b7871a7dd99ad0c87
parente1ec354306742a5804a20e2651cf49945cd17287 (diff)
downloaddotwm-d5e55aab6499a89e9dfaf5b976938bfe3e2f6aee.tar.gz
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.
-rwxr-xr-xautostart15
-rw-r--r--src/command.rs70
-rw-r--r--src/dotwm.rs112
-rw-r--r--src/safe_x11/mod.rs17
-rw-r--r--src/safe_x11/window.rs68
-rw-r--r--src/socket/parser.rs3
6 files changed, 276 insertions, 9 deletions
diff --git a/autostart b/autostart
index a0b4919..3957d67 100755
--- a/autostart
+++ b/autostart
@@ -2,21 +2,26 @@
netcat -U dotwm.sock <<EOF
bind Mod4 p exec dmenu_run
+bind Mod4 Return exec xterm
+bind Mod4 f fullscreen
+
bind Mod4 h move-win -10 0
bind Mod4 j move-win 0 10
bind Mod4 k move-win 0 -10
bind Mod4 l move-win 10 0
-bind Mod4 Return exec xterm
bind Mod4-Control h resize-win -10 0
bind Mod4-Control j resize-win 0 10
bind Mod4-Control k resize-win 0 -10
bind Mod4-Control l resize-win 10 0
-bind Mod4-Shift h move-win-to 0 0
-bind Mod4-Shift j move-win-to 100 100
-bind Mod4-Shift k move-win-to 200 200
-bind Mod4-Shift l move-win-to 0 1000
+bind Mod4-Shift h move-win-sticky left
+bind Mod4-Shift j move-win-sticky down
+bind Mod4-Shift k move-win-sticky up
+bind Mod4-Shift l move-win-sticky right
+
+bind Mod4-Shift c close-win
bind Mod4 Tab focus-next
+
EOF
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::<i32>().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::<i32>().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<XWindow>,
/// 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<WmAtom, Atom>,
+ /// Vec holding atoms for EWHM support
+ pub netatoms: HashMap<NetAtom, Atom>,
+ /// 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<i32> {
+ self.geometries().iter().map(|x| x.0).collect()
+ }
+
+ pub fn y_geometries(&self) -> Vec<i32> {
+ 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<T: Into<Vec<u8>>>(display: *mut Display, color: T) -> u64{
pub fn lookup_keysym(ev: &mut XKeyEvent) -> xlib::KeySym {
unsafe { xlib::XLookupKeysym(ev, 0) }
}
+
+pub fn atom<T: Into<Vec<u8>>>(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<T: Into<Vec<u8>>>(&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<ExecFn, &'static str> {
"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"),
}