refactoring; Arc -> Rc; basic tiling functionality

This commit is contained in:
noonebtw 2021-01-19 01:28:50 +01:00
parent 86fc396a84
commit 80fdb915ab
4 changed files with 1022 additions and 874 deletions

View file

@ -9,3 +9,4 @@ edition = "2018"
[dependencies]
nix = "0.19.1"
x11 = {version = "2.18.2", features = ["xlib"] }
log = "0.4.13"

3
rustfmt.toml Normal file
View file

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

528
src/wm.rs
View file

@ -1,25 +1,26 @@
// asdf
use std::{
cell::{Cell, RefCell, RefMut},
cell::{RefCell, RefMut},
collections::{hash_map::Entry, HashMap},
ffi::CString,
io::{Error, ErrorKind, Result},
ptr::{null, null_mut},
rc::{Rc, Weak as WeakRc},
sync::{Arc, Weak},
rc::{Rc, Weak},
};
use x11::{
xlib,
xlib::{
Atom, ButtonPressMask, ButtonReleaseMask, CWEventMask, ControlMask, EnterWindowMask,
FocusChangeMask, GrabModeAsync, LeaveWindowMask, LockMask, Mod1Mask, Mod2Mask, Mod3Mask,
Mod4Mask, Mod5Mask, PointerMotionMask, PropertyChangeMask, ShiftMask, StructureNotifyMask,
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)]
@ -91,26 +92,19 @@ impl Default for WMAtoms {
}
}
#[derive(Clone, Copy, Debug)]
pub struct ClientState {
size: (u32, u32),
position: (i32, i32),
}
#[derive(Clone, Debug)]
pub struct Client {
window: Window,
state: Cell<ClientState>,
size: (i32, i32),
position: (i32, i32),
}
impl Default for Client {
fn default() -> Self {
Self {
window: 0,
state: Cell::new(ClientState {
size: (0, 0),
position: (0, 0),
}),
}
}
}
@ -122,10 +116,6 @@ impl Client {
..Default::default()
}
}
pub fn get_state(&self) -> ClientState {
self.state.get()
}
}
impl PartialEq for Client {
@ -312,8 +302,9 @@ pub struct WMStateMut {
// u64 : window to move
// (i32, i32) : initial window position
resize_window: Option<(u64, (i32, i32))>,
clients: HashMap<Window, Rc<Client>>,
focused_client: WeakRc<Client>,
clients: HashMap<Window, Rc<RefCell<Client>>>,
focused_client: Weak<RefCell<Client>>,
master_stack: Vec<Weak<RefCell<Client>>>,
}
impl Default for WMStateMut {
@ -322,17 +313,18 @@ impl Default for WMStateMut {
move_window: None,
resize_window: None,
clients: HashMap::new(),
focused_client: WeakRc::new(),
focused_client: Weak::new(),
master_stack: vec![],
}
}
}
pub struct WMState {
xlib_state: XLibState,
key_handlers: Vec<(i32, u32, Arc<dyn Fn(&Self, &XEvent)>)>,
key_handlers: Vec<(i32, u32, Rc<dyn Fn(&Self, &XEvent)>)>,
// (button, mod_mask, button_mask)
buttons: Vec<(u32, u32, i64)>,
event_handlers: Vec<Arc<dyn Fn(&Self, &XEvent)>>,
event_handlers: Vec<Rc<dyn Fn(&Self, &XEvent)>>,
mut_state: RefCell<WMStateMut>,
}
@ -354,17 +346,33 @@ impl WMState {
Mod1Mask,
ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
)
.add_event_handler(Self::handle_move_window)
//.add_event_handler(Self::handle_move_window)
.grab_button(
3,
Mod1Mask,
ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
)
.add_event_handler(Self::handle_resize_window)
//.add_event_handler(Self::handle_resize_window)
.add_key_handler("T", Mod1Mask, |state, _| {
println!("spawning terminal");
let _ = state.spawn("xterm", &[]);
})
.add_key_handler("M", Mod1Mask, |state, event| {
Some(state.mut_state.borrow_mut()).and_then(|mut mstate| {
(mstate
.clients
.iter()
.filter(|&(_, c)| c.borrow().window == unsafe { event.button.subwindow })
.next()
.and_then(|(_, c)| Some(c.clone())))
.and_then(|c| {
mstate.master_stack.push(Rc::downgrade(&c));
Some(())
})
});
state.arrange_clients();
})
.add_key_handler("L", Mod1Mask, |state, _| {
println!("{:#?}", state.mut_state.borrow());
})
@ -419,153 +427,22 @@ impl WMState {
match event.get_type() {
xlib::MapRequest => {
let event = unsafe { &event.map_request };
let _ = self
.mut_state
.try_borrow_mut()
.ok()
.and_then(|mut m| {
if !m.clients.contains_key(&event.window) {
Some(
m.clients
.entry(event.window)
.or_insert_with(|| Rc::new(Client::new(event.window)))
.clone(),
)
} else {
None
}
})
.and_then(|c| {
unsafe { xlib::XMapWindow(self.dpy(), c.window) };
unsafe {
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(
c.window,
button,
mod_mask,
button_mask,
);
});
self.arrange_clients();
self.focus_client(c);
Some(())
});
self.map_request(unsafe { &event.map_request });
}
xlib::UnmapNotify => {
let event = unsafe { &event.unmap };
println!("UnmapNotify: {:?}", event.window);
if event.send_event == 0 {
let _ = self.mut_state.try_borrow_mut().and_then(|mut_state| {
if mut_state.clients.contains_key(&event.window) {
RefMut::map(mut_state, |t| &mut t.clients).remove(&event.window);
}
Ok(())
});
}
self.arrange_clients();
self.unmap_notify(unsafe { &event.unmap });
}
xlib::DestroyNotify => {
let event = unsafe { &event.destroy_window };
println!("DestroyNotify: {:?}", event.window);
let _ = self.mut_state.try_borrow_mut().and_then(|mut_state| {
if mut_state.clients.contains_key(&event.window) {
RefMut::map(mut_state, |t| &mut t.clients).remove(&event.window);
}
Ok(())
});
self.arrange_clients();
self.destroy_notify(unsafe { &event.destroy_window });
}
xlib::ConfigureRequest => {
let event = unsafe { &event.configure_request };
match self.mut_state.borrow_mut().clients.entry(event.window) {
Entry::Occupied(entry) => {
self.configure_client(entry.get().clone());
}
_ => {
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,
);
}
}
}
self.configure_request(unsafe { &event.configure_request });
}
xlib::EnterNotify => {
let event = unsafe { &event.crossing };
println!("EnterNotify: {:?}", event.window);
match self.mut_state.try_borrow().ok().and_then(|mut_state| {
mut_state
.clients
.get(&event.window)
.and_then(|c| Some(c.clone()))
}) {
Some(client) => {
self.focus_client(client);
}
None => {}
};
self.enter_notify(unsafe { &event.crossing });
}
xlib::ButtonPress => {
let event = unsafe { &event.button };
match self.mut_state.try_borrow().ok().and_then(|mut_state| {
mut_state
.clients
.get(&event.subwindow)
.and_then(|c| Some(c.clone()))
}) {
Some(client) => {
self.focus_client(client.clone());
println!("raising window {:?}", client.window);
unsafe {
xlib::XRaiseWindow(self.dpy(), client.window);
xlib::XSync(self.dpy(), 0);
}
}
None => {}
};
self.button_press(unsafe { &event.button });
}
xlib::KeyPress => {
let clean_mask = self.xlib_state.clean_mask();
@ -600,11 +477,12 @@ impl WMState {
self
}
#[allow(dead_code)]
pub fn add_event_handler<F>(mut self, handler: F) -> Self
where
F: Fn(&Self, &XEvent) + 'static,
{
self.event_handlers.push(Arc::new(handler));
self.event_handlers.push(Rc::new(handler));
self
}
@ -616,17 +494,187 @@ impl WMState {
{
let keycode = self.xlib_state.keycode(key);
self.key_handlers.push((keycode, mask, Arc::new(handler)));
self.key_handlers.push((keycode, mask, Rc::new(handler)));
self.xlib_state.grab_key(self.root(), keycode, mask);
self
}
fn unfocus_client(&self, client: Rc<Client>) {
fn map_request(&self, event: &xlib::XMapRequestEvent) {
info!("[MapRequest] event: {:#?}", event);
let _ = Some(self.mut_state.borrow_mut())
.and_then(|mut state| {
if !state.clients.contains_key(&event.window) {
info!("[MapRequest] new client: {:#?}", event.window);
Some(
state
.clients
.entry(event.window)
.or_insert_with(|| Rc::new(RefCell::new(Client::new(event.window))))
.clone(),
)
} else {
None
}
})
.and_then(|c| {
unsafe {
xlib::XMapWindow(self.dpy(), c.borrow().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(
c.borrow().window,
button,
mod_mask,
button_mask,
);
});
self.arrange_clients();
self.focus_client(c.clone());
Some(())
});
}
fn unmap_notify(&self, event: &xlib::XUnmapEvent) {
info!("[UnmapNotify] event: {:#?}", event);
if event.send_event == 0 {
let _ = Some(self.mut_state.borrow_mut()).and_then(|mut state| {
if state.clients.contains_key(&event.window) {
let client = state.clients.remove(&event.window);
info!("[UnmapNotify] removing client: {:#?}", client);
}
Some(())
});
}
self.arrange_clients();
}
fn destroy_notify(&self, event: &xlib::XDestroyWindowEvent) {
info!("[DestroyNotify] event: {:?}", event);
let _ = Some(self.mut_state.borrow_mut()).and_then(|mut state| {
let _entry = state.clients.remove(&event.window);
info!("[DestroyNotify] removed entry: {:?}", _entry);
Some(())
});
self.arrange_clients();
}
fn configure_request(&self, event: &xlib::XConfigureRequestEvent) {
info!("[ConfigureRequest] event: {:?}", event);
match self.mut_state.borrow_mut().clients.entry(event.window) {
Entry::Occupied(entry) => {
info!(
"[ConfigureRequest] found Client for Window({:?}): {:#?}",
event.window,
entry.get()
);
self.configure_client(entry.get().clone());
}
_ => {
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(&self, event: &xlib::XCrossingEvent) {
info!("[EnterNotify] event: {:?}", event);
Some(self.mut_state.borrow())
.and_then(|state| {
state
.clients
.get(&event.window)
.and_then(|c| Some(c.clone()))
})
.and_then(|c| {
info!(
"[EnterNotify] focusing Client for Window({:?})",
event.window
);
self.focus_client(c);
Some(())
});
}
fn button_press(&self, event: &xlib::XButtonEvent) {
info!("[ButtonPress] event: {:?}", event);
Some(self.mut_state.borrow())
.and_then(|state| {
state
.clients
.get(&event.subwindow)
.and_then(|c| Some(c.clone()))
})
.and_then(|c| {
info!(
"[ButtonPress] focusing Client for Window({:?})",
event.window
);
self.focus_client(c.clone());
info!("[ButtonPress] raising Window({:?})", event.window);
unsafe {
xlib::XRaiseWindow(self.dpy(), c.borrow().window);
xlib::XSync(self.dpy(), 0);
}
Some(())
});
}
fn unfocus_client(&self, client: Rc<RefCell<Client>>) {
unsafe {
xlib::XSetInputFocus(
self.dpy(),
client.window,
client.borrow().window,
xlib::RevertToPointerRoot,
xlib::CurrentTime,
);
@ -639,7 +687,7 @@ impl WMState {
}
}
fn focus_client(&self, client: Rc<Client>) {
fn focus_client(&self, client: Rc<RefCell<Client>>) {
let _ = self.mut_state.try_borrow_mut().and_then(|m| {
let mut focused_client = RefMut::map(m, |m| &mut m.focused_client);
match focused_client.upgrade() {
@ -657,7 +705,7 @@ impl WMState {
unsafe {
xlib::XSetInputFocus(
self.dpy(),
client.window,
client.borrow().window,
xlib::RevertToPointerRoot,
xlib::CurrentTime,
);
@ -669,16 +717,18 @@ impl WMState {
xlib::XA_WINDOW,
32,
xlib::PropModeReplace,
&client.window as *const u64 as *const _,
&client.borrow().window as *const u64 as *const _,
1,
);
}
self.xlib_state
.send_event(client.window, self.xlib_state.atoms.take_focus);
.send_event(client.borrow().window, self.xlib_state.atoms.take_focus);
}
fn arrange_clients(&self) {
self.refresh_stack();
let (screen_w, screen_h) = unsafe {
(
xlib::XDisplayWidth(self.dpy(), self.xlib_state.screen()),
@ -686,33 +736,106 @@ impl WMState {
)
};
let clients_num = self.mut_state.borrow().clients.len() as i32;
Some(self.mut_state.borrow_mut()).and_then(|mut state| {
// no need to arrange any clients if there is no clients
if !state.clients.is_empty() {
// if master stack is empty, populate with first entry in clients list
if state.master_stack.is_empty() {
let first_client = Rc::downgrade(state.clients.iter().next().unwrap().1);
state.master_stack.push(first_client);
}
if clients_num != 0 {
let window_w = screen_w / clients_num;
let window_w = {
let has_aux_stack = state
.clients
.iter()
.filter(|&(_, client)| {
state
.master_stack
.iter()
.filter(|weak_client| weak_client.upgrade().unwrap() != *client)
.count() != 0
})
.count() != 0;
for (i, (_, client)) in self.mut_state.borrow().clients.iter().enumerate() {
let client_state = ClientState {
size: (window_w as u32, screen_h as u32),
position: (window_w * i as i32, 0),
if has_aux_stack {
screen_w / 2
} else {
screen_w
}
};
client.state.replace(client_state);
for (i, weak_client) in state.master_stack.iter().enumerate() {
let client = weak_client.upgrade().unwrap();
let mut wc = xlib::XWindowChanges {
x: client_state.position.0,
y: client_state.position.1,
width: client_state.size.0 as i32,
height: client_state.size.1 as i32,
let mut wc = {
let mut client = client.borrow_mut();
let window_h = screen_h / state.master_stack.len() as i32;
client.size = (window_w, window_h);
client.position = (0, window_h * i as i32);
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,
client.borrow().window,
(xlib::CWY | xlib::CWX | xlib::CWHeight | xlib::CWWidth) as u32,
&mut wc,
);
self.configure_client(client.clone());
xlib::XSync(self.dpy(), 0);
}
}
// filter only windows that arent inthe master stack, essentially aux stack
for (i, (_, client)) in state
.clients
.iter()
.filter(|&(_, c)| {
state
.master_stack
.iter()
.filter(|w| w.upgrade().unwrap() == *c)
.count() == 0
})
.enumerate()
{
let mut wc = {
let mut client = client.borrow_mut();
let window_h =
screen_h / (state.clients.len() - state.master_stack.len()) as i32;
client.size = (window_w, window_h);
client.position = (window_w, window_h * i as i32);
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.borrow().window,
(xlib::CWY | xlib::CWX | xlib::CWHeight | xlib::CWWidth) as u32,
&mut wc,
);
@ -723,31 +846,52 @@ impl WMState {
}
}
}
Some(())
});
}
fn configure_client(&self, client: Rc<Client>) {
let client_state = client.get_state();
fn refresh_stack(&self) {
Some(self.mut_state.borrow_mut()).and_then(|mut state| {
state.master_stack = state
.master_stack
.iter()
.filter_map(|weak_client| {
weak_client
.upgrade()
.and_then(|_| Some(weak_client.clone()))
})
.collect();
let mut event = xlib::XConfigureEvent {
Some(())
});
}
fn configure_client(&self, client: Rc<RefCell<Client>>) {
let mut event = {
let client = client.borrow();
xlib::XConfigureEvent {
type_: xlib::ConfigureNotify,
display: self.dpy(),
event: client.window,
window: client.window,
x: client_state.position.0,
y: client_state.position.1,
width: client_state.size.0 as i32,
height: client_state.size.1 as i32,
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(),
client.window,
event.window,
0,
StructureNotifyMask,
&mut event as *mut _ as *mut XEvent,

BIN
vm-ss.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB