From a42dfcb962a5c00ea0fcc73adcb3a456b2752594 Mon Sep 17 00:00:00 2001 From: noonebtw Date: Sun, 25 Apr 2021 01:03:33 +0200 Subject: [PATCH] lots of changes, added XLib (state struct for interfacing with xlib) --- src/clients.rs | 201 +++-- src/main.rs | 6 +- src/state.rs | 508 +++-------- src/wm.rs | 2236 ++++++++++++++++++++++++------------------------ src/xlib.rs | 468 ++++++++++ 5 files changed, 1866 insertions(+), 1553 deletions(-) create mode 100644 src/xlib.rs diff --git a/src/clients.rs b/src/clients.rs index a443f8e..9c60956 100644 --- a/src/clients.rs +++ b/src/clients.rs @@ -12,21 +12,36 @@ mod client { #[derive(Clone, Debug)] pub struct Client { - pub(super) window: Window, - pub(super) floating: bool, - pub(super) size: (i32, i32), - pub(super) position: (i32, i32), + pub(crate) window: Window, + pub(crate) size: (i32, i32), + pub(crate) position: (i32, i32), + } + + impl Default for Client { + fn default() -> Self { + Self { + window: 0, + size: (100, 100), + position: (0, 0), + } + } } impl Client { - pub fn new(window: Window, floating: bool, size: (i32, i32), position: (i32, i32)) -> Self { + pub fn new(window: Window, size: (i32, i32), position: (i32, i32)) -> Self { Self { window, - floating, size, position, } } + + pub fn new_default(window: Window) -> Self { + Self { + window, + ..Default::default() + } + } } impl Hash for Client { @@ -99,7 +114,7 @@ mod client { */ } -use client::*; +pub use client::*; #[cfg(test)] mod tests { @@ -113,23 +128,25 @@ mod tests { window: 1, size: (1, 1), position: (1, 1), - floating: false, }); clients.insert(Client { window: 2, size: (1, 1), position: (1, 1), - floating: false, }); - clients.arange_virtual_screen(600, 400, None); + clients.arrange_virtual_screen(600, 400, None); println!("{:#?}", clients); + clients + .iter_current_screen() + .for_each(|c| println!("{:?}", c)); + clients.remove(&1u64); - clients.arange_virtual_screen(600, 400, None); + clients.arrange_virtual_screen(600, 400, None); println!("{:#?}", clients); @@ -139,10 +156,9 @@ mod tests { window: 3, size: (1, 1), position: (1, 1), - floating: false, }); - clients.arange_virtual_screen(600, 400, None); + clients.arrange_virtual_screen(600, 400, None); println!("{:#?}", clients); @@ -150,7 +166,7 @@ mod tests { clients.rotate_left(); - clients.arange_virtual_screen(600, 400, None); + clients.arrange_virtual_screen(600, 400, None); println!("{:#?}", clients); } @@ -173,18 +189,48 @@ pub enum ClientEntry { Vacant, } +impl Into> for ClientEntry { + fn into(self) -> Option { + match self { + Self::Vacant => None, + Self::Tiled(client) | Self::Floating(client) => Some(client), + } + } +} + +impl ClientEntry { + pub fn into_option(self) -> Option { + self.into() + } + + pub fn unwrap(self) -> T { + self.into_option().unwrap() + } + + pub fn is_vacant(&self) -> bool { + match self { + ClientEntry::Vacant => true, + _ => false, + } + } + + pub fn is_occupied(&self) -> bool { + !self.is_vacant() + } +} + #[derive(Debug, Clone)] pub struct ClientState { - clients: Clients, - floating_clients: Clients, - virtual_screens: VecDeque, + pub(self) clients: Clients, + pub(self) floating_clients: Clients, + focused: Option, + pub(self) virtual_screens: VecDeque, } #[derive(Debug, Clone)] struct VirtualScreen { master: ClientRefs, aux: ClientRefs, - focused: Option, } impl Default for ClientState { @@ -195,6 +241,7 @@ impl Default for ClientState { Self { clients: Default::default(), floating_clients: Default::default(), + focused: None, virtual_screens: vss, } } @@ -215,7 +262,7 @@ impl ClientState { } } - pub fn insert(&mut self, client: Client) { + pub fn insert(&mut self, client: Client) -> Option<&Client> { let key = client.key(); self.clients.insert(key, client); @@ -223,6 +270,10 @@ impl ClientState { if let Some(vs) = self.virtual_screens.front_mut() { vs.aux.push(key); } + + self.focus_client(&key); + + self.clients.get(&key) } pub fn remove(&mut self, key: &K) @@ -235,6 +286,35 @@ impl ClientState { self.floating_clients.remove(&key.key()); } + pub fn contains(&self, key: &K) -> bool + where + K: ClientKey, + { + let key = key.key(); + + self.clients.contains_key(&key) || self.floating_clients.contains_key(&key) + } + + pub fn iter_floating(&self) -> impl Iterator { + self.floating_clients.iter() + } + + pub fn iter_hidden(&self) -> impl Iterator { + self.clients + .iter() + .filter(move |&(k, _)| !self.virtual_screens.front().unwrap().contains(k)) + } + + pub fn iter_visible(&self) -> impl Iterator { + self.iter_floating().chain(self.iter_current_screen()) + } + + pub fn iter_current_screen(&self) -> impl Iterator { + self.clients + .iter() + .filter(move |&(k, _)| self.virtual_screens.front().unwrap().contains(k)) + } + pub fn get(&self, key: &K) -> ClientEntry<&Client> where K: ClientKey, @@ -248,16 +328,11 @@ impl ClientState { } } - pub fn get_mut(&mut self, key: &K) -> ClientEntry<&mut Client> - where - K: ClientKey, - { - match self.clients.get_mut(&key.key()) { - Some(client) => ClientEntry::Tiled(client), - None => match self.floating_clients.get_mut(&key.key()) { - Some(client) => ClientEntry::Floating(client), - None => ClientEntry::Vacant, - }, + pub fn get_focused(&self) -> ClientEntry<&Client> { + if let Some(focused) = self.focused { + self.get(&focused) + } else { + ClientEntry::Vacant } } @@ -325,14 +400,56 @@ impl ClientState { ) } - /// focuses client `key` on current virtual screen - pub fn focus_client(&mut self, key: &K) + /** + focuses client `key` if it contains `key` and returns a reference to the newly and the previously + focused clients if any. + */ + pub fn focus_client(&mut self, key: &K) -> (ClientEntry<&Client>, ClientEntry<&Client>) where K: ClientKey, { - match self.virtual_screens.front_mut() { - Some(vs) => vs.focus(key), - None => {} + if self.contains(key) { + match self.focused { + // focus the new client and return reference to it and the previously focused client. + Some(focused) => { + self.focused = Some(key.key()); + (self.get(key), self.get(&focused)) + } + /* + not currently focusing any client + focus the new client and return reference to it. + */ + None => { + self.focused = Some(key.key()); + (self.get(key), ClientEntry::Vacant) + } + } + } else { + // key is not a reference to a valid client + (ClientEntry::Vacant, ClientEntry::Vacant) + } + } + + /** + sets `self.focused` to `None` and returns a reference to the previously focused Client if any. + */ + pub fn unfocus(&mut self) -> ClientEntry<&Client> { + match self.focused { + Some(focused) => { + self.focused = None; + self.get(&focused) + } + None => ClientEntry::Vacant, + } + } + + pub fn is_focused(&self, key: &K) -> bool + where + K: ClientKey, + { + match self.focused { + Some(focused) => focused == key.key(), + None => false, } } @@ -387,7 +504,7 @@ impl ClientState { screen width and screen height. Optionally adds a gap between windows `gap.unwrap_or(0)` pixels wide. */ - pub fn arange_virtual_screen(&mut self, width: i32, height: i32, gap: Option) { + pub fn arrange_virtual_screen(&mut self, width: i32, height: i32, gap: Option) { let gap = gap.unwrap_or(0); // should be fine to unwrap since we will always have at least 1 virtual screen @@ -444,7 +561,6 @@ impl Default for VirtualScreen { Self { master: Default::default(), aux: Default::default(), - focused: None, } } } @@ -465,12 +581,6 @@ impl VirtualScreen { self.master.retain(|k| *k != key); self.aux.retain(|k| *k != key); - if let Some(k) = self.focused { - if k == key { - self.focused = None; - } - } - self.refresh(); } @@ -483,11 +593,4 @@ impl VirtualScreen { self.master.extend(self.aux.drain(..1)); } } - - fn focus(&mut self, key: &K) - where - K: ClientKey, - { - self.focused = Some(key.key()); - } } diff --git a/src/main.rs b/src/main.rs index f443226..b4d59a4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,11 @@ use log::info; use std::io::Result; -//mod state; mod clients; +mod state; mod util; mod wm; +mod xlib; #[allow(dead_code)] unsafe extern "C" fn xlib_error_handler( @@ -33,6 +34,5 @@ fn main() -> Result<()> { info!("Hello, World!"); //wm::WMState::init().run(); - - Ok(()) + state::WindowManager::new().run(); } diff --git a/src/state.rs b/src/state.rs index 8b951c0..51ffaa1 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,417 +1,161 @@ -use std::{ - cell::RefCell, - collections::HashMap, - ffi::CString, - ptr::{null, null_mut}, - rc::{Rc, Weak}, -}; - -use x11::xlib::{ - self, Atom, ControlMask, LockMask, Mod1Mask, Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask, ShiftMask, - Window, XDefaultScreen, XEvent, XInternAtom, XOpenDisplay, XRootWindow, -}; -use xlib::GrabModeAsync; - use log::info; -use crate::util::BuildIdentityHasher; +use x11::xlib::{self, Window, XEvent}; -#[derive(Clone)] -pub struct Display(Rc<*mut x11::xlib::Display>); +use crate::{ + clients::{Client, ClientKey, ClientState}, + xlib::XLib, +}; -impl Display { - pub fn new(display: *mut x11::xlib::Display) -> Self { - Self { - 0: Rc::new(display), - } - } - - pub fn get(&self) -> *mut x11::xlib::Display { - *self.0 - } +pub struct WindowManager { + clients: ClientState, + xlib: XLib, } -#[derive(Clone, Debug)] -pub struct Client { - window: Window, - floating: bool, - size: (i32, i32), - position: (i32, i32), +pub enum Direction { + Left, + Right, } -impl Default for Client { - fn default() -> Self { - Self { - window: 0, - floating: false, - size: (0, 0), - position: (0, 0), - } - } -} +impl WindowManager { + pub fn new() -> Self { + let clients = ClientState::with_virtualscreens(3); + let xlib = XLib::new().init(); -impl Client { - pub fn new(window: xlib::Window) -> Self { - Self { - window, - ..Default::default() - } - } -} - -impl PartialEq for Client { - fn eq(&self, other: &Self) -> bool { - self.window == other.window - } -} - -impl Eq for Client {} - -#[derive(Clone, Debug)] -struct VirtualScreen { - master_stack: HashMap>, BuildIdentityHasher>, - aux_stack: HashMap>, BuildIdentityHasher>, - focused_client: Weak>, -} - -impl VirtualScreen { - fn new() -> Self { - Self { - master_stack: HashMap::default(), - aux_stack: HashMap::default(), - focused_client: Weak::new(), - } + Self { clients, xlib } } - fn contains_client(&self, client: Rc>) -> bool { - self.master_stack.contains_key(&client.borrow().window) - || self.aux_stack.contains_key(&client.borrow().window) - } -} + pub fn run(mut self) -> ! { + loop { + let event = self.xlib.next_event(); -struct XLibAtoms { - protocols: Atom, - delete_window: Atom, - active_window: Atom, - take_focus: Atom, -} - -impl XLibAtoms { - fn init(display: Display) -> Self { - unsafe { - Self { - protocols: { - let name = CString::new("WM_PROTOCOLS").unwrap(); - XInternAtom(display.get(), name.as_c_str().as_ptr(), 0) - }, - delete_window: { - let name = CString::new("WM_DELETE_WINDOW").unwrap(); - XInternAtom(display.get(), name.as_c_str().as_ptr(), 0) - }, - active_window: { - let name = CString::new("WM_ACTIVE_WINDOW").unwrap(); - XInternAtom(display.get(), name.as_c_str().as_ptr(), 0) - }, - take_focus: { - let name = CString::new("WM_TAKE_FOCUS").unwrap(); - XInternAtom(display.get(), name.as_c_str().as_ptr(), 0) - }, - } - } - } -} - -struct XLibState { - display: Display, - root: Window, - screen: i32, - // atoms - atoms: XLibAtoms, -} - -impl XLibState { - fn new() -> Self { - let (display, screen, root) = unsafe { - let display = XOpenDisplay(null()); - assert_ne!(display, null_mut()); - - let screen = XDefaultScreen(display); - let root = XRootWindow(display, screen); - - (Display::new(display), screen, root) - }; - - Self { - atoms: XLibAtoms::init(display.clone()), - display, - root, - screen, - } - } - - fn dpy(&self) -> *mut x11::xlib::Display { - self.display.get() - } - - fn root(&self) -> Window { - self.root - } - - fn screen(&self) -> i32 { - self.screen - } - - pub fn grab_key(&self, window: xlib::Window, keycode: i32, mod_mask: u32) { - let numlock_mask = self.get_numlock_mask(); - let modifiers = vec![0, LockMask, numlock_mask, LockMask | numlock_mask]; - for &modifier in modifiers.iter() { - unsafe { - xlib::XGrabKey( - self.dpy(), - keycode, - mod_mask | modifier, - window, - 1, /* true */ - GrabModeAsync, - GrabModeAsync, - ); + match event.get_type() { + xlib::MapRequest => self.map_request(&event), + xlib::UnmapNotify => self.unmap_notify(&event), + xlib::ConfigureRequest => self.configure_request(&event), + xlib::EnterNotify => self.enter_notify(&event), + xlib::DestroyNotify => self.destroy_notify(&event), + xlib::ButtonPress => self.destroy_notify(&event), + _ => {} } } } - pub fn grab_button(&self, window: xlib::Window, button: u32, mod_mask: u32, button_mask: i64) { - let numlock_mask = self.get_numlock_mask(); - let modifiers = vec![0, LockMask, numlock_mask, LockMask | numlock_mask]; - - modifiers.iter().for_each(|&modifier| { - unsafe { - xlib::XGrabButton( - self.dpy(), - button, - mod_mask | modifier, - window, - 1, /*true */ - button_mask as u32, - GrabModeAsync, - GrabModeAsync, - 0, - 0, - ); - } - }); - } - - pub fn keycode(&self, string: &str) -> i32 { - let c_string = CString::new(string).unwrap(); - unsafe { - let keysym = xlib::XStringToKeysym(c_string.as_ptr()); - xlib::XKeysymToKeycode(self.dpy(), keysym) as i32 + fn rotate_virtual_screen(&mut self, dir: Direction) { + match dir { + Direction::Left => self.clients.rotate_left(), + Direction::Right => self.clients.rotate_right(), } - } - - fn check_for_protocol(&self, window: xlib::Window, proto: xlib::Atom) -> bool { - let mut protos: *mut xlib::Atom = null_mut(); - let mut num_protos: i32 = 0; - - unsafe { - if xlib::XGetWMProtocols(self.dpy(), window, &mut protos, &mut num_protos) != 0 { - for i in 0..num_protos { - if *protos.offset(i as isize) == proto { - return true; - } - } - } - } - - return false; - } - - fn send_event(&self, window: xlib::Window, proto: Option) -> bool { - if proto.is_some() && self.check_for_protocol(window, proto.unwrap()) { - let mut data = xlib::ClientMessageData::default(); - data.set_long(0, proto.unwrap() as i64); - let mut event = XEvent { - client_message: xlib::XClientMessageEvent { - type_: xlib::ClientMessage, - serial: 0, - display: self.dpy(), - send_event: 0, - window, - format: 32, - message_type: self.atoms.protocols, - data, - }, - }; - - unsafe { - xlib::XSendEvent(self.dpy(), window, 0, xlib::NoEventMask, &mut event); - } - - return true; - } - - return false; - } - - fn get_numlock_mask(&self) -> u32 { - unsafe { - let modmap = xlib::XGetModifierMapping(self.dpy()); - let max_keypermod = (*modmap).max_keypermod; - - for i in 0..8 { - for j in 0..max_keypermod { - if *(*modmap) - .modifiermap - .offset((i * max_keypermod + j) as isize) - == xlib::XKeysymToKeycode(self.dpy(), x11::keysym::XK_Num_Lock as u64) - { - return 1 << i; - } - } - } - } - - 0 - } - - fn clean_mod_mask(&self) -> u32 { - !(self.get_numlock_mask() | LockMask) - & (ShiftMask | ControlMask | Mod1Mask | Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask) - } -} - -trait DisplayServer { - type Window; - - fn grab_key(&self, window: Self::Window, keycode: i32, mod_mask: u32); - fn grab_button(&self, window: Self::Window, keycode: i32, button_mask: u32, mod_mask: u32); -} - -pub struct WMState { - xlib_state: XLibState, - key_handlers: Vec<(i32, u32, Rc)>, - // (button, mod_mask, button_mask) - buttons: Vec<(u32, u32, i64)>, - event_handlers: Vec>, - - //move_window: - // u64 : window to move - // (i32, i32) : initial cursor position - // (i32, i32) : initial window position - move_window: Option<(u64, (i32, i32), (i32, i32))>, - //resize_window: - // u64 : window to move - // (i32, i32) : initial window position - resize_window: Option<(u64, (i32, i32))>, - clients: HashMap>>, - focused_client: Weak>, - current_vscreen: usize, - virtual_screens: Vec, -} - -impl WMState { - fn stack_unstacked_clients(&mut self) { - info!("[stack_unstacked_clients] "); - let current_vscreen = self.current_vscreen; self.clients - .iter() - .filter(|(w, c)| !c.borrow().floating && !self.is_client_stacked(w)) - .map(|(w, c)| (w.clone(), Rc::downgrade(c))) - .collect::>)>>() - .iter() - .for_each(|(w, c)| { - info!( - "[stack_unstacked_clients] inserting Window({:?}) into aux_stack", - w - ); + .iter_current_screen() + .for_each(|(_, c)| self.xlib.move_resize_client(c)); - self.virtual_screens[current_vscreen] - .aux_stack - .insert(w.clone(), c.clone()); - }); - } - - fn is_client_stacked(&self, window: &Window) -> bool { - self.virtual_screens - .iter() - .any(|vs| vs.contains_window(window)) - } - - fn client_for_window(&self, window: &Window) -> Option>> { self.clients - .iter() - .filter(|&(w, _)| *w == *window) - .next() - .map(|(_, c)| c.clone()) - } + .iter_hidden() + .for_each(|(_, c)| self.xlib.hide_client(c)); - fn switch_stack_for_client(&mut self, window: &Window) { - if let Some(client) = self.client_for_window(window) { - info!("[switch_stack_for_client] client: {:#?}", client.borrow()); - client.borrow_mut().floating = false; + // focus first client in all visible clients + let to_focus = self.clients.iter_visible().next().map(|(k, _)| k).cloned(); - if self.virtual_screens[self.current_vscreen] - .master_stack - .contains_key(window) - { - self.virtual_screens[self.current_vscreen] - .master_stack - .remove(window); - self.virtual_screens[self.current_vscreen] - .aux_stack - .insert(*window, Rc::downgrade(&client)); - info!("[switch_stack_for_client] moved to aux stack"); - } else { - self.virtual_screens[self.current_vscreen] - .aux_stack - .remove(window); - self.virtual_screens[self.current_vscreen] - .master_stack - .insert(*window, Rc::downgrade(&client)); - info!("[switch_stack_for_client] moved to master stack"); - } + if let Some(key) = to_focus { + self.focus_client(&key); } } - fn refresh_screen(&mut self) { - let current_vscreen = self.current_vscreen; + fn arrange_clients(&mut self) { + let (width, height) = self.xlib.dimensions(); + self.clients.arrange_virtual_screen(width, height, None); - self.virtual_screens - .get_mut(current_vscreen) - .and_then(|vs| { - vs.master_stack.retain(|_, c| { - c.upgrade().is_some() && !c.upgrade().unwrap().borrow().floating - }); - vs.aux_stack.retain(|_, c| { - c.upgrade().is_some() && !c.upgrade().unwrap().borrow().floating - }); + self.clients + .iter_current_screen() + .for_each(|(_, c)| self.xlib.move_resize_client(c)); + } - Some(()) - }); + fn focus_client(&mut self, key: &K) + where + K: ClientKey, + { + let (new, old) = self.clients.focus_client(key); - self.stack_unstacked_clients(); + if let Some(new) = new.into_option() { + self.xlib.focus_client(new); + } - if self.virtual_screens[current_vscreen] - .master_stack - .is_empty() - { - info!("[refresh_screen] master stack was empty, pushing first client if exists:"); + if let Some(old) = old.into_option() { + self.xlib.unfocus_client(old); + } + } - self.virtual_screens[current_vscreen] - .aux_stack - .iter() - .filter(|(_, c)| !c.upgrade().unwrap().borrow().floating) - .next() - .map(|(w, c)| (w.clone(), c.clone())) - .and_then(|(w, c)| { - info!("[arrange_clients] Window({:#?})", w); + fn unfocus_client(&mut self) { + if let Some(client) = self.clients.unfocus().into_option() { + self.xlib.unfocus_client(client); + } + } - self.virtual_screens[current_vscreen] - .master_stack - .insert(w, c); - self.virtual_screens[current_vscreen].aux_stack.remove(&w) - }); + fn new_client(&mut self, window: Window) { + self.clients.insert(Client::new_default(window)).unwrap(); + self.xlib.map_window(window); + + self.focus_client(&window); + + self.arrange_clients(); + } + + fn map_request(&mut self, event: &XEvent) { + let event = unsafe { &event.map_request }; + + info!("MapRequest: {:?}", event); + + if !self.clients.contains(&event.window) { + info!("MapRequest: new client"); + + self.new_client(event.window); + } + } + + fn unmap_notify(&mut self, event: &XEvent) { + let event = unsafe { &event.unmap }; + info!("UnmapNotify: {:?}", event); + + self.clients.remove(&event.window); + + self.arrange_clients(); + } + + fn destroy_notify(&mut self, event: &XEvent) { + let event = unsafe { &event.destroy_window }; + info!("DestroyNotify: {:?}", event); + + self.clients.remove(&event.window); + + self.arrange_clients(); + } + + fn configure_request(&mut self, event: &XEvent) { + let event = unsafe { &event.configure_request }; + info!("ConfigureRequest: {:?}", event); + + match self.clients.get(&event.window).into_option() { + Some(client) => self.xlib.configure_client(client), + None => self.xlib.configure_window(event), + } + } + + fn enter_notify(&mut self, event: &XEvent) { + let event = unsafe { &event.crossing }; + info!("EnterNotify: {:?}", event); + + self.focus_client(&event.window); + } + + fn button_notify(&mut self, event: &XEvent) { + let event = unsafe { &event.button }; + info!("EnterNotify: {:?}", event); + + self.focus_client(&event.subwindow); + if let Some(client) = self.clients.get(&event.subwindow).into_option() { + self.xlib.raise_client(client); } } } diff --git a/src/wm.rs b/src/wm.rs index 3fd4e40..dfeaf13 100644 --- a/src/wm.rs +++ b/src/wm.rs @@ -1,1119 +1,1117 @@ -/* -use std::{ - borrow::Borrow, - cell::RefCell, - collections::HashSet, - ffi::CString, - hash::{Hash, Hasher}, - io::{Error, ErrorKind, Result}, - ops::Deref, - ptr::{null, null_mut}, - rc::{Rc, Weak}, -}; - -use x11::{ - xlib, - xlib::{ - Atom, ButtonPressMask, ButtonReleaseMask, CWEventMask, ControlMask, EnterWindowMask, - FocusChangeMask, GrabModeAsync, LockMask, Mod1Mask, Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask, - PointerMotionMask, PropertyChangeMask, ShiftMask, StructureNotifyMask, - SubstructureNotifyMask, SubstructureRedirectMask, Window, XDefaultScreen, XEvent, - XInternAtom, XOpenDisplay, XRootWindow, - }, -}; - -use log::info; - -use nix::unistd::{close, execvp, fork, setsid, ForkResult}; - -#[derive(Clone)] -pub struct Display(Rc<*mut x11::xlib::Display>); - -impl Display { - pub fn new(display: *mut x11::xlib::Display) -> Self { - Self { - 0: Rc::new(display), - } - } - - pub fn get(&self) -> *mut x11::xlib::Display { - *self.0 - } -} - -pub struct WMAtoms { - protocols: Atom, - delete_window: Atom, - active_window: Atom, - take_focus: Atom, -} - -impl WMAtoms { - fn init(display: Display) -> Self { - unsafe { - Self { - protocols: { - let name = CString::new("WM_PROTOCOLS").unwrap(); - XInternAtom(display.get(), name.as_c_str().as_ptr(), 0) - }, - delete_window: { - let name = CString::new("WM_DELETE_WINDOW").unwrap(); - XInternAtom(display.get(), name.as_c_str().as_ptr(), 0) - }, - active_window: { - let name = CString::new("WM_ACTIVE_WINDOW").unwrap(); - XInternAtom(display.get(), name.as_c_str().as_ptr(), 0) - }, - take_focus: { - let name = CString::new("WM_TAKE_FOCUS").unwrap(); - XInternAtom(display.get(), name.as_c_str().as_ptr(), 0) - }, - } - } - } -} - -use crate::util::BuildIdentityHasher; -use weak_table::WeakHashSet; - -#[derive(Clone, Debug)] -struct VirtualScreen { - master_stack: WeakHashSet, BuildIdentityHasher>, - aux_stack: WeakHashSet, BuildIdentityHasher>, - focused_client: Weak, -} - -impl VirtualScreen { - fn new() -> Self { - Self { - master_stack: Default::default(), - aux_stack: Default::default(), - focused_client: Weak::new(), - } - } - - fn contains_client(&self, client: &Rc) -> bool { - self.master_stack.contains(client.as_ref()) || self.aux_stack.contains(client.as_ref()) - } -} - -pub struct XLibState { - display: Display, - root: Window, - screen: i32, - pub atoms: WMAtoms, -} - -impl XLibState { - pub fn new() -> Self { - let (display, screen, root) = unsafe { - let display = XOpenDisplay(null()); - assert_ne!(display, null_mut()); - - let display = Display::new(display); - let screen = XDefaultScreen(display.get()); - let root = XRootWindow(display.get(), screen); - - (display, screen, root) - }; - - Self { - atoms: WMAtoms::init(display.clone()), - display, - root, - screen, - } - } - - pub fn dpy(&self) -> *mut x11::xlib::Display { - self.display.get() - } - - pub fn root(&self) -> Window { - self.root - } - - pub fn screen(&self) -> i32 { - self.screen - } - - pub fn grab_key(&self, window: xlib::Window, keycode: i32, mod_mask: u32) { - let numlock_mask = self.numlock_mask(); - let modifiers = vec![0, LockMask, numlock_mask, LockMask | numlock_mask]; - for &modifier in modifiers.iter() { - unsafe { - xlib::XGrabKey( - self.dpy(), - keycode, - mod_mask | modifier, - window, - 1, /* true */ - GrabModeAsync, - GrabModeAsync, - ); - } - } - } - - pub fn grab_button(&self, window: xlib::Window, button: u32, mod_mask: u32, button_mask: i64) { - let numlock_mask = self.numlock_mask(); - let modifiers = vec![0, LockMask, numlock_mask, LockMask | numlock_mask]; - - modifiers.iter().for_each(|&modifier| { - unsafe { - xlib::XGrabButton( - self.dpy(), - button, - mod_mask | modifier, - window, - 1, /*true */ - button_mask as u32, - GrabModeAsync, - GrabModeAsync, - 0, - 0, - ); - } - }); - } - - pub fn keycode(&self, string: S) -> i32 - where - S: Into, - { - let c_string = CString::new(string.into()).unwrap(); - unsafe { - let keysym = xlib::XStringToKeysym(c_string.as_ptr()); - xlib::XKeysymToKeycode(self.dpy(), keysym) as i32 - } - } - - fn check_for_protocol(&self, window: xlib::Window, proto: xlib::Atom) -> bool { - let mut protos: *mut xlib::Atom = null_mut(); - let mut num_protos: i32 = 0; - - unsafe { - if xlib::XGetWMProtocols(self.dpy(), window, &mut protos, &mut num_protos) != 0 { - for i in 0..num_protos { - if *protos.offset(i as isize) == proto { - return true; - } - } - } - } - - return false; - } - - fn send_event(&self, window: xlib::Window, proto: xlib::Atom) -> bool { - if self.check_for_protocol(window, proto) { - let mut data = xlib::ClientMessageData::default(); - data.set_long(0, proto as i64); - - let mut event = XEvent { - client_message: xlib::XClientMessageEvent { - type_: xlib::ClientMessage, - serial: 0, - display: self.dpy(), - send_event: 0, - window, - format: 32, - message_type: self.atoms.protocols, - data, - }, - }; - - unsafe { - xlib::XSendEvent(self.dpy(), window, 0, xlib::NoEventMask, &mut event); - } - - true - } else { - false - } - } - - fn numlock_mask(&self) -> u32 { - unsafe { - let modmap = xlib::XGetModifierMapping(self.dpy()); - let max_keypermod = (*modmap).max_keypermod; - - for i in 0..8 { - for j in 0..max_keypermod { - if *(*modmap) - .modifiermap - .offset((i * max_keypermod + j) as isize) - == xlib::XKeysymToKeycode(self.dpy(), x11::keysym::XK_Num_Lock as u64) - { - return 1 << i; - } - } - } - } - - 0 - } - - fn clean_mask(&self) -> u32 { - !(self.numlock_mask() | LockMask) - & (ShiftMask | ControlMask | Mod1Mask | Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask) - } -} - -struct Key { - keycode: i32, - mod_mask: u32, -} - -struct Button { - button: i32, - mod_mask: u32, - button_mask: u64, -} - -enum KeyOrButton { - Key(Key), - Button(Button), -} - -pub struct WMState { - xlib_state: XLibState, - key_handlers: Vec<(i32, u32, Rc)>, - // (button, mod_mask, button_mask) - buttons: Vec<(u32, u32, i64)>, - event_handlers: Vec>, - - // MutState: - - //move_window: - // u64 : window to move - // (i32, i32) : initial cursor position - // (i32, i32) : initial window position - move_window: Option<(u64, (i32, i32), (i32, i32))>, - //resize_window: - // u64 : window to move - // (i32, i32) : initial window position - resize_window: Option<(u64, (i32, i32))>, - clients: HashSet, BuildIdentityHasher>, - focused_client: Weak, - current_vscreen: usize, - virtual_screens: Vec, -} - -impl WMState { - pub fn new() -> Self { - Self { - xlib_state: XLibState::new(), - key_handlers: vec![], - event_handlers: vec![], - buttons: vec![], - move_window: None, - resize_window: None, - clients: Default::default(), - focused_client: Weak::new(), - current_vscreen: 0, - virtual_screens: vec![VirtualScreen::new()], - } - } - - pub fn init() -> Self { - let state = Self::new() - .grab_button( - 1, - Mod1Mask, - ButtonPressMask | ButtonReleaseMask | PointerMotionMask, - ) - .grab_button( - 2, - Mod1Mask, - ButtonPressMask | ButtonReleaseMask | PointerMotionMask, - ) - .grab_button( - 3, - Mod1Mask, - ButtonPressMask | ButtonReleaseMask | PointerMotionMask, - ) - .add_event_handler(Self::handle_toggle_floating) - .add_event_handler(Self::handle_move_window) - .add_event_handler(Self::handle_resize_window) - .add_key_handler("T", Mod1Mask, |state, _| { - println!("spawning terminal"); - let _ = state.spawn("xterm", &[]); - }) - .add_key_handler("Left", Mod1Mask, |state, _| { - state.handle_change_virtual_screen(-1); - }) - .add_key_handler("Right", Mod1Mask, |state, _| { - state.handle_change_virtual_screen(1); - }) - .add_key_handler("M", Mod1Mask, Self::handle_switch_stack) - .add_key_handler("Q", Mod1Mask, |state, event| unsafe { - if event.key.subwindow != 0 { - if !state - .xlib_state - .send_event(event.key.subwindow, state.xlib_state.atoms.delete_window) - { - xlib::XKillClient(state.dpy(), event.key.subwindow); - } - } - }) - .add_key_handler("Q", Mod1Mask | ShiftMask, |state, _event| { - unsafe { - xlib::XCloseDisplay(state.dpy()); - } - - std::process::exit(0); - }); - - unsafe { - let mut wa: xlib::XSetWindowAttributes = std::mem::MaybeUninit::zeroed().assume_init(); - wa.event_mask = SubstructureRedirectMask - | StructureNotifyMask - | SubstructureNotifyMask - | EnterWindowMask - | PointerMotionMask - | ButtonPressMask; - - xlib::XChangeWindowAttributes(state.dpy(), state.root(), CWEventMask, &mut wa); - - xlib::XSelectInput(state.dpy(), state.root(), wa.event_mask); - } - - state - } - - // MutState Functions: - - fn stack_unstacked_clients(&mut self) { - info!("[stack_unstacked_clients] "); - - let unstacked_clients = self - .clients - .iter() - .filter(|&c| !c.floating && !self.is_client_stacked(c)) - .cloned() - .collect::>(); - - unstacked_clients.iter().for_each(|c| { - info!( - "[stack_unstacked_clients] inserting Client({:?}) into aux_stack", - c - ); - - self.virtual_screens[self.current_vscreen] - .aux_stack - .insert(c.clone()); - }); - } - - fn is_client_stacked(&self, client: &Rc) -> bool { - self.virtual_screens - .iter() - .any(|vs| vs.contains_client(client)) - } - - fn client_for_window(&self, window: Window) -> Option> { - self.clients - .iter() - .filter(|&c| c.window == window) - .next() - .cloned() - } - - fn switch_stack_for_client(&mut self, client: Rc) { - info!("[switch_stack_for_client] client: {:#?}", client); - - if self.virtual_screens[self.current_vscreen] - .master_stack - .contains(client.as_ref()) - { - self.virtual_screens[self.current_vscreen] - .master_stack - .remove(client.as_ref()); - self.virtual_screens[self.current_vscreen] - .aux_stack - .insert(client.clone()); - info!("[switch_stack_for_client] moved to aux stack"); - } else { - self.virtual_screens[self.current_vscreen] - .aux_stack - .remove(client.as_ref()); - self.virtual_screens[self.current_vscreen] - .master_stack - .insert(client.clone()); - info!("[switch_stack_for_client] moved to master stack"); - } - - self.clients.replace(Client::new_rc(InnerClient { - floating: false, - ..**client.as_ref() - })); - } - - fn refresh_screen(&mut self) { - let current_vscreen = self.current_vscreen; - - self.stack_unstacked_clients(); - - if let Some(vs) = self.virtual_screens.get_mut(self.current_vscreen) { - vs.master_stack.retain(|c| !c.0.borrow().floating); - vs.aux_stack.retain(|c| !c.floating); - - if vs.master_stack.is_empty() { - info!("[refresh_screen] master stack was empty, pushing first client if exists:"); - vs.aux_stack.iter().filter(|c| !c.floating).next().map(|c| { - info!("[arrange_clients] Client({:#?})", c); - - self.virtual_screens[current_vscreen] - .master_stack - .insert(c.clone()); - self.virtual_screens[current_vscreen] - .aux_stack - .remove(c.as_ref()); - }); - } - } - } - - pub fn run(mut self) -> Self { - let event_handlers = self.event_handlers.clone(); - let key_handlers = self.key_handlers.clone(); - - loop { - let event = unsafe { - let mut event: xlib::XEvent = std::mem::MaybeUninit::zeroed().assume_init(); - xlib::XNextEvent(self.dpy(), &mut event); - - event - }; - - for handler in event_handlers.iter() { - handler(&mut self, &event); - } - - match event.get_type() { - xlib::MapRequest => { - self.map_request(unsafe { &event.map_request }); - } - xlib::UnmapNotify => { - self.unmap_notify(unsafe { &event.unmap }); - } - xlib::DestroyNotify => { - self.destroy_notify(unsafe { &event.destroy_window }); - } - xlib::ConfigureRequest => { - self.configure_request(unsafe { &event.configure_request }); - } - xlib::EnterNotify => { - self.enter_notify(unsafe { &event.crossing }); - } - xlib::ButtonPress => { - self.button_press(unsafe { &event.button }); - } - xlib::KeyPress => { - let clean_mask = self.xlib_state.clean_mask(); - - key_handlers.iter().for_each(|(key, mask, handler)| { - if unsafe { - event.key.keycode == *key as u32 - && event.key.state & clean_mask == *mask & clean_mask - } { - handler(&mut self, &event); - } - }) - } - _ => {} - } - } - } - - pub fn dpy(&self) -> *mut xlib::Display { - self.xlib_state.dpy() - } - - pub fn root(&self) -> xlib::Window { - self.xlib_state.root() - } - - pub fn grab_button(mut self, button: u32, mod_mask: u32, button_mask: i64) -> Self { - self.buttons.push((button, mod_mask, button_mask)); - self.xlib_state - .grab_button(self.root(), button, mod_mask, button_mask); - - self - } - - #[allow(dead_code)] - pub fn add_event_handler(mut self, handler: F) -> Self - where - F: Fn(&mut Self, &XEvent) + 'static, - { - self.event_handlers.push(Rc::new(handler)); - - self - } - - pub fn add_key_handler(mut self, key: S, mask: u32, handler: F) -> Self - where - S: Into, - F: Fn(&mut Self, &XEvent) + 'static, - { - let keycode = self.xlib_state.keycode(key); - - self.key_handlers.push((keycode, mask, Rc::new(handler))); - self.xlib_state.grab_key(self.root(), keycode, mask); - - self - } - - fn map_request(&mut self, event: &xlib::XMapRequestEvent) { - info!("[MapRequest] event: {:#?}", event); - - if self.client_for_window(event.window).is_none() { - info!("[MapRequest] new client: {:#?}", event.window); - let client = Rc::new(Client::new(event.window)); - self.clients.insert(client.clone()); - - unsafe { - xlib::XMapWindow(self.dpy(), client.window); - - xlib::XSelectInput( - self.dpy(), - event.window, - EnterWindowMask | FocusChangeMask | PropertyChangeMask | StructureNotifyMask, - ); - } - - self.buttons - .iter() - .for_each(|&(button, mod_mask, button_mask)| { - self.xlib_state - .grab_button(client.window, button, mod_mask, button_mask); - }); - - self.arrange_clients(); - self.focus_client(&client); - } - } - - fn unmap_notify(&mut self, event: &xlib::XUnmapEvent) { - info!("[UnmapNotify] event: {:#?}", event); - - if event.send_event == 0 { - if let Some(client) = self.client_for_window(event.window) { - self.clients.remove(&client); - info!("[UnmapNotify] removing client: {:#?}", client); - } - } - - self.arrange_clients(); - } - - fn destroy_notify(&mut self, event: &xlib::XDestroyWindowEvent) { - info!("[DestroyNotify] event: {:?}", event); - - if let Some(client) = self.client_for_window(event.window) { - self.clients.remove(&client); - - info!("[DestroyNotify] removed entry: {:?}", client); - } - - self.arrange_clients(); - } - - fn configure_request(&mut self, event: &xlib::XConfigureRequestEvent) { - info!("[ConfigureRequest] event: {:?}", event); - - match self.client_for_window(event.window) { - Some(client) => { - info!("[ConfigureRequest] found Client {:#?}", client,); - - self.configure_client(&client); - } - _ => { - info!( - "[ConfigureRequest] no client found for Window({:?}), calling XConfigureWindow()", - event.window); - - let mut wc = xlib::XWindowChanges { - x: event.x, - y: event.y, - width: event.width, - height: event.height, - border_width: event.border_width, - sibling: event.above, - stack_mode: event.detail, - }; - - unsafe { - xlib::XConfigureWindow( - self.dpy(), - event.window, - event.value_mask as u32, - &mut wc, - ); - } - } - } - } - - fn enter_notify(&mut self, event: &xlib::XCrossingEvent) { - info!("[EnterNotify] event: {:?}", event); - - if let Some(client) = self.client_for_window(event.window) { - info!("[EnterNotify] focusing Client ({:?})", client); - self.focus_client(&client); - } - } - - fn button_press(&mut self, event: &xlib::XButtonEvent) { - info!("[ButtonPress] event: {:?}", event); - - if let Some(client) = self.client_for_window(event.subwindow) { - info!("[ButtonPress] focusing Client ({:?})", client); - self.focus_client(&client); - - info!("[ButtonPress] raising Window({:?})", event.window); - - unsafe { - xlib::XRaiseWindow(self.dpy(), client.window); - xlib::XSync(self.dpy(), 0); - } - } - } - - fn unfocus_client(&self, client: &Rc) { - unsafe { - xlib::XSetInputFocus( - self.dpy(), - client.window, - xlib::RevertToPointerRoot, - xlib::CurrentTime, - ); - - xlib::XDeleteProperty(self.dpy(), self.root(), self.xlib_state.atoms.active_window); - } - } - - fn focus_client(&mut self, client: &Rc) { - if let Some(focused_client) = self.focused_client.upgrade() { - self.unfocus_client(&focused_client); - } - - self.focused_client = Rc::downgrade(client); - - self.virtual_screens[self.current_vscreen].focused_client = self.focused_client.clone(); - - unsafe { - xlib::XSetInputFocus( - self.dpy(), - client.window, - xlib::RevertToPointerRoot, - xlib::CurrentTime, - ); - - xlib::XChangeProperty( - self.dpy(), - self.root(), - self.xlib_state.atoms.active_window, - xlib::XA_WINDOW, - 32, - xlib::PropModeReplace, - &client.window as *const u64 as *const _, - 1, - ); - } - - self.xlib_state - .send_event(client.window, self.xlib_state.atoms.take_focus); - } - - fn arrange_clients(&self) { - let (screen_w, screen_h) = unsafe { - ( - xlib::XDisplayWidth(self.dpy(), self.xlib_state.screen()), - xlib::XDisplayHeight(self.dpy(), self.xlib_state.screen()), - ) - }; - - if !self.clients.is_empty() { - info!("[arrange_clients] refreshing screen"); - self.refresh_screen(); - - let window_w = { - if self.virtual_screens[self.current_vscreen] - .aux_stack - .is_empty() - { - screen_w - } else { - screen_w / 2 - } - }; - - if let Some(vc) = self.virtual_screens.get_mut(self.current_vscreen) { - vc.master_stack - .iter() - .zip(std::iter::repeat(vc.master_stack.len())) - .enumerate() - .chain( - vc.aux_stack - .iter() - .zip(std::iter::repeat(vc.aux_stack.len())) - .enumerate(), - ) - .for_each(|(i, (client, length))| { - let (mut wc, size, position) = { - let window_h = screen_h / length as i32; - - let size = (window_w, window_h); - let position = (0, window_h * i as i32); - - ( - xlib::XWindowChanges { - x: position.0, - y: position.1, - width: size.0, - height: size.1, - border_width: 0, - sibling: 0, - stack_mode: 0, - }, - size, - position, - ) - }; - - unsafe { - xlib::XConfigureWindow( - self.dpy(), - client.window, - (xlib::CWY | xlib::CWX | xlib::CWHeight | xlib::CWWidth) as u32, - &mut wc, - ); - - self.clients.replace(Client::new_rc(InnerClient { - size, - position, - ..**client - })); - - self.configure_client(&client); - - xlib::XSync(self.dpy(), 0); - } - }); - } - } - } - - fn configure_client(&self, client: &Rc) { - let mut event = { - xlib::XConfigureEvent { - type_: xlib::ConfigureNotify, - display: self.dpy(), - event: client.window, - window: client.window, - x: client.position.0, - y: client.position.1, - width: client.size.0, - height: client.size.1, - border_width: 0, - override_redirect: 0, - send_event: 0, - serial: 0, - above: 0, - } - }; - - unsafe { - xlib::XSendEvent( - self.dpy(), - event.window, - 0, - StructureNotifyMask, - &mut event as *mut _ as *mut XEvent, - ); - } - } - - fn handle_change_virtual_screen(&mut self, direction: i32) { - assert!(direction == 1 || direction == -1); - - // hide all windows from current virtual screen - self.virtual_screens[self.current_vscreen] - .master_stack - .iter() - .chain(self.virtual_screens[self.current_vscreen].aux_stack.iter()) - .for_each(|c| unsafe { - xlib::XMoveWindow(self.dpy(), c.window, c.size.0 * -2, c.position.1); - }); - - // change current_vscreen variable - let mut new_vscreen = self.current_vscreen as isize + direction as isize; - - if new_vscreen >= self.virtual_screens.len() as isize { - self.virtual_screens.push(VirtualScreen::new()); - } else if new_vscreen < 0 { - new_vscreen = self.virtual_screens.len() as isize - 1; - } - - self.current_vscreen = new_vscreen as usize; - - self.arrange_clients(); - - // focus the focused cliend of the new virtual screen - if let Some(client) = self.virtual_screens[self.current_vscreen] - .focused_client - .upgrade() - { - self.focus_client(&client); - } - } - - fn handle_switch_stack(&mut self, event: &XEvent) { - if let Some(client) = self.client_for_window(event.button.subwindow) { - self.switch_stack_for_client(client); - } - - self.arrange_clients(); - } - - fn handle_toggle_floating(&mut self, event: &XEvent) { - if event.get_type() == xlib::ButtonPress { - let event = unsafe { event.button }; - let clean_mask = self.xlib_state.clean_mask(); - - if event.button == 2 - && event.state & clean_mask == Mod1Mask & clean_mask - && event.subwindow != 0 - { - if let Some(client) = self.client_for_window(event.subwindow) { - info!( - "[handle_toggle_floating] {:#?} floating -> {:?}", - client, !client.floating - ); - - self.clients.replace(Client::new_rc(InnerClient { - floating: !client.floating, - ..**client - })); - } - - self.arrange_clients(); - } - } - } - - fn handle_move_window(&mut self, event: &XEvent) { - let clean_mask = self.xlib_state.clean_mask(); - - if unsafe { - self.move_window.is_none() - && event.get_type() == xlib::ButtonPress - && event.button.button == 1 - && event.button.state & clean_mask == Mod1Mask & clean_mask - && event.button.subwindow != 0 - } { - let win_pos = unsafe { - let mut attr: xlib::XWindowAttributes = - std::mem::MaybeUninit::zeroed().assume_init(); - xlib::XGetWindowAttributes(self.dpy(), event.button.subwindow, &mut attr); - - (attr.x, attr.y) - }; - - self.move_window = Some(unsafe { - ( - event.button.subwindow, - (event.button.x, event.button.y), - win_pos, - ) - }); - - if let Some(client) = self.client_for_window(event.button.subwindow) { - self.clients.replace(Client::new_rc(InnerClient { - floating: true, - ..**client - })); - } - - self.arrange_clients(); - } else if unsafe { - self.move_window.is_some() - && event.get_type() == xlib::ButtonRelease - && event.button.button == 1 - } { - self.move_window = None; - } else if self.move_window.is_some() && event.get_type() == xlib::MotionNotify { - let move_window = self.move_window.unwrap(); - - let attr = unsafe { - let mut attr: xlib::XWindowAttributes = - std::mem::MaybeUninit::zeroed().assume_init(); - xlib::XGetWindowAttributes(self.dpy(), move_window.0, &mut attr); - - attr - }; - - let (x, y) = unsafe { - ( - event.motion.x - move_window.1 .0 + move_window.2 .0, - event.motion.y - move_window.1 .1 + move_window.2 .1, - ) - }; - - let mut wc = xlib::XWindowChanges { - x, - y, - width: attr.width, - height: attr.height, - border_width: 0, - sibling: 0, - stack_mode: 0, - }; - - unsafe { - xlib::XConfigureWindow( - self.dpy(), - move_window.0, - (xlib::CWX | xlib::CWY) as u32, - &mut wc, - ); - - xlib::XSync(self.dpy(), 0); - } - } - } - - fn handle_resize_window(&mut self, event: &XEvent) { - let clean_mask = self.xlib_state.clean_mask(); - - let resize_window = self.resize_window; - - if unsafe { - resize_window.is_none() - && event.get_type() == xlib::ButtonPress - && event.button.button == 3 - && event.button.state & clean_mask == Mod1Mask & clean_mask - && event.button.subwindow != 0 - } { - unsafe { - let mut attr: xlib::XWindowAttributes = - std::mem::MaybeUninit::zeroed().assume_init(); - - xlib::XGetWindowAttributes(self.dpy(), event.button.subwindow, &mut attr); - - self.resize_window = Some((event.button.subwindow, (attr.x, attr.y))); - - xlib::XWarpPointer( - self.dpy(), - 0, - event.button.subwindow, - 0, - 0, - 0, - 0, - attr.width + attr.border_width - 1, - attr.height + attr.border_width - 1, - ); - }; - - if let Some(client) = self.client_for_window(unsafe { event.button.subwindow }) { - self.clients.replace(Client::new_rc(InnerClient { - floating: true, - ..**client - })); - } - - self.arrange_clients(); - } else if unsafe { - resize_window.is_some() - && event.get_type() == xlib::ButtonRelease - && event.button.button == 3 - } { - self.resize_window = None; - } else if resize_window.is_some() && event.get_type() == xlib::MotionNotify { - let resize_window = resize_window.unwrap(); - - let attr = unsafe { - let mut attr: xlib::XWindowAttributes = - std::mem::MaybeUninit::zeroed().assume_init(); - xlib::XGetWindowAttributes(self.dpy(), resize_window.0, &mut attr); - - attr - }; - - unsafe { - let (width, height) = { - ( - std::cmp::max( - 1, - event.motion.x - resize_window.1 .0 + 2 * attr.border_width + 1, - ), - std::cmp::max( - 1, - event.motion.y - resize_window.1 .1 + 2 * attr.border_width + 1, - ), - ) - }; - - let mut wc = xlib::XWindowChanges { - x: attr.x, - y: attr.y, - width, - height, - border_width: attr.border_width, - sibling: 0, - stack_mode: 0, - }; - - xlib::XConfigureWindow( - self.dpy(), - resize_window.0, - (xlib::CWWidth | xlib::CWHeight) as u32, - &mut wc, - ); - - xlib::XSync(self.dpy(), 0); - } - } - } - - // spawn a new process / calls execvp - pub fn spawn(&self, command: T, args: &[T]) -> Result<()> { - let fd = unsafe { xlib::XConnectionNumber(self.dpy()) }; - - match unsafe { fork() } { - Ok(ForkResult::Parent { .. }) => Ok(()), - Ok(ForkResult::Child) => { - // i dont think i want to exit this block without closing the program, - // so unwrap everything - - close(fd) - .or_else(|_| Err("failed to close x connection")) - .unwrap(); - setsid().ok().ok_or("failed to setsid").unwrap(); - - let c_cmd = CString::new(command.to_string()).unwrap(); - - let c_args: Vec<_> = args - .iter() - .map(|s| CString::new(s.to_string()).unwrap()) - .collect(); - - execvp( - &c_cmd, - &c_args.iter().map(|s| s.as_c_str()).collect::>(), - ) - .or(Err("failed to execvp()")) - .unwrap(); - - eprintln!("execvp({}) failed.", c_cmd.to_str().unwrap()); - std::process::exit(0); - } - Err(_) => Err(Error::new(ErrorKind::Other, "failed to fork.")), - } - } -} -*/ +// use std::{ +// borrow::Borrow, +// cell::RefCell, +// collections::HashSet, +// ffi::CString, +// hash::{Hash, Hasher}, +// io::{Error, ErrorKind, Result}, +// ops::Deref, +// ptr::{null, null_mut}, +// rc::{Rc, Weak}, +// }; +// +// use x11::{ +// xlib, +// xlib::{ +// Atom, ButtonPressMask, ButtonReleaseMask, CWEventMask, ControlMask, EnterWindowMask, +// FocusChangeMask, GrabModeAsync, LockMask, Mod1Mask, Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask, +// PointerMotionMask, PropertyChangeMask, ShiftMask, StructureNotifyMask, +// SubstructureNotifyMask, SubstructureRedirectMask, Window, XDefaultScreen, XEvent, +// XInternAtom, XOpenDisplay, XRootWindow, +// }, +// }; +// +// use log::info; +// +// use nix::unistd::{close, execvp, fork, setsid, ForkResult}; +// +// #[derive(Clone)] +// pub struct Display(Rc<*mut x11::xlib::Display>); +// +// impl Display { +// pub fn new(display: *mut x11::xlib::Display) -> Self { +// Self { +// 0: Rc::new(display), +// } +// } +// +// pub fn get(&self) -> *mut x11::xlib::Display { +// *self.0 +// } +// } +// +// pub struct WMAtoms { +// protocols: Atom, +// delete_window: Atom, +// active_window: Atom, +// take_focus: Atom, +// } +// +// impl WMAtoms { +// fn init(display: Display) -> Self { +// unsafe { +// Self { +// protocols: { +// let name = CString::new("WM_PROTOCOLS").unwrap(); +// XInternAtom(display.get(), name.as_c_str().as_ptr(), 0) +// }, +// delete_window: { +// let name = CString::new("WM_DELETE_WINDOW").unwrap(); +// XInternAtom(display.get(), name.as_c_str().as_ptr(), 0) +// }, +// active_window: { +// let name = CString::new("WM_ACTIVE_WINDOW").unwrap(); +// XInternAtom(display.get(), name.as_c_str().as_ptr(), 0) +// }, +// take_focus: { +// let name = CString::new("WM_TAKE_FOCUS").unwrap(); +// XInternAtom(display.get(), name.as_c_str().as_ptr(), 0) +// }, +// } +// } +// } +// } +// +// use crate::util::BuildIdentityHasher; +// use weak_table::WeakHashSet; +// +// #[derive(Clone, Debug)] +// struct VirtualScreen { +// master_stack: WeakHashSet, BuildIdentityHasher>, +// aux_stack: WeakHashSet, BuildIdentityHasher>, +// focused_client: Weak, +// } +// +// impl VirtualScreen { +// fn new() -> Self { +// Self { +// master_stack: Default::default(), +// aux_stack: Default::default(), +// focused_client: Weak::new(), +// } +// } +// +// fn contains_client(&self, client: &Rc) -> bool { +// self.master_stack.contains(client.as_ref()) || self.aux_stack.contains(client.as_ref()) +// } +// } +// +// pub struct XLibState { +// display: Display, +// root: Window, +// screen: i32, +// pub atoms: WMAtoms, +// } +// +// impl XLibState { +// pub fn new() -> Self { +// let (display, screen, root) = unsafe { +// let display = XOpenDisplay(null()); +// assert_ne!(display, null_mut()); +// +// let display = Display::new(display); +// let screen = XDefaultScreen(display.get()); +// let root = XRootWindow(display.get(), screen); +// +// (display, screen, root) +// }; +// +// Self { +// atoms: WMAtoms::init(display.clone()), +// display, +// root, +// screen, +// } +// } +// +// pub fn dpy(&self) -> *mut x11::xlib::Display { +// self.display.get() +// } +// +// pub fn root(&self) -> Window { +// self.root +// } +// +// pub fn screen(&self) -> i32 { +// self.screen +// } +// +// pub fn grab_key(&self, window: xlib::Window, keycode: i32, mod_mask: u32) { +// let numlock_mask = self.numlock_mask(); +// let modifiers = vec![0, LockMask, numlock_mask, LockMask | numlock_mask]; +// for &modifier in modifiers.iter() { +// unsafe { +// xlib::XGrabKey( +// self.dpy(), +// keycode, +// mod_mask | modifier, +// window, +// 1, /* true */ +// GrabModeAsync, +// GrabModeAsync, +// ); +// } +// } +// } +// +// pub fn grab_button(&self, window: xlib::Window, button: u32, mod_mask: u32, button_mask: i64) { +// let numlock_mask = self.numlock_mask(); +// let modifiers = vec![0, LockMask, numlock_mask, LockMask | numlock_mask]; +// +// modifiers.iter().for_each(|&modifier| { +// unsafe { +// xlib::XGrabButton( +// self.dpy(), +// button, +// mod_mask | modifier, +// window, +// 1, /*true */ +// button_mask as u32, +// GrabModeAsync, +// GrabModeAsync, +// 0, +// 0, +// ); +// } +// }); +// } +// +// pub fn keycode(&self, string: S) -> i32 +// where +// S: Into, +// { +// let c_string = CString::new(string.into()).unwrap(); +// unsafe { +// let keysym = xlib::XStringToKeysym(c_string.as_ptr()); +// xlib::XKeysymToKeycode(self.dpy(), keysym) as i32 +// } +// } +// +// fn check_for_protocol(&self, window: xlib::Window, proto: xlib::Atom) -> bool { +// let mut protos: *mut xlib::Atom = null_mut(); +// let mut num_protos: i32 = 0; +// +// unsafe { +// if xlib::XGetWMProtocols(self.dpy(), window, &mut protos, &mut num_protos) != 0 { +// for i in 0..num_protos { +// if *protos.offset(i as isize) == proto { +// return true; +// } +// } +// } +// } +// +// return false; +// } +// +// fn send_event(&self, window: xlib::Window, proto: xlib::Atom) -> bool { +// if self.check_for_protocol(window, proto) { +// let mut data = xlib::ClientMessageData::default(); +// data.set_long(0, proto as i64); +// +// let mut event = XEvent { +// client_message: xlib::XClientMessageEvent { +// type_: xlib::ClientMessage, +// serial: 0, +// display: self.dpy(), +// send_event: 0, +// window, +// format: 32, +// message_type: self.atoms.protocols, +// data, +// }, +// }; +// +// unsafe { +// xlib::XSendEvent(self.dpy(), window, 0, xlib::NoEventMask, &mut event); +// } +// +// true +// } else { +// false +// } +// } +// +// fn numlock_mask(&self) -> u32 { +// unsafe { +// let modmap = xlib::XGetModifierMapping(self.dpy()); +// let max_keypermod = (*modmap).max_keypermod; +// +// for i in 0..8 { +// for j in 0..max_keypermod { +// if *(*modmap) +// .modifiermap +// .offset((i * max_keypermod + j) as isize) +// == xlib::XKeysymToKeycode(self.dpy(), x11::keysym::XK_Num_Lock as u64) +// { +// return 1 << i; +// } +// } +// } +// } +// +// 0 +// } +// +// fn clean_mask(&self) -> u32 { +// !(self.numlock_mask() | LockMask) +// & (ShiftMask | ControlMask | Mod1Mask | Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask) +// } +// } +// +// struct Key { +// keycode: i32, +// mod_mask: u32, +// } +// +// struct Button { +// button: i32, +// mod_mask: u32, +// button_mask: u64, +// } +// +// enum KeyOrButton { +// Key(Key), +// Button(Button), +// } +// +// pub struct WMState { +// xlib_state: XLibState, +// key_handlers: Vec<(i32, u32, Rc)>, +// // (button, mod_mask, button_mask) +// buttons: Vec<(u32, u32, i64)>, +// event_handlers: Vec>, +// +// // MutState: +// +// //move_window: +// // u64 : window to move +// // (i32, i32) : initial cursor position +// // (i32, i32) : initial window position +// move_window: Option<(u64, (i32, i32), (i32, i32))>, +// //resize_window: +// // u64 : window to move +// // (i32, i32) : initial window position +// resize_window: Option<(u64, (i32, i32))>, +// clients: HashSet, BuildIdentityHasher>, +// focused_client: Weak, +// current_vscreen: usize, +// virtual_screens: Vec, +// } +// +// impl WMState { +// pub fn new() -> Self { +// Self { +// xlib_state: XLibState::new(), +// key_handlers: vec![], +// event_handlers: vec![], +// buttons: vec![], +// move_window: None, +// resize_window: None, +// clients: Default::default(), +// focused_client: Weak::new(), +// current_vscreen: 0, +// virtual_screens: vec![VirtualScreen::new()], +// } +// } +// +// pub fn init() -> Self { +// let state = Self::new() +// .grab_button( +// 1, +// Mod1Mask, +// ButtonPressMask | ButtonReleaseMask | PointerMotionMask, +// ) +// .grab_button( +// 2, +// Mod1Mask, +// ButtonPressMask | ButtonReleaseMask | PointerMotionMask, +// ) +// .grab_button( +// 3, +// Mod1Mask, +// ButtonPressMask | ButtonReleaseMask | PointerMotionMask, +// ) +// .add_event_handler(Self::handle_toggle_floating) +// .add_event_handler(Self::handle_move_window) +// .add_event_handler(Self::handle_resize_window) +// .add_key_handler("T", Mod1Mask, |state, _| { +// println!("spawning terminal"); +// let _ = state.spawn("xterm", &[]); +// }) +// .add_key_handler("Left", Mod1Mask, |state, _| { +// state.handle_change_virtual_screen(-1); +// }) +// .add_key_handler("Right", Mod1Mask, |state, _| { +// state.handle_change_virtual_screen(1); +// }) +// .add_key_handler("M", Mod1Mask, Self::handle_switch_stack) +// .add_key_handler("Q", Mod1Mask, |state, event| unsafe { +// if event.key.subwindow != 0 { +// if !state +// .xlib_state +// .send_event(event.key.subwindow, state.xlib_state.atoms.delete_window) +// { +// xlib::XKillClient(state.dpy(), event.key.subwindow); +// } +// } +// }) +// .add_key_handler("Q", Mod1Mask | ShiftMask, |state, _event| { +// unsafe { +// xlib::XCloseDisplay(state.dpy()); +// } +// +// std::process::exit(0); +// }); +// +// unsafe { +// let mut wa: xlib::XSetWindowAttributes = std::mem::MaybeUninit::zeroed().assume_init(); +// wa.event_mask = SubstructureRedirectMask +// | StructureNotifyMask +// | SubstructureNotifyMask +// | EnterWindowMask +// | PointerMotionMask +// | ButtonPressMask; +// +// xlib::XChangeWindowAttributes(state.dpy(), state.root(), CWEventMask, &mut wa); +// +// xlib::XSelectInput(state.dpy(), state.root(), wa.event_mask); +// } +// +// state +// } +// +// // MutState Functions: +// +// fn stack_unstacked_clients(&mut self) { +// info!("[stack_unstacked_clients] "); +// +// let unstacked_clients = self +// .clients +// .iter() +// .filter(|&c| !c.floating && !self.is_client_stacked(c)) +// .cloned() +// .collect::>(); +// +// unstacked_clients.iter().for_each(|c| { +// info!( +// "[stack_unstacked_clients] inserting Client({:?}) into aux_stack", +// c +// ); +// +// self.virtual_screens[self.current_vscreen] +// .aux_stack +// .insert(c.clone()); +// }); +// } +// +// fn is_client_stacked(&self, client: &Rc) -> bool { +// self.virtual_screens +// .iter() +// .any(|vs| vs.contains_client(client)) +// } +// +// fn client_for_window(&self, window: Window) -> Option> { +// self.clients +// .iter() +// .filter(|&c| c.window == window) +// .next() +// .cloned() +// } +// +// fn switch_stack_for_client(&mut self, client: Rc) { +// info!("[switch_stack_for_client] client: {:#?}", client); +// +// if self.virtual_screens[self.current_vscreen] +// .master_stack +// .contains(client.as_ref()) +// { +// self.virtual_screens[self.current_vscreen] +// .master_stack +// .remove(client.as_ref()); +// self.virtual_screens[self.current_vscreen] +// .aux_stack +// .insert(client.clone()); +// info!("[switch_stack_for_client] moved to aux stack"); +// } else { +// self.virtual_screens[self.current_vscreen] +// .aux_stack +// .remove(client.as_ref()); +// self.virtual_screens[self.current_vscreen] +// .master_stack +// .insert(client.clone()); +// info!("[switch_stack_for_client] moved to master stack"); +// } +// +// self.clients.replace(Client::new_rc(InnerClient { +// floating: false, +// ..**client.as_ref() +// })); +// } +// +// fn refresh_screen(&mut self) { +// let current_vscreen = self.current_vscreen; +// +// self.stack_unstacked_clients(); +// +// if let Some(vs) = self.virtual_screens.get_mut(self.current_vscreen) { +// vs.master_stack.retain(|c| !c.0.borrow().floating); +// vs.aux_stack.retain(|c| !c.floating); +// +// if vs.master_stack.is_empty() { +// info!("[refresh_screen] master stack was empty, pushing first client if exists:"); +// vs.aux_stack.iter().filter(|c| !c.floating).next().map(|c| { +// info!("[arrange_clients] Client({:#?})", c); +// +// self.virtual_screens[current_vscreen] +// .master_stack +// .insert(c.clone()); +// self.virtual_screens[current_vscreen] +// .aux_stack +// .remove(c.as_ref()); +// }); +// } +// } +// } +// +// pub fn run(mut self) -> Self { +// let event_handlers = self.event_handlers.clone(); +// let key_handlers = self.key_handlers.clone(); +// +// loop { +// let event = unsafe { +// let mut event: xlib::XEvent = std::mem::MaybeUninit::zeroed().assume_init(); +// xlib::XNextEvent(self.dpy(), &mut event); +// +// event +// }; +// +// for handler in event_handlers.iter() { +// handler(&mut self, &event); +// } +// +// match event.get_type() { +// xlib::MapRequest => { +// self.map_request(unsafe { &event.map_request }); +// } +// xlib::UnmapNotify => { +// self.unmap_notify(unsafe { &event.unmap }); +// } +// xlib::DestroyNotify => { +// self.destroy_notify(unsafe { &event.destroy_window }); +// } +// xlib::ConfigureRequest => { +// self.configure_request(unsafe { &event.configure_request }); +// } +// xlib::EnterNotify => { +// self.enter_notify(unsafe { &event.crossing }); +// } +// xlib::ButtonPress => { +// self.button_press(unsafe { &event.button }); +// } +// xlib::KeyPress => { +// let clean_mask = self.xlib_state.clean_mask(); +// +// key_handlers.iter().for_each(|(key, mask, handler)| { +// if unsafe { +// event.key.keycode == *key as u32 +// && event.key.state & clean_mask == *mask & clean_mask +// } { +// handler(&mut self, &event); +// } +// }) +// } +// _ => {} +// } +// } +// } +// +// pub fn dpy(&self) -> *mut xlib::Display { +// self.xlib_state.dpy() +// } +// +// pub fn root(&self) -> xlib::Window { +// self.xlib_state.root() +// } +// +// pub fn grab_button(mut self, button: u32, mod_mask: u32, button_mask: i64) -> Self { +// self.buttons.push((button, mod_mask, button_mask)); +// self.xlib_state +// .grab_button(self.root(), button, mod_mask, button_mask); +// +// self +// } +// +// #[allow(dead_code)] +// pub fn add_event_handler(mut self, handler: F) -> Self +// where +// F: Fn(&mut Self, &XEvent) + 'static, +// { +// self.event_handlers.push(Rc::new(handler)); +// +// self +// } +// +// pub fn add_key_handler(mut self, key: S, mask: u32, handler: F) -> Self +// where +// S: Into, +// F: Fn(&mut Self, &XEvent) + 'static, +// { +// let keycode = self.xlib_state.keycode(key); +// +// self.key_handlers.push((keycode, mask, Rc::new(handler))); +// self.xlib_state.grab_key(self.root(), keycode, mask); +// +// self +// } +// +// fn map_request(&mut self, event: &xlib::XMapRequestEvent) { +// info!("[MapRequest] event: {:#?}", event); +// +// if self.client_for_window(event.window).is_none() { +// info!("[MapRequest] new client: {:#?}", event.window); +// let client = Rc::new(Client::new(event.window)); +// self.clients.insert(client.clone()); +// +// unsafe { +// xlib::XMapWindow(self.dpy(), client.window); +// +// xlib::XSelectInput( +// self.dpy(), +// event.window, +// EnterWindowMask | FocusChangeMask | PropertyChangeMask | StructureNotifyMask, +// ); +// } +// +// self.buttons +// .iter() +// .for_each(|&(button, mod_mask, button_mask)| { +// self.xlib_state +// .grab_button(client.window, button, mod_mask, button_mask); +// }); +// +// self.arrange_clients(); +// self.focus_client(&client); +// } +// } +// +// fn unmap_notify(&mut self, event: &xlib::XUnmapEvent) { +// info!("[UnmapNotify] event: {:#?}", event); +// +// if event.send_event == 0 { +// if let Some(client) = self.client_for_window(event.window) { +// self.clients.remove(&client); +// info!("[UnmapNotify] removing client: {:#?}", client); +// } +// } +// +// self.arrange_clients(); +// } +// +// fn destroy_notify(&mut self, event: &xlib::XDestroyWindowEvent) { +// info!("[DestroyNotify] event: {:?}", event); +// +// if let Some(client) = self.client_for_window(event.window) { +// self.clients.remove(&client); +// +// info!("[DestroyNotify] removed entry: {:?}", client); +// } +// +// self.arrange_clients(); +// } +// +// fn configure_request(&mut self, event: &xlib::XConfigureRequestEvent) { +// info!("[ConfigureRequest] event: {:?}", event); +// +// match self.client_for_window(event.window) { +// Some(client) => { +// info!("[ConfigureRequest] found Client {:#?}", client,); +// +// self.configure_client(&client); +// } +// _ => { +// info!( +// "[ConfigureRequest] no client found for Window({:?}), calling XConfigureWindow()", +// event.window); +// +// let mut wc = xlib::XWindowChanges { +// x: event.x, +// y: event.y, +// width: event.width, +// height: event.height, +// border_width: event.border_width, +// sibling: event.above, +// stack_mode: event.detail, +// }; +// +// unsafe { +// xlib::XConfigureWindow( +// self.dpy(), +// event.window, +// event.value_mask as u32, +// &mut wc, +// ); +// } +// } +// } +// } +// +// fn enter_notify(&mut self, event: &xlib::XCrossingEvent) { +// info!("[EnterNotify] event: {:?}", event); +// +// if let Some(client) = self.client_for_window(event.window) { +// info!("[EnterNotify] focusing Client ({:?})", client); +// self.focus_client(&client); +// } +// } +// +// fn button_press(&mut self, event: &xlib::XButtonEvent) { +// info!("[ButtonPress] event: {:?}", event); +// +// if let Some(client) = self.client_for_window(event.subwindow) { +// info!("[ButtonPress] focusing Client ({:?})", client); +// self.focus_client(&client); +// +// info!("[ButtonPress] raising Window({:?})", event.window); +// +// unsafe { +// xlib::XRaiseWindow(self.dpy(), client.window); +// xlib::XSync(self.dpy(), 0); +// } +// } +// } +// +// fn unfocus_client(&self, client: &Rc) { +// unsafe { +// xlib::XSetInputFocus( +// self.dpy(), +// client.window, +// xlib::RevertToPointerRoot, +// xlib::CurrentTime, +// ); +// +// xlib::XDeleteProperty(self.dpy(), self.root(), self.xlib_state.atoms.active_window); +// } +// } +// +// fn focus_client(&mut self, client: &Rc) { +// if let Some(focused_client) = self.focused_client.upgrade() { +// self.unfocus_client(&focused_client); +// } +// +// self.focused_client = Rc::downgrade(client); +// +// self.virtual_screens[self.current_vscreen].focused_client = self.focused_client.clone(); +// +// unsafe { +// xlib::XSetInputFocus( +// self.dpy(), +// client.window, +// xlib::RevertToPointerRoot, +// xlib::CurrentTime, +// ); +// +// xlib::XChangeProperty( +// self.dpy(), +// self.root(), +// self.xlib_state.atoms.active_window, +// xlib::XA_WINDOW, +// 32, +// xlib::PropModeReplace, +// &client.window as *const u64 as *const _, +// 1, +// ); +// } +// +// self.xlib_state +// .send_event(client.window, self.xlib_state.atoms.take_focus); +// } +// +// fn arrange_clients(&self) { +// let (screen_w, screen_h) = unsafe { +// ( +// xlib::XDisplayWidth(self.dpy(), self.xlib_state.screen()), +// xlib::XDisplayHeight(self.dpy(), self.xlib_state.screen()), +// ) +// }; +// +// if !self.clients.is_empty() { +// info!("[arrange_clients] refreshing screen"); +// self.refresh_screen(); +// +// let window_w = { +// if self.virtual_screens[self.current_vscreen] +// .aux_stack +// .is_empty() +// { +// screen_w +// } else { +// screen_w / 2 +// } +// }; +// +// if let Some(vc) = self.virtual_screens.get_mut(self.current_vscreen) { +// vc.master_stack +// .iter() +// .zip(std::iter::repeat(vc.master_stack.len())) +// .enumerate() +// .chain( +// vc.aux_stack +// .iter() +// .zip(std::iter::repeat(vc.aux_stack.len())) +// .enumerate(), +// ) +// .for_each(|(i, (client, length))| { +// let (mut wc, size, position) = { +// let window_h = screen_h / length as i32; +// +// let size = (window_w, window_h); +// let position = (0, window_h * i as i32); +// +// ( +// xlib::XWindowChanges { +// x: position.0, +// y: position.1, +// width: size.0, +// height: size.1, +// border_width: 0, +// sibling: 0, +// stack_mode: 0, +// }, +// size, +// position, +// ) +// }; +// +// unsafe { +// xlib::XConfigureWindow( +// self.dpy(), +// client.window, +// (xlib::CWY | xlib::CWX | xlib::CWHeight | xlib::CWWidth) as u32, +// &mut wc, +// ); +// +// self.clients.replace(Client::new_rc(InnerClient { +// size, +// position, +// ..**client +// })); +// +// self.configure_client(&client); +// +// xlib::XSync(self.dpy(), 0); +// } +// }); +// } +// } +// } +// +// fn configure_client(&self, client: &Rc) { +// let mut event = { +// xlib::XConfigureEvent { +// type_: xlib::ConfigureNotify, +// display: self.dpy(), +// event: client.window, +// window: client.window, +// x: client.position.0, +// y: client.position.1, +// width: client.size.0, +// height: client.size.1, +// border_width: 0, +// override_redirect: 0, +// send_event: 0, +// serial: 0, +// above: 0, +// } +// }; +// +// unsafe { +// xlib::XSendEvent( +// self.dpy(), +// event.window, +// 0, +// StructureNotifyMask, +// &mut event as *mut _ as *mut XEvent, +// ); +// } +// } +// +// fn handle_change_virtual_screen(&mut self, direction: i32) { +// assert!(direction == 1 || direction == -1); +// +// // hide all windows from current virtual screen +// self.virtual_screens[self.current_vscreen] +// .master_stack +// .iter() +// .chain(self.virtual_screens[self.current_vscreen].aux_stack.iter()) +// .for_each(|c| unsafe { +// xlib::XMoveWindow(self.dpy(), c.window, c.size.0 * -2, c.position.1); +// }); +// +// // change current_vscreen variable +// let mut new_vscreen = self.current_vscreen as isize + direction as isize; +// +// if new_vscreen >= self.virtual_screens.len() as isize { +// self.virtual_screens.push(VirtualScreen::new()); +// } else if new_vscreen < 0 { +// new_vscreen = self.virtual_screens.len() as isize - 1; +// } +// +// self.current_vscreen = new_vscreen as usize; +// +// self.arrange_clients(); +// +// // focus the focused cliend of the new virtual screen +// if let Some(client) = self.virtual_screens[self.current_vscreen] +// .focused_client +// .upgrade() +// { +// self.focus_client(&client); +// } +// } +// +// fn handle_switch_stack(&mut self, event: &XEvent) { +// if let Some(client) = self.client_for_window(event.button.subwindow) { +// self.switch_stack_for_client(client); +// } +// +// self.arrange_clients(); +// } +// +// fn handle_toggle_floating(&mut self, event: &XEvent) { +// if event.get_type() == xlib::ButtonPress { +// let event = unsafe { event.button }; +// let clean_mask = self.xlib_state.clean_mask(); +// +// if event.button == 2 +// && event.state & clean_mask == Mod1Mask & clean_mask +// && event.subwindow != 0 +// { +// if let Some(client) = self.client_for_window(event.subwindow) { +// info!( +// "[handle_toggle_floating] {:#?} floating -> {:?}", +// client, !client.floating +// ); +// +// self.clients.replace(Client::new_rc(InnerClient { +// floating: !client.floating, +// ..**client +// })); +// } +// +// self.arrange_clients(); +// } +// } +// } +// +// fn handle_move_window(&mut self, event: &XEvent) { +// let clean_mask = self.xlib_state.clean_mask(); +// +// if unsafe { +// self.move_window.is_none() +// && event.get_type() == xlib::ButtonPress +// && event.button.button == 1 +// && event.button.state & clean_mask == Mod1Mask & clean_mask +// && event.button.subwindow != 0 +// } { +// let win_pos = unsafe { +// let mut attr: xlib::XWindowAttributes = +// std::mem::MaybeUninit::zeroed().assume_init(); +// xlib::XGetWindowAttributes(self.dpy(), event.button.subwindow, &mut attr); +// +// (attr.x, attr.y) +// }; +// +// self.move_window = Some(unsafe { +// ( +// event.button.subwindow, +// (event.button.x, event.button.y), +// win_pos, +// ) +// }); +// +// if let Some(client) = self.client_for_window(event.button.subwindow) { +// self.clients.replace(Client::new_rc(InnerClient { +// floating: true, +// ..**client +// })); +// } +// +// self.arrange_clients(); +// } else if unsafe { +// self.move_window.is_some() +// && event.get_type() == xlib::ButtonRelease +// && event.button.button == 1 +// } { +// self.move_window = None; +// } else if self.move_window.is_some() && event.get_type() == xlib::MotionNotify { +// let move_window = self.move_window.unwrap(); +// +// let attr = unsafe { +// let mut attr: xlib::XWindowAttributes = +// std::mem::MaybeUninit::zeroed().assume_init(); +// xlib::XGetWindowAttributes(self.dpy(), move_window.0, &mut attr); +// +// attr +// }; +// +// let (x, y) = unsafe { +// ( +// event.motion.x - move_window.1 .0 + move_window.2 .0, +// event.motion.y - move_window.1 .1 + move_window.2 .1, +// ) +// }; +// +// let mut wc = xlib::XWindowChanges { +// x, +// y, +// width: attr.width, +// height: attr.height, +// border_width: 0, +// sibling: 0, +// stack_mode: 0, +// }; +// +// unsafe { +// xlib::XConfigureWindow( +// self.dpy(), +// move_window.0, +// (xlib::CWX | xlib::CWY) as u32, +// &mut wc, +// ); +// +// xlib::XSync(self.dpy(), 0); +// } +// } +// } +// +// fn handle_resize_window(&mut self, event: &XEvent) { +// let clean_mask = self.xlib_state.clean_mask(); +// +// let resize_window = self.resize_window; +// +// if unsafe { +// resize_window.is_none() +// && event.get_type() == xlib::ButtonPress +// && event.button.button == 3 +// && event.button.state & clean_mask == Mod1Mask & clean_mask +// && event.button.subwindow != 0 +// } { +// unsafe { +// let mut attr: xlib::XWindowAttributes = +// std::mem::MaybeUninit::zeroed().assume_init(); +// +// xlib::XGetWindowAttributes(self.dpy(), event.button.subwindow, &mut attr); +// +// self.resize_window = Some((event.button.subwindow, (attr.x, attr.y))); +// +// xlib::XWarpPointer( +// self.dpy(), +// 0, +// event.button.subwindow, +// 0, +// 0, +// 0, +// 0, +// attr.width + attr.border_width - 1, +// attr.height + attr.border_width - 1, +// ); +// }; +// +// if let Some(client) = self.client_for_window(unsafe { event.button.subwindow }) { +// self.clients.replace(Client::new_rc(InnerClient { +// floating: true, +// ..**client +// })); +// } +// +// self.arrange_clients(); +// } else if unsafe { +// resize_window.is_some() +// && event.get_type() == xlib::ButtonRelease +// && event.button.button == 3 +// } { +// self.resize_window = None; +// } else if resize_window.is_some() && event.get_type() == xlib::MotionNotify { +// let resize_window = resize_window.unwrap(); +// +// let attr = unsafe { +// let mut attr: xlib::XWindowAttributes = +// std::mem::MaybeUninit::zeroed().assume_init(); +// xlib::XGetWindowAttributes(self.dpy(), resize_window.0, &mut attr); +// +// attr +// }; +// +// unsafe { +// let (width, height) = { +// ( +// std::cmp::max( +// 1, +// event.motion.x - resize_window.1 .0 + 2 * attr.border_width + 1, +// ), +// std::cmp::max( +// 1, +// event.motion.y - resize_window.1 .1 + 2 * attr.border_width + 1, +// ), +// ) +// }; +// +// let mut wc = xlib::XWindowChanges { +// x: attr.x, +// y: attr.y, +// width, +// height, +// border_width: attr.border_width, +// sibling: 0, +// stack_mode: 0, +// }; +// +// xlib::XConfigureWindow( +// self.dpy(), +// resize_window.0, +// (xlib::CWWidth | xlib::CWHeight) as u32, +// &mut wc, +// ); +// +// xlib::XSync(self.dpy(), 0); +// } +// } +// } +// +// // spawn a new process / calls execvp +// pub fn spawn(&self, command: T, args: &[T]) -> Result<()> { +// let fd = unsafe { xlib::XConnectionNumber(self.dpy()) }; +// +// match unsafe { fork() } { +// Ok(ForkResult::Parent { .. }) => Ok(()), +// Ok(ForkResult::Child) => { +// // i dont think i want to exit this block without closing the program, +// // so unwrap everything +// +// close(fd) +// .or_else(|_| Err("failed to close x connection")) +// .unwrap(); +// setsid().ok().ok_or("failed to setsid").unwrap(); +// +// let c_cmd = CString::new(command.to_string()).unwrap(); +// +// let c_args: Vec<_> = args +// .iter() +// .map(|s| CString::new(s.to_string()).unwrap()) +// .collect(); +// +// execvp( +// &c_cmd, +// &c_args.iter().map(|s| s.as_c_str()).collect::>(), +// ) +// .or(Err("failed to execvp()")) +// .unwrap(); +// +// eprintln!("execvp({}) failed.", c_cmd.to_str().unwrap()); +// std::process::exit(0); +// } +// Err(_) => Err(Error::new(ErrorKind::Other, "failed to fork.")), +// } +// } +// } diff --git a/src/xlib.rs b/src/xlib.rs new file mode 100644 index 0000000..a734af6 --- /dev/null +++ b/src/xlib.rs @@ -0,0 +1,468 @@ +use std::ptr::{null, null_mut}; +use std::{ffi::CString, rc::Rc}; + +use x11::xlib::{ + self, Atom, ButtonPressMask, CWEventMask, ControlMask, EnterWindowMask, FocusChangeMask, + LockMask, Mod1Mask, Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask, PointerMotionMask, + PropertyChangeMask, ShiftMask, StructureNotifyMask, SubstructureNotifyMask, + SubstructureRedirectMask, Window, XConfigureRequestEvent, XDefaultScreen, XEvent, XInternAtom, + XMapWindow, XOpenDisplay, XRaiseWindow, XRootWindow, XSync, +}; +use xlib::GrabModeAsync; + +use crate::clients::Client; + +pub struct XLib { + display: Display, + root: Window, + screen: i32, + atoms: Atoms, +} + +struct Atoms { + protocols: Atom, + delete_window: Atom, + active_window: Atom, + take_focus: Atom, +} + +pub enum KeyOrButton { + Key(i32, u32), + Button(u32, u32, u64), +} + +impl KeyOrButton { + pub fn key(keycode: i32, modmask: u32) -> Self { + Self::Key(keycode, modmask) + } + pub fn button(button: u32, modmask: u32, buttonmask: u64) -> Self { + Self::Button(button, modmask, buttonmask) + } +} + +#[derive(Clone)] +pub struct Display(Rc<*mut xlib::Display>); + +impl XLib { + pub fn new() -> Self { + let (display, screen, root) = unsafe { + let display = XOpenDisplay(null()); + + assert_ne!(display, null_mut()); + + let display = Display::new(display); + let screen = XDefaultScreen(display.get()); + let root = XRootWindow(display.get(), screen); + + (display, screen, root) + }; + + Self { + atoms: Atoms::init(display.clone()), + root, + screen, + display, + } + } + + pub fn init(self) -> Self { + unsafe { + let mut window_attributes = + std::mem::MaybeUninit::::zeroed().assume_init(); + + window_attributes.event_mask = SubstructureRedirectMask + | StructureNotifyMask + | SubstructureNotifyMask + | EnterWindowMask + | PointerMotionMask + | ButtonPressMask; + + xlib::XChangeWindowAttributes( + self.dpy(), + self.root, + CWEventMask, + &mut window_attributes, + ); + + xlib::XSelectInput(self.dpy(), self.root, window_attributes.event_mask); + } + + self + } + + fn dpy(&self) -> *mut xlib::Display { + self.display.get() + } + + pub fn next_event(&self) -> XEvent { + unsafe { + let mut event = std::mem::MaybeUninit::::zeroed().assume_init(); + xlib::XNextEvent(self.dpy(), &mut event); + + event + } + } + + pub fn grab_key_or_button(&self, window: Window, key: &KeyOrButton) { + let numlock_mask = self.get_numlock_mask(); + let modifiers = vec![0, LockMask, numlock_mask, LockMask | numlock_mask]; + + for modifier in modifiers.iter() { + match key { + &KeyOrButton::Key(keycode, modmask) => { + unsafe { + xlib::XGrabKey( + self.dpy(), + keycode, + modmask | modifier, + window, + 1, /* true */ + GrabModeAsync, + GrabModeAsync, + ); + } + } + &KeyOrButton::Button(button, modmask, buttonmask) => { + unsafe { + xlib::XGrabButton( + self.dpy(), + button, + modmask | modifier, + window, + 1, /*true */ + buttonmask as u32, + GrabModeAsync, + GrabModeAsync, + 0, + 0, + ); + } + } + } + } + } + + pub fn focus_client(&self, client: &Client) { + unsafe { + xlib::XSetInputFocus( + self.dpy(), + client.window, + xlib::RevertToPointerRoot, + xlib::CurrentTime, + ); + + xlib::XChangeProperty( + self.dpy(), + self.root, + self.atoms.active_window, + xlib::XA_WINDOW, + 32, + xlib::PropModeReplace, + &client.window as *const u64 as *const _, + 1, + ); + } + + self.send_event(client, self.atoms.take_focus); + } + + pub fn unfocus_client(&self, client: &Client) { + unsafe { + xlib::XSetInputFocus( + self.dpy(), + client.window, + xlib::RevertToPointerRoot, + xlib::CurrentTime, + ); + + xlib::XDeleteProperty(self.dpy(), self.root, self.atoms.active_window); + } + } + + pub fn move_resize_client(&self, client: &Client) { + let mut windowchanges = xlib::XWindowChanges { + x: client.position.0, + y: client.position.1, + width: client.size.0, + height: client.size.1, + border_width: 0, + sibling: 0, + stack_mode: 0, + }; + + unsafe { + xlib::XConfigureWindow( + self.dpy(), + client.window, + (xlib::CWY | xlib::CWX | xlib::CWHeight | xlib::CWWidth) as u32, + &mut windowchanges, + ); + + // I don't think I have to call this ~ + //self.configure(client); + + xlib::XSync(self.dpy(), 0); + } + } + + pub fn move_client(&self, client: &Client) { + let mut wc = xlib::XWindowChanges { + x: client.position.0, + y: client.position.1, + width: client.size.0, + height: client.size.1, + border_width: 0, + sibling: 0, + stack_mode: 0, + }; + + unsafe { + xlib::XConfigureWindow( + self.dpy(), + client.window, + (xlib::CWWidth | xlib::CWHeight) as u32, + &mut wc, + ); + + xlib::XSync(self.dpy(), 0); + } + } + + pub fn resize_client(&self, client: &Client) { + let mut wc = xlib::XWindowChanges { + x: client.position.0, + y: client.position.1, + width: client.size.0, + height: client.size.1, + border_width: 0, + sibling: 0, + stack_mode: 0, + }; + + unsafe { + xlib::XConfigureWindow( + self.dpy(), + client.window, + (xlib::CWX | xlib::CWY) as u32, + &mut wc, + ); + + xlib::XSync(self.dpy(), 0); + } + } + + pub fn hide_client(&self, client: &Client) { + let mut wc = xlib::XWindowChanges { + x: client.size.0 * -2, + y: client.position.1, + width: client.size.0, + height: client.size.1, + border_width: 0, + sibling: 0, + stack_mode: 0, + }; + + unsafe { + xlib::XConfigureWindow( + self.dpy(), + client.window, + (xlib::CWWidth | xlib::CWHeight) as u32, + &mut wc, + ); + + xlib::XSync(self.dpy(), 0); + } + } + + pub fn raise_client(&self, client: &Client) { + unsafe { + XRaiseWindow(self.dpy(), client.window); + XSync(self.dpy(), 0); + } + } + + pub fn configure_window(&self, event: &XConfigureRequestEvent) { + let mut wc = xlib::XWindowChanges { + x: event.x, + y: event.y, + width: event.width, + height: event.height, + border_width: event.border_width, + sibling: event.above, + stack_mode: event.detail, + }; + + unsafe { + xlib::XConfigureWindow(self.dpy(), event.window, event.value_mask as u32, &mut wc); + } + } + + pub fn configure_client(&self, client: &Client) { + let mut event = xlib::XConfigureEvent { + type_: xlib::ConfigureNotify, + display: self.dpy(), + event: client.window, + window: client.window, + x: client.position.0, + y: client.position.1, + width: client.size.0, + height: client.size.1, + border_width: 0, + override_redirect: 0, + send_event: 0, + serial: 0, + above: 0, + }; + + unsafe { + xlib::XSendEvent( + self.dpy(), + event.window, + 0, + StructureNotifyMask, + &mut event as *mut _ as *mut XEvent, + ); + } + } + + pub fn map_window(&self, window: Window) { + unsafe { + XMapWindow(self.dpy(), window); + + xlib::XSelectInput( + self.dpy(), + window, + EnterWindowMask | FocusChangeMask | PropertyChangeMask | StructureNotifyMask, + ); + } + } + + pub fn dimensions(&self) -> (i32, i32) { + unsafe { + ( + xlib::XDisplayWidth(self.dpy(), self.screen), + xlib::XDisplayHeight(self.dpy(), self.screen), + ) + } + } + + fn check_for_protocol(&self, client: &Client, proto: xlib::Atom) -> bool { + let mut protos: *mut xlib::Atom = null_mut(); + let mut num_protos: i32 = 0; + + unsafe { + if xlib::XGetWMProtocols(self.dpy(), client.window, &mut protos, &mut num_protos) != 0 { + for i in 0..num_protos { + if *protos.offset(i as isize) == proto { + return true; + } + } + } + } + + return false; + } + + fn send_event(&self, client: &Client, proto: xlib::Atom) -> bool { + if self.check_for_protocol(client, proto) { + let mut data = xlib::ClientMessageData::default(); + data.set_long(0, proto as i64); + + let mut event = XEvent { + client_message: xlib::XClientMessageEvent { + type_: xlib::ClientMessage, + serial: 0, + display: self.dpy(), + send_event: 0, + window: client.window, + format: 32, + message_type: self.atoms.protocols, + data, + }, + }; + + unsafe { + xlib::XSendEvent(self.dpy(), client.window, 0, xlib::NoEventMask, &mut event); + } + + true + } else { + false + } + } + + fn keycode(&self, string: S) -> i32 + where + S: AsRef, + { + let c_string = CString::new(string.as_ref()).unwrap(); + + unsafe { + let keysym = xlib::XStringToKeysym(c_string.as_ptr()); + xlib::XKeysymToKeycode(self.dpy(), keysym) as i32 + } + } + + fn get_numlock_mask(&self) -> u32 { + unsafe { + let modmap = xlib::XGetModifierMapping(self.dpy()); + let max_keypermod = (*modmap).max_keypermod; + + for i in 0..8 { + for j in 0..max_keypermod { + if *(*modmap) + .modifiermap + .offset((i * max_keypermod + j) as isize) + == xlib::XKeysymToKeycode(self.dpy(), x11::keysym::XK_Num_Lock as u64) + { + return 1 << i; + } + } + } + } + + 0 + } + + fn get_clean_mask(&self) -> u32 { + !(self.get_numlock_mask() | LockMask) + & (ShiftMask | ControlMask | Mod1Mask | Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask) + } +} + +impl Display { + pub fn new(display: *mut x11::xlib::Display) -> Self { + Self { + 0: Rc::new(display), + } + } + + pub fn get(&self) -> *mut x11::xlib::Display { + *self.0 + } +} + +impl Atoms { + fn init(display: Display) -> Self { + unsafe { + Self { + protocols: { + let name = CString::new("WM_PROTOCOLS").unwrap(); + XInternAtom(display.get(), name.as_c_str().as_ptr(), 0) + }, + delete_window: { + let name = CString::new("WM_DELETE_WINDOW").unwrap(); + XInternAtom(display.get(), name.as_c_str().as_ptr(), 0) + }, + active_window: { + let name = CString::new("WM_ACTIVE_WINDOW").unwrap(); + XInternAtom(display.get(), name.as_c_str().as_ptr(), 0) + }, + take_focus: { + let name = CString::new("WM_TAKE_FOCUS").unwrap(); + XInternAtom(display.get(), name.as_c_str().as_ptr(), 0) + }, + } + } + } +}