reworked Client list
This commit is contained in:
parent
5823c9ae66
commit
81536fb52c
|
@ -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"
|
|
@ -1,3 +1,2 @@
|
|||
hard_tabs = true
|
||||
imports_granularity = "Crate"
|
||||
wrap_comments = true
|
|
@ -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
24
src/util.rs
Normal 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
279
src/wm.rs
|
@ -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,56 +39,34 @@ 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,111 +332,106 @@ 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 {
|
||||
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue