reworked Client list

This commit is contained in:
noonebtw 2021-04-22 02:22:46 +02:00
parent 5823c9ae66
commit 81536fb52c
5 changed files with 1151 additions and 1183 deletions

View file

@ -10,4 +10,5 @@ edition = "2018"
nix = "0.19.1" nix = "0.19.1"
x11 = {version = "2.18.2", features = ["xlib"] } x11 = {version = "2.18.2", features = ["xlib"] }
log = "0.4.13" log = "0.4.13"
weak-table = "0.3.0"
simple_logger = "1.11.0" simple_logger = "1.11.0"

View file

@ -1,3 +1,2 @@
hard_tabs = true
imports_granularity = "Crate" imports_granularity = "Crate"
wrap_comments = true wrap_comments = true

View file

@ -1,6 +1,8 @@
use std::io::Result;
use log::info; use log::info;
use std::io::Result;
//mod state;
mod util;
mod wm; mod wm;
#[allow(dead_code)] #[allow(dead_code)]
@ -25,7 +27,6 @@ unsafe extern "C" fn xlib_error_handler(
} }
} }
fn main() -> Result<()> { fn main() -> Result<()> {
simple_logger::SimpleLogger::new().init().unwrap(); simple_logger::SimpleLogger::new().init().unwrap();
info!("Hello, World!"); info!("Hello, World!");

24
src/util.rs Normal file
View file

@ -0,0 +1,24 @@
use std::hash::{BuildHasherDefault, Hasher};
#[derive(Debug, Clone, Copy, Default)]
pub struct IdentityHasher(usize);
impl Hasher for IdentityHasher {
fn finish(&self) -> u64 {
self.0 as u64
}
fn write(&mut self, _bytes: &[u8]) {
unimplemented!("IdentityHasher only supports usize keys")
}
fn write_u64(&mut self, i: u64) {
self.0 = i as usize;
}
fn write_usize(&mut self, i: usize) {
self.0 = i;
}
}
pub type BuildIdentityHasher = BuildHasherDefault<IdentityHasher>;

279
src/wm.rs
View file

@ -1,8 +1,8 @@
// asdf
use std::{ use std::{
cell::RefCell, cell::RefCell,
collections::{hash_map::Entry, HashMap}, collections::{hash_map::Entry, HashMap, HashSet},
ffi::CString, ffi::CString,
hash::{Hash, Hasher},
io::{Error, ErrorKind, Result}, io::{Error, ErrorKind, Result},
ptr::{null, null_mut}, ptr::{null, null_mut},
rc::{Rc, Weak}, rc::{Rc, Weak},
@ -39,56 +39,34 @@ impl Display {
} }
pub struct WMAtoms { pub struct WMAtoms {
pub protocols: Option<Atom>, protocols: Atom,
pub delete: Option<Atom>, delete_window: Atom,
pub active_window: Option<Atom>, active_window: Atom,
pub take_focus: Option<Atom>, take_focus: Atom,
} }
impl WMAtoms { impl WMAtoms {
pub fn init(display: Display) -> Self { fn init(display: Display) -> Self {
unsafe {
Self { Self {
protocols: { protocols: {
Some(unsafe { let name = CString::new("WM_PROTOCOLS").unwrap();
let wm_protocols_str = CString::new("WM_PROTOCOLS").unwrap(); XInternAtom(display.get(), name.as_c_str().as_ptr(), 0)
XInternAtom(display.get(), wm_protocols_str.as_c_str().as_ptr(), 0)
})
.filter(|&atom| atom != 0)
}, },
delete: { delete_window: {
Some(unsafe { let name = CString::new("WM_DELETE_WINDOW").unwrap();
let wm_delete_str = CString::new("WM_DELETE_WINDOW").unwrap(); XInternAtom(display.get(), name.as_c_str().as_ptr(), 0)
XInternAtom(display.get(), wm_delete_str.as_c_str().as_ptr(), 0)
})
.filter(|&atom| atom != 0)
}, },
active_window: { active_window: {
Some(unsafe { let name = CString::new("WM_ACTIVE_WINDOW").unwrap();
let atom_cstr = CString::new("_NET_ACTIVE_WINDOW").unwrap(); XInternAtom(display.get(), name.as_c_str().as_ptr(), 0)
XInternAtom(display.get(), atom_cstr.as_c_str().as_ptr(), 0)
})
.filter(|&atom| atom != 0)
}, },
take_focus: { take_focus: {
Some(unsafe { let name = CString::new("WM_TAKE_FOCUS").unwrap();
let atom_cstr = CString::new("WM_TAKE_FOCUS").unwrap(); XInternAtom(display.get(), name.as_c_str().as_ptr(), 0)
XInternAtom(display.get(), atom_cstr.as_c_str().as_ptr(), 0)
})
.filter(|&atom| atom != 0)
}, },
..Default::default()
} }
} }
}
impl Default for WMAtoms {
fn default() -> Self {
Self {
protocols: None,
delete: None,
active_window: None,
take_focus: None,
}
} }
} }
@ -111,6 +89,12 @@ impl Default for Client {
} }
} }
impl Hash for Client {
fn hash<H: Hasher>(&self, state: &mut H) {
self.window.hash(state);
}
}
impl Client { impl Client {
pub fn new(window: xlib::Window) -> Self { pub fn new(window: xlib::Window) -> Self {
Self { Self {
@ -128,49 +112,27 @@ impl PartialEq for Client {
impl Eq for Client {} impl Eq for Client {}
use std::hash::{BuildHasherDefault, Hasher}; use crate::util::BuildIdentityHasher;
use weak_table::WeakHashSet;
#[derive(Debug, Clone, Copy, Default)]
struct IdentityHasher(usize);
impl Hasher for IdentityHasher {
fn finish(&self) -> u64 {
self.0 as u64
}
fn write(&mut self, _bytes: &[u8]) {
unimplemented!("IdentityHasher only supports usize keys")
}
fn write_u64(&mut self, i: u64) {
self.0 = i as usize;
}
fn write_usize(&mut self, i: usize) {
self.0 = i;
}
}
type BuildIdentityHasher = BuildHasherDefault<IdentityHasher>;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
struct VirtualScreen { struct VirtualScreen {
master_stack: HashMap<Window, Weak<RefCell<Client>>, BuildIdentityHasher>, master_stack: WeakHashSet<Weak<Client>, BuildIdentityHasher>,
aux_stack: HashMap<Window, Weak<RefCell<Client>>, BuildIdentityHasher>, aux_stack: WeakHashSet<Weak<Client>, BuildIdentityHasher>,
focused_client: Weak<RefCell<Client>>, focused_client: Weak<Client>,
} }
impl VirtualScreen { impl VirtualScreen {
fn new() -> Self { fn new() -> Self {
Self { Self {
master_stack: HashMap::default(), master_stack: Default::default(),
aux_stack: HashMap::default(), aux_stack: Default::default(),
focused_client: Weak::new(), focused_client: Weak::new(),
} }
} }
fn contains_window(&self, window: &Window) -> bool { fn contains_client(&self, client: &Rc<Client>) -> bool {
self.master_stack.contains_key(window) || self.aux_stack.contains_key(window) self.master_stack.contains(client) || self.aux_stack.contains(client)
} }
} }
@ -195,10 +157,10 @@ impl XLibState {
}; };
Self { Self {
display: display.clone(), atoms: WMAtoms::init(display.clone()),
display,
root, root,
screen, screen,
atoms: WMAtoms::init(display),
} }
} }
@ -214,7 +176,7 @@ impl XLibState {
self.screen self.screen
} }
pub fn grab_key(&self, window: xlib::Window, keycode: i32, mask: u32) { pub fn grab_key(&self, window: xlib::Window, keycode: i32, mod_mask: u32) {
let numlock_mask = self.numlock_mask(); let numlock_mask = self.numlock_mask();
let modifiers = vec![0, LockMask, numlock_mask, LockMask | numlock_mask]; let modifiers = vec![0, LockMask, numlock_mask, LockMask | numlock_mask];
for &modifier in modifiers.iter() { for &modifier in modifiers.iter() {
@ -222,7 +184,7 @@ impl XLibState {
xlib::XGrabKey( xlib::XGrabKey(
self.dpy(), self.dpy(),
keycode, keycode,
mask | modifier, mod_mask | modifier,
window, window,
1, /* true */ 1, /* true */
GrabModeAsync, GrabModeAsync,
@ -282,13 +244,11 @@ impl XLibState {
return false; return false;
} }
fn send_event(&self, window: xlib::Window, proto: Option<xlib::Atom>) -> bool { fn send_event(&self, window: xlib::Window, proto: xlib::Atom) -> bool {
if proto.is_some() if self.check_for_protocol(window, proto) {
&& self.check_for_protocol(window, proto.unwrap())
&& self.atoms.protocols.is_some()
{
let mut data = xlib::ClientMessageData::default(); let mut data = xlib::ClientMessageData::default();
data.set_long(0, proto.unwrap() as i64); data.set_long(0, proto as i64);
let mut event = XEvent { let mut event = XEvent {
client_message: xlib::XClientMessageEvent { client_message: xlib::XClientMessageEvent {
type_: xlib::ClientMessage, type_: xlib::ClientMessage,
@ -297,7 +257,7 @@ impl XLibState {
send_event: 0, send_event: 0,
window, window,
format: 32, format: 32,
message_type: self.atoms.protocols.unwrap(), message_type: self.atoms.protocols,
data, data,
}, },
}; };
@ -306,10 +266,10 @@ impl XLibState {
xlib::XSendEvent(self.dpy(), window, 0, xlib::NoEventMask, &mut event); xlib::XSendEvent(self.dpy(), window, 0, xlib::NoEventMask, &mut event);
} }
return true; true
} else {
false
} }
return false;
} }
fn numlock_mask(&self) -> u32 { fn numlock_mask(&self) -> u32 {
@ -350,8 +310,8 @@ pub struct WMStateMut {
// u64 : window to move // u64 : window to move
// (i32, i32) : initial window position // (i32, i32) : initial window position
resize_window: Option<(u64, (i32, i32))>, resize_window: Option<(u64, (i32, i32))>,
clients: HashMap<Window, Rc<RefCell<Client>>>, clients: HashSet<Rc<Client>, BuildIdentityHasher>,
focused_client: Weak<RefCell<Client>>, focused_client: Weak<Client>,
current_vscreen: usize, current_vscreen: usize,
virtual_screens: Vec<VirtualScreen>, virtual_screens: Vec<VirtualScreen>,
} }
@ -361,7 +321,7 @@ impl Default for WMStateMut {
Self { Self {
move_window: None, move_window: None,
resize_window: None, resize_window: None,
clients: HashMap::new(), clients: Default::default(),
focused_client: Weak::new(), focused_client: Weak::new(),
current_vscreen: 0, current_vscreen: 0,
virtual_screens: vec![VirtualScreen::new()], virtual_screens: vec![VirtualScreen::new()],
@ -372,111 +332,106 @@ impl Default for WMStateMut {
impl WMStateMut { impl WMStateMut {
fn stack_unstacked_clients(&mut self) { fn stack_unstacked_clients(&mut self) {
info!("[stack_unstacked_clients] "); info!("[stack_unstacked_clients] ");
let current_vscreen = self.current_vscreen;
self.clients self.clients
.iter() .iter()
.filter(|(w, c)| { .filter(|&c| !c.floating && self.is_client_stacked(c))
!c.borrow().floating .for_each(|c| {
&& !self.is_client_stacked(w)
})
.map(|(w, c)| (w.clone(), Rc::downgrade(c)))
.collect::<Vec<(u64, Weak<RefCell<Client>>)>>()
.iter()
.for_each(|(w, c)| {
info!( info!(
"[stack_unstacked_clients] inserting Window({:?}) into aux_stack", "[stack_unstacked_clients] inserting Client({:?}) into aux_stack",
w c
); );
self.virtual_screens[current_vscreen] self.virtual_screens[self.current_vscreen]
.aux_stack .aux_stack
.insert(w.clone(), c.clone()); .insert(c.clone());
}); });
} }
fn is_client_stacked(&self, window: &Window) -> bool { fn is_client_stacked(&self, client: &Rc<Client>) -> bool {
self.virtual_screens self.virtual_screens
.iter() .iter()
.any(|vs| vs.contains_window(window)) .any(|vs| vs.contains_client(client))
} }
fn client_for_window(&self, window: &Window) -> Option<Rc<RefCell<Client>>> { fn client_for_window(&self, window: &Window) -> Option<Rc<Client>> {
self.clients self.clients
.iter() .iter()
.filter(|&(w, _)| *w == *window) .filter(|&c| c.window == *window)
.next() .next()
.map(|(_, c)| c.clone()) .map(|c| c.clone())
} }
fn switch_stack_for_client(&mut self, window: &Window) { fn switch_stack_for_client(&mut self, window: &Window) {
if let Some(client) = self.client_for_window(window) { if let Some(client) = self.client_for_window(window) {
info!("[switch_stack_for_client] client: {:#?}", client.borrow()); info!("[switch_stack_for_client] client: {:#?}", client);
client.borrow_mut().floating = false;
if self.virtual_screens[self.current_vscreen] if self.virtual_screens[self.current_vscreen]
.master_stack .master_stack
.contains_key(window) .contains(&client)
{ {
self.virtual_screens[self.current_vscreen] self.virtual_screens[self.current_vscreen]
.master_stack .master_stack
.remove(window); .remove(&client);
self.virtual_screens[self.current_vscreen] self.virtual_screens[self.current_vscreen]
.aux_stack .aux_stack
.insert(*window, Rc::downgrade(&client)); .insert(client.clone());
info!("[switch_stack_for_client] moved to aux stack"); info!("[switch_stack_for_client] moved to aux stack");
} else { } else {
self.virtual_screens[self.current_vscreen] self.virtual_screens[self.current_vscreen]
.aux_stack .aux_stack
.remove(window); .remove(&client);
self.virtual_screens[self.current_vscreen] self.virtual_screens[self.current_vscreen]
.master_stack .master_stack
.insert(*window, Rc::downgrade(&client)); .insert(client.clone());
info!("[switch_stack_for_client] moved to master stack"); info!("[switch_stack_for_client] moved to master stack");
} }
self.clients.replace(Rc::new(Client {
floating: false,
..*client
}));
} }
} }
fn refresh_screen(&mut self) { fn refresh_screen(&mut self) {
let current_vscreen = self.current_vscreen; 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(); self.stack_unstacked_clients();
if self.virtual_screens[current_vscreen] if let Some(vs) = self.virtual_screens.get_mut(self.current_vscreen) {
.master_stack vs.master_stack.retain(|c| !c.floating);
.is_empty() 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:"); info!("[refresh_screen] master stack was empty, pushing first client if exists:");
vs.aux_stack.iter().filter(|c| !c.floating).next().map(|c| {
self.virtual_screens[current_vscreen] info!("[arrange_clients] Client({:#?})", c);
.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] self.virtual_screens[current_vscreen]
.master_stack .master_stack
.insert(w, c); .insert(c.clone());
self.virtual_screens[current_vscreen].aux_stack.remove(&w) self.virtual_screens[current_vscreen].aux_stack.remove(&c);
}); });
} }
} }
}
}
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 { pub struct WMState {
@ -532,10 +487,9 @@ impl WMState {
.add_key_handler("M", Mod1Mask, Self::handle_switch_stack) .add_key_handler("M", Mod1Mask, Self::handle_switch_stack)
.add_key_handler("Q", Mod1Mask, |state, event| unsafe { .add_key_handler("Q", Mod1Mask, |state, event| unsafe {
if event.key.subwindow != 0 { if event.key.subwindow != 0 {
if state.xlib_state.atoms.delete.is_none() if !state
|| !state
.xlib_state .xlib_state
.send_event(event.key.subwindow, state.xlib_state.atoms.delete) .send_event(event.key.subwindow, state.xlib_state.atoms.delete_window)
{ {
xlib::XKillClient(state.dpy(), event.key.subwindow); xlib::XKillClient(state.dpy(), event.key.subwindow);
} }
@ -659,28 +613,26 @@ impl WMState {
let _ = Some(self.mut_state.borrow_mut()) let _ = Some(self.mut_state.borrow_mut())
.and_then(|mut state| { .and_then(|mut state| {
if !state.clients.contains_key(&event.window) { if state.client_for_window(&event.window).is_none() {
info!("[MapRequest] new client: {:#?}", event.window); info!("[MapRequest] new client: {:#?}", event.window);
Some( let client = Rc::new(Client::new(event.window));
state state.clients.insert(client.clone());
.clients
.entry(event.window) Some(client)
.or_insert_with(|| Rc::new(RefCell::new(Client::new(event.window))))
.clone(),
)
} else { } else {
None None
} }
}) })
.and_then(|c| { .and_then(|c| {
unsafe { unsafe {
xlib::XMapWindow(self.dpy(), c.borrow().window); xlib::XMapWindow(self.dpy(), c.window);
xlib::XSelectInput( xlib::XSelectInput(
self.dpy(), self.dpy(),
event.window, event.window,
EnterWindowMask EnterWindowMask
| FocusChangeMask | PropertyChangeMask | FocusChangeMask
| PropertyChangeMask
| StructureNotifyMask, | StructureNotifyMask,
); );
} }
@ -688,12 +640,8 @@ impl WMState {
self.buttons self.buttons
.iter() .iter()
.for_each(|&(button, mod_mask, button_mask)| { .for_each(|&(button, mod_mask, button_mask)| {
self.xlib_state.grab_button( self.xlib_state
c.borrow().window, .grab_button(c.window, button, mod_mask, button_mask);
button,
mod_mask,
button_mask,
);
}); });
self.arrange_clients(); self.arrange_clients();
@ -708,7 +656,7 @@ impl WMState {
if event.send_event == 0 { if event.send_event == 0 {
let _ = Some(self.mut_state.borrow_mut()).and_then(|mut state| { let _ = Some(self.mut_state.borrow_mut()).and_then(|mut state| {
if state.clients.contains_key(&event.window) { if state.client_for_window(&event.window).is_none() {
let client = state.clients.remove(&event.window); let client = state.clients.remove(&event.window);
info!("[UnmapNotify] removing client: {:#?}", client); info!("[UnmapNotify] removing client: {:#?}", client);
} }
@ -833,15 +781,11 @@ impl WMState {
xlib::CurrentTime, xlib::CurrentTime,
); );
xlib::XDeleteProperty( xlib::XDeleteProperty(self.dpy(), self.root(), self.xlib_state.atoms.active_window);
self.dpy(),
self.root(),
self.xlib_state.atoms.active_window.unwrap(),
);
} }
} }
fn focus_client(&self, client: Rc<RefCell<Client>>) { fn focus_client(&self, client: Rc<Client>) {
Some(self.mut_state.borrow_mut()).and_then(|mut state| { Some(self.mut_state.borrow_mut()).and_then(|mut state| {
let current_vscreen = state.current_vscreen; let current_vscreen = state.current_vscreen;
@ -868,7 +812,7 @@ impl WMState {
xlib::XChangeProperty( xlib::XChangeProperty(
self.dpy(), self.dpy(),
self.root(), self.root(),
self.xlib_state.atoms.active_window.unwrap(), self.xlib_state.atoms.active_window,
xlib::XA_WINDOW, xlib::XA_WINDOW,
32, 32,
xlib::PropModeReplace, xlib::PropModeReplace,
@ -1028,7 +972,6 @@ impl WMState {
assert!(direction == 1 || direction == -1); assert!(direction == 1 || direction == -1);
Some(self.mut_state.borrow_mut()).and_then(|mut state| { Some(self.mut_state.borrow_mut()).and_then(|mut state| {
// hide all windows from current virtual screen // hide all windows from current virtual screen
state.virtual_screens[state.current_vscreen] state.virtual_screens[state.current_vscreen]
.master_stack .master_stack