hopefully Client-Logic that actually works now

This commit is contained in:
noonebtw 2021-04-23 01:59:46 +02:00
parent 5608f1d9da
commit 08a0c6b089
3 changed files with 756 additions and 126 deletions

View file

@ -10,5 +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 = {path = "/mnt/storage/rust/weak-table-rs"} weak-table = "0.3.0"
simple_logger = "1.11.0" simple_logger = "1.11.0"

View file

@ -1,15 +1,9 @@
use std::{ use std::{borrow::Borrow, cell::RefCell, collections::HashMap, rc::Rc};
borrow::{Borrow, BorrowMut},
collections::HashSet,
ops::{Deref, DerefMut},
rc::Rc,
};
use std::{ use std::{
hash::{Hash, Hasher}, hash::{Hash, Hasher},
rc::Weak, rc::Weak,
}; };
use weak_table::WeakHashSet;
use x11::xlib::Window; use x11::xlib::Window;
use crate::util::BuildIdentityHasher; use crate::util::BuildIdentityHasher;
@ -36,6 +30,12 @@ impl PartialEq for Client {
impl Eq for Client {} impl Eq for Client {}
impl Borrow<Window> for Client {
fn borrow(&self) -> &Window {
&self.window
}
}
trait ClientKey { trait ClientKey {
fn key(&self) -> u64; fn key(&self) -> u64;
} }
@ -84,135 +84,348 @@ impl<'a> Borrow<dyn ClientKey + 'a> for Rc<Client> {
} }
} }
trait ClientList {
fn contains_key<T>(&self, key: &T) -> bool
where
T: ClientKey;
fn get_with_key<T>(&self, key: &T) -> Option<Rc<Client>>
where
T: ClientKey;
fn remove_key<T>(&mut self, key: &T) -> bool
where
T: ClientKey;
}
struct Clients(HashSet<Rc<Client>, BuildIdentityHasher>);
impl Default for Clients {
fn default() -> Self {
Self(Default::default())
}
}
impl Deref for Clients {
type Target = HashSet<Rc<Client>, BuildIdentityHasher>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for Clients {
fn deref_mut(&mut self) -> &mut HashSet<Rc<Client>, BuildIdentityHasher> {
&mut self.0
}
}
impl ClientList for Clients {
fn contains_key<T>(&self, key: &T) -> bool
where
T: ClientKey,
{
self.0.contains(key as &dyn ClientKey)
}
fn get_with_key<T>(&self, key: &T) -> Option<Rc<Client>>
where
T: ClientKey,
{
self.0.get(key as &dyn ClientKey).cloned()
}
fn remove_key<T>(&mut self, key: &T) -> bool
where
T: ClientKey,
{
self.0.remove(key as &dyn ClientKey)
}
}
struct ClientRefs(WeakHashSet<Weak<Client>, BuildIdentityHasher>);
impl Default for ClientRefs {
fn default() -> Self {
Self(Default::default())
}
}
impl Deref for ClientRefs {
type Target = WeakHashSet<Weak<Client>, BuildIdentityHasher>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for ClientRefs {
fn deref_mut(&mut self) -> &mut WeakHashSet<Weak<Client>, BuildIdentityHasher> {
&mut self.0
}
}
impl ClientList for ClientRefs {
fn contains_key<T>(&self, key: &T) -> bool
where
T: ClientKey,
{
self.0.contains(key as &dyn ClientKey)
}
fn get_with_key<T>(&self, key: &T) -> Option<Rc<Client>>
where
T: ClientKey,
{
self.0.get(key as &dyn ClientKey)
}
fn remove_key<T>(&mut self, key: &T) -> bool
where
T: ClientKey,
{
self.0.remove(key as &dyn ClientKey)
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
#[test] #[test]
fn client_lists_test() { fn client_lists_test() {}
let mut clients: Clients = Default::default(); }
clients.insert(Rc::new(Client { mod no_refcell {
window: 1, use std::{collections::VecDeque, iter::repeat};
floating: false,
position: (1, 1),
size: (1, 1),
}));
assert!(clients.contains_key(&1u64)); use super::*;
let mut client_refs = ClientRefs::default(); type ClientsWrapped = Rc<RefCell<Clients>>;
type Clients = HashMap<Window, Client, BuildIdentityHasher>;
type ClientRef = u64;
type ClientRefs = Vec<ClientRef>;
client_refs.insert(clients.get_with_key(&1u64).unwrap()); struct ClientState {
clients: Clients,
virtual_screens: VecDeque<VirtualScreen>,
}
assert!(client_refs.contains_key(&1u64)); struct VirtualScreen {
master: ClientRefs,
aux: ClientRefs,
focused: Option<ClientRef>,
}
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<K>(&self, key: &K) -> Option<&Client>
where
K: ClientKey,
{
self.clients.get(&key.key())
}
fn get_mut<K>(&mut self, key: &K) -> Option<&mut Client>
where
K: ClientKey,
{
self.clients.get_mut(&key.key())
}
fn toggle_floating<K>(&mut self, key: &K) -> Option<bool>
where
K: ClientKey,
{
match self.get_mut(key) {
Some(client) => {
client.floating = !client.floating;
Some(client.floating)
}
None => None,
}
}
fn get_virtualscreen_for_client<K>(&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<K>(&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<K>(&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::<Vec<_>>();
match self.virtual_screens.front_mut() {
Some(vs) => vs.aux.extend(unstacked.into_iter()),
None => {}
}
}
fn switch_stack_for_client<K>(&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<K>(&self, key: &K) -> bool
where
K: ClientKey,
{
self.master.contains(&key.key()) || self.aux.contains(&key.key())
}
fn focus<K>(&mut self, key: &K)
where
K: ClientKey,
{
self.focused = Some(key.key());
}
} }
} }
/*
mod refcell {
use std::collections::VecDeque;
use super::*;
type ClientsWrapped = Rc<RefCell<Clients>>;
type Clients = HashMap<Window, Rc<RefCell<Client>>, BuildIdentityHasher>;
type ClientRef = Weak<RefCell<Client>>;
type ClientRefs = Vec<ClientRef>;
struct ClientState {
clients: Clients,
virtual_screens: VecDeque<VirtualScreen>,
}
struct VirtualScreen {
master: ClientRefs,
aux: ClientRefs,
focused: Option<ClientRef>,
}
impl ClientState {
fn insert(&mut self, client: Client) {
let key = client.key();
self.clients.insert(key, Rc::new(RefCell::new(client)));
}
fn get<K>(&self, key: &K) -> Option<&Rc<RefCell<Client>>>
where
K: ClientKey,
{
self.clients.get(&key.key())
}
fn toggle_floating<K>(&mut self, key: &K) -> Option<bool>
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<K>(&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<K>(&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<K>(&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::<Vec<_>>();
match self.virtual_screens.front_mut() {
Some(vs) => vs.aux.extend(unstacked.into_iter()),
None => {}
}
}
fn arrange(&mut self) {}
}
impl VirtualScreen {
fn contains<K>(&self, key: &K) -> bool
where
K: ClientKey,
{
self.master.contains(&key.key()) || self.aux.contains(&key.key())
}
fn focus<K>(&mut self, key: &K)
where
K: ClientKey,
{
self.focused = Some(key.key());
}
}
}
*/

417
src/state.rs Normal file
View file

@ -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<Window, Weak<RefCell<Client>>, BuildIdentityHasher>,
aux_stack: HashMap<Window, Weak<RefCell<Client>>, BuildIdentityHasher>,
focused_client: Weak<RefCell<Client>>,
}
impl VirtualScreen {
fn new() -> Self {
Self {
master_stack: HashMap::default(),
aux_stack: HashMap::default(),
focused_client: Weak::new(),
}
}
fn contains_client(&self, client: Rc<RefCell<Client>>) -> 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<xlib::Atom>) -> 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<dyn Fn(&Self, &XEvent)>)>,
// (button, mod_mask, button_mask)
buttons: Vec<(u32, u32, i64)>,
event_handlers: Vec<Rc<dyn Fn(&Self, &XEvent)>>,
//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<Window, Rc<RefCell<Client>>>,
focused_client: Weak<RefCell<Client>>,
current_vscreen: usize,
virtual_screens: Vec<VirtualScreen>,
}
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::<Vec<(u64, Weak<RefCell<Client>>)>>()
.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<Rc<RefCell<Client>>> {
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)
});
}
}
}