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"
x11 = {version = "2.18.2", features = ["xlib"] }
log = "0.4.13"
weak-table = "0.3.0"
simple_logger = "1.11.0"

View file

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

View file

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