diff --git a/Cargo.toml b/Cargo.toml index a01419a..9d9fbd3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,5 +10,5 @@ edition = "2018" nix = "0.19.1" x11 = {version = "2.18.2", features = ["xlib"] } log = "0.4.13" -weak-table = {path = "/mnt/storage/rust/weak-table-rs"} +weak-table = "0.3.0" simple_logger = "1.11.0" \ No newline at end of file diff --git a/src/clients.rs b/src/clients.rs index 1398488..d2a987f 100644 --- a/src/clients.rs +++ b/src/clients.rs @@ -1,15 +1,9 @@ -use std::{ - borrow::{Borrow, BorrowMut}, - collections::HashSet, - ops::{Deref, DerefMut}, - rc::Rc, -}; +use std::{borrow::Borrow, cell::RefCell, collections::HashMap, rc::Rc}; use std::{ hash::{Hash, Hasher}, rc::Weak, }; -use weak_table::WeakHashSet; use x11::xlib::Window; use crate::util::BuildIdentityHasher; @@ -36,6 +30,12 @@ impl PartialEq for Client { impl Eq for Client {} +impl Borrow for Client { + fn borrow(&self) -> &Window { + &self.window + } +} + trait ClientKey { fn key(&self) -> u64; } @@ -84,135 +84,348 @@ impl<'a> Borrow for Rc { } } -trait ClientList { - fn contains_key(&self, key: &T) -> bool - where - T: ClientKey; - - fn get_with_key(&self, key: &T) -> Option> - where - T: ClientKey; - - fn remove_key(&mut self, key: &T) -> bool - where - T: ClientKey; -} - -struct Clients(HashSet, BuildIdentityHasher>); - -impl Default for Clients { - fn default() -> Self { - Self(Default::default()) - } -} - -impl Deref for Clients { - type Target = HashSet, BuildIdentityHasher>; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for Clients { - fn deref_mut(&mut self) -> &mut HashSet, BuildIdentityHasher> { - &mut self.0 - } -} - -impl ClientList for Clients { - fn contains_key(&self, key: &T) -> bool - where - T: ClientKey, - { - self.0.contains(key as &dyn ClientKey) - } - - fn get_with_key(&self, key: &T) -> Option> - where - T: ClientKey, - { - self.0.get(key as &dyn ClientKey).cloned() - } - - fn remove_key(&mut self, key: &T) -> bool - where - T: ClientKey, - { - self.0.remove(key as &dyn ClientKey) - } -} - -struct ClientRefs(WeakHashSet, BuildIdentityHasher>); - -impl Default for ClientRefs { - fn default() -> Self { - Self(Default::default()) - } -} - -impl Deref for ClientRefs { - type Target = WeakHashSet, BuildIdentityHasher>; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for ClientRefs { - fn deref_mut(&mut self) -> &mut WeakHashSet, BuildIdentityHasher> { - &mut self.0 - } -} - -impl ClientList for ClientRefs { - fn contains_key(&self, key: &T) -> bool - where - T: ClientKey, - { - self.0.contains(key as &dyn ClientKey) - } - - fn get_with_key(&self, key: &T) -> Option> - where - T: ClientKey, - { - self.0.get(key as &dyn ClientKey) - } - - fn remove_key(&mut self, key: &T) -> bool - where - T: ClientKey, - { - self.0.remove(key as &dyn ClientKey) - } -} - #[cfg(test)] mod tests { use super::*; #[test] - fn client_lists_test() { - let mut clients: Clients = Default::default(); + fn client_lists_test() {} +} - clients.insert(Rc::new(Client { - window: 1, - floating: false, - position: (1, 1), - size: (1, 1), - })); +mod no_refcell { + use std::{collections::VecDeque, iter::repeat}; - assert!(clients.contains_key(&1u64)); + use super::*; - let mut client_refs = ClientRefs::default(); + type ClientsWrapped = Rc>; + type Clients = HashMap; + type ClientRef = u64; + type ClientRefs = Vec; - client_refs.insert(clients.get_with_key(&1u64).unwrap()); + struct ClientState { + clients: Clients, + virtual_screens: VecDeque, + } - assert!(client_refs.contains_key(&1u64)); + struct VirtualScreen { + master: ClientRefs, + aux: ClientRefs, + focused: Option, + } - clients.remove_key(&1u64); + impl ClientState { + fn insert(&mut self, client: Client) { + let key = client.key(); - assert!(!client_refs.contains_key(&1u64)); + self.clients.insert(key, client); + } + + fn get(&self, key: &K) -> Option<&Client> + where + K: ClientKey, + { + self.clients.get(&key.key()) + } + + fn get_mut(&mut self, key: &K) -> Option<&mut Client> + where + K: ClientKey, + { + self.clients.get_mut(&key.key()) + } + + fn toggle_floating(&mut self, key: &K) -> Option + where + K: ClientKey, + { + match self.get_mut(key) { + Some(client) => { + client.floating = !client.floating; + Some(client.floating) + } + None => None, + } + } + + fn get_virtualscreen_for_client(&self, key: &K) -> Option<&VirtualScreen> + where + K: ClientKey, + { + self.virtual_screens.iter().find_map( + |vs| { + if vs.contains(key) { + Some(vs) + } else { + None + } + }, + ) + } + + fn get_mut_virtualscreen_for_client(&mut self, key: &K) -> Option<&mut VirtualScreen> + where + K: ClientKey, + { + self.virtual_screens.iter_mut().find_map( + |vs| { + if vs.contains(key) { + Some(vs) + } else { + None + } + }, + ) + } + + /// focuses client `key` on current virtual screen + fn focus_client(&mut self, key: &K) + where + K: ClientKey, + { + match self.virtual_screens.front_mut() { + Some(vs) => vs.focus(key), + None => {} + } + } + + fn stack_unstacked(&mut self) { + let unstacked = self + .clients + .iter() + .filter(|&(key, client)| { + !client.floating && self.get_virtualscreen_for_client(key).is_some() + }) + .map(|(key, _)| key) + .collect::>(); + + match self.virtual_screens.front_mut() { + Some(vs) => vs.aux.extend(unstacked.into_iter()), + None => {} + } + } + + fn switch_stack_for_client(&mut self, key: &K) + where + K: ClientKey, + { + if let Some(vs) = self.get_mut_virtualscreen_for_client(key) { + match vs.master.iter().position(|&key| key == key.key()) { + Some(index) => { + vs.aux.extend(vs.master.drain(index..=index)); + } + None => { + let index = vs.aux.iter().position(|&key| key == key.key()).unwrap(); + vs.master.extend(vs.aux.drain(index..=index)); + } + } + } + } + + fn refresh_virtual_screen(&mut self) { + let clients = &self.clients; + + if let Some(vs) = self.virtual_screens.front_mut() { + vs.master.retain(|key| match clients.get(key) { + Some(client) => !client.floating, + None => false, + }); + vs.aux.retain(|key| match clients.get(key) { + Some(client) => !client.floating, + None => false, + }); + + // if master is empty but aux has at least one client, drain from aux to master + if vs.master.is_empty() && !vs.aux.is_empty() { + vs.master.extend(vs.aux.drain(..1)); + } + } + } + + /** + resizes and moves clients on the current virtual screen with `width` and `height` as + screen width and screen height + */ + fn arange_virtual_screen(&mut self, width: i32, height: i32) { + // should be fine to unwrap since we will always have at least 1 virtual screen + + if let Some(vs) = self.virtual_screens.front_mut() { + // if aux is empty -> width : width / 2 + let width = width / 1 + i32::from(!vs.aux.is_empty()); + + // chaining master and aux together with `Zip`s for height and x reduces duplicate code + for ((i, key), (height, x)) in vs + .master + .iter() + .enumerate() + // add repeating height for each window and x pos for each window + .zip(repeat(height / vs.master.len() as i32).zip(repeat(0i32))) + .chain( + vs.aux + .iter() + .enumerate() + .zip(repeat(height / vs.aux.len() as i32).zip(repeat(width))), + ) + { + let size = (width, height); + let position = (x, height * i as i32); + + if let Some(client) = self.clients.get_mut(key) { + *client = Client { + size, + position, + ..*client + }; + } + } + } + } + + // Should have xlib send those changes back to the x server after this function + } + + impl VirtualScreen { + fn contains(&self, key: &K) -> bool + where + K: ClientKey, + { + self.master.contains(&key.key()) || self.aux.contains(&key.key()) + } + + fn focus(&mut self, key: &K) + where + K: ClientKey, + { + self.focused = Some(key.key()); + } } } + +/* +mod refcell { + use std::collections::VecDeque; + + use super::*; + + type ClientsWrapped = Rc>; + type Clients = HashMap>, BuildIdentityHasher>; + type ClientRef = Weak>; + type ClientRefs = Vec; + + struct ClientState { + clients: Clients, + virtual_screens: VecDeque, + } + + struct VirtualScreen { + master: ClientRefs, + aux: ClientRefs, + focused: Option, + } + + impl ClientState { + fn insert(&mut self, client: Client) { + let key = client.key(); + + self.clients.insert(key, Rc::new(RefCell::new(client))); + } + + fn get(&self, key: &K) -> Option<&Rc>> + where + K: ClientKey, + { + self.clients.get(&key.key()) + } + + fn toggle_floating(&mut self, key: &K) -> Option + where + K: ClientKey, + { + match self.get(key) { + Some(client) => { + let client = client.borrow_mut(); + client.floating = !client.floating; + + Some(client.floating) + } + None => None, + } + } + + fn get_virtualscreen_for_client(&self, key: &K) -> Option<&VirtualScreen> + where + K: ClientKey, + { + self.virtual_screens.iter().find_map( + |vs| { + if vs.contains(key) { + Some(vs) + } else { + None + } + }, + ) + } + + fn get_mut_virtualscreen_for_client(&mut self, key: &K) -> Option<&mut VirtualScreen> + where + K: ClientKey, + { + self.virtual_screens.iter_mut().find_map( + |vs| { + if vs.contains(key) { + Some(vs) + } else { + None + } + }, + ) + } + + /// focuses client `key` on current virtual screen + fn focus_client(&mut self, key: &K) + where + K: ClientKey, + { + match self.virtual_screens.front_mut() { + Some(vs) => vs.focus(key), + None => {} + } + } + + fn stack_unstacked(&mut self) { + let unstacked = self + .clients + .iter() + .filter(|&(key, client)| { + !client.as_ref().borrow().floating + && self.get_virtualscreen_for_client(key).is_some() + }) + .map(|(key, _)| key) + .collect::>(); + + match self.virtual_screens.front_mut() { + Some(vs) => vs.aux.extend(unstacked.into_iter()), + None => {} + } + } + + fn arrange(&mut self) {} + } + + impl VirtualScreen { + fn contains(&self, key: &K) -> bool + where + K: ClientKey, + { + self.master.contains(&key.key()) || self.aux.contains(&key.key()) + } + + fn focus(&mut self, key: &K) + where + K: ClientKey, + { + self.focused = Some(key.key()); + } + } +} +*/ diff --git a/src/state.rs b/src/state.rs new file mode 100644 index 0000000..8b951c0 --- /dev/null +++ b/src/state.rs @@ -0,0 +1,417 @@ +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; + +#[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 + } +} + +#[derive(Clone, Debug)] +pub struct Client { + window: Window, + floating: bool, + size: (i32, i32), + position: (i32, i32), +} + +impl Default for Client { + fn default() -> Self { + Self { + window: 0, + floating: false, + size: (0, 0), + position: (0, 0), + } + } +} + +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(), + } + } + + fn contains_client(&self, client: Rc>) -> bool { + self.master_stack.contains_key(&client.borrow().window) + || self.aux_stack.contains_key(&client.borrow().window) + } +} + +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, + ); + } + } + } + + 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 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 + ); + + 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()) + } + + 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; + + 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"); + } + } + } + + fn refresh_screen(&mut self) { + let current_vscreen = self.current_vscreen; + + 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 + }); + + Some(()) + }); + + self.stack_unstacked_clients(); + + if self.virtual_screens[current_vscreen] + .master_stack + .is_empty() + { + info!("[refresh_screen] master stack was empty, pushing first client if exists:"); + + 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); + + self.virtual_screens[current_vscreen] + .master_stack + .insert(w, c); + self.virtual_screens[current_vscreen].aux_stack.remove(&w) + }); + } + } +}