This commit is contained in:
noonebtw 2021-04-25 07:45:18 +02:00
parent 7d5aae9cbe
commit 4235fff343
5 changed files with 153 additions and 110 deletions

2
.gitignore vendored
View file

@ -1,3 +1,5 @@
/target /target
*~ *~
/Cargo.lock /Cargo.lock
/.cargo/
/wmlog

View file

@ -7,8 +7,6 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
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

@ -3,6 +3,8 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::num::NonZeroI32; use std::num::NonZeroI32;
use log::{debug, error};
use crate::util::BuildIdentityHasher; use crate::util::BuildIdentityHasher;
mod client { mod client {
@ -282,7 +284,7 @@ impl ClientState {
self.clients.insert(key, client); self.clients.insert(key, client);
if let Some(vs) = self.virtual_screens.front_mut() { if let Some(vs) = self.virtual_screens.front_mut() {
vs.aux.push(key); vs.insert(&key);
} }
self.focus_client(&key); self.focus_client(&key);
@ -373,13 +375,13 @@ impl ClientState {
/** /**
Sets a tiled client to floating and returns true, does nothing for a floating client and Sets a tiled client to floating and returns true, does nothing for a floating client and
returns false. returns false. If this function returns `true` you have to call `arrange_clients` after.
*/ */
pub fn set_floating<K>(&mut self, key: &K) -> bool pub fn set_floating<K>(&mut self, key: &K) -> bool
where where
K: ClientKey, K: ClientKey,
{ {
if self.get(key).is_floating() { if self.get(key).is_tiled() {
self.toggle_floating(key); self.toggle_floating(key);
true true
@ -388,6 +390,10 @@ impl ClientState {
} }
} }
/**
This function invalidates the tiling, call `arrange_clients` to fix it again (it doesn't do it
automatically since xlib has to move and resize all windows anyways).
*/
pub fn toggle_floating<K>(&mut self, key: &K) pub fn toggle_floating<K>(&mut self, key: &K)
where where
K: ClientKey, K: ClientKey,
@ -404,10 +410,12 @@ impl ClientState {
(None, Some(client)) => { (None, Some(client)) => {
self.clients.insert(key, client); self.clients.insert(key, client);
if let Some(vs) = self.virtual_screens.front_mut() { if let Some(vs) = self.virtual_screens.front_mut() {
vs.aux.push(key); vs.insert(&key);
} }
} }
_ => {} _ => {
error!("wtf? Client was present in tiled and floating list.")
}
}; };
} }
@ -497,38 +505,12 @@ impl ClientState {
} }
} }
/**
This shouldn't be ever needed to be called since any client added is automatically added
to the first `VirtualScreen`.
*/
#[deprecated]
fn stack_unstacked(&mut self) {
let unstacked = self
.clients
.iter()
.filter(|&(key, _)| self.get_virtualscreen_for_client(key).is_none())
.map(|(key, _)| key)
.collect::<Vec<_>>();
if let Some(vs) = self.virtual_screens.front_mut() {
vs.aux.extend(unstacked.into_iter());
}
}
pub fn switch_stack_for_client<K>(&mut self, key: &K) pub fn switch_stack_for_client<K>(&mut self, key: &K)
where where
K: ClientKey, K: ClientKey,
{ {
if let Some(vs) = self.get_mut_virtualscreen_for_client(key) { if let Some(vs) = self.get_mut_virtualscreen_for_client(key) {
match vs.master.iter().position(|&key| key == key.key()) { vs.switch_stack_for_client(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));
}
}
} }
} }
@ -554,7 +536,7 @@ impl ClientState {
// should be fine to unwrap since we will always have at least 1 virtual screen // 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 let Some(vs) = self.virtual_screens.front_mut() {
// if aux is empty -> width : width / 2 // if aux is empty -> width : width / 2
let width = width / (1 + i32::from(!vs.aux.is_empty() && !vs.master.is_empty())); let width = width / (1 + i32::from(!vs.aux.is_empty()));
// make sure we dont devide by 0 // make sure we dont devide by 0
let master_height = height let master_height = height
@ -583,7 +565,7 @@ impl ClientState {
.zip(repeat(aux_height).zip(repeat(width))), .zip(repeat(aux_height).zip(repeat(width))),
) )
{ {
let size = (width + gap * 2, height + gap * 2); let size = (width - gap * 2, height - gap * 2);
let position = (x + gap, height * i as i32 + gap); let position = (x + gap, height * i as i32 + gap);
if let Some(client) = self.clients.get_mut(key) { if let Some(client) = self.clients.get_mut(key) {
@ -595,6 +577,8 @@ impl ClientState {
} }
} }
} }
debug!("{:#?}", self);
} }
// Should have xlib send those changes back to the x server after this function // Should have xlib send those changes back to the x server after this function
@ -617,6 +601,15 @@ impl VirtualScreen {
self.master.contains(&key.key()) || self.aux.contains(&key.key()) self.master.contains(&key.key()) || self.aux.contains(&key.key())
} }
fn insert<K>(&mut self, key: &K)
where
K: ClientKey,
{
self.aux.push(key.key());
self.refresh();
}
fn remove<K>(&mut self, key: &K) fn remove<K>(&mut self, key: &K)
where where
K: ClientKey, K: ClientKey,
@ -628,6 +621,23 @@ impl VirtualScreen {
self.refresh(); self.refresh();
} }
fn switch_stack_for_client<K>(&mut self, key: &K)
where
K: ClientKey,
{
match self.master.iter().position(|&k| k == key.key()) {
Some(index) => {
self.aux.extend(self.master.drain(index..=index));
}
None => {
let index = self.aux.iter().position(|&k| k == key.key()).unwrap();
self.master.extend(self.aux.drain(index..=index));
}
}
self.refresh();
}
/** /**
if `self.master` is empty but `self.aux` has at least one client, drain from aux to master if `self.master` is empty but `self.aux` has at least one client, drain from aux to master
this ensures that if only 1 `Client` is on this `VirtualScreen` it will be on the master stack this ensures that if only 1 `Client` is on this `VirtualScreen` it will be on the master stack

View file

@ -3,7 +3,10 @@ use std::rc::Rc;
use log::{error, info}; use log::{error, info};
use x11::xlib::{self, ShiftMask, Window, XButtonEvent, XEvent, XKeyEvent, XMotionEvent}; use x11::xlib::{self, ShiftMask, Window, XButtonEvent, XEvent, XKeyEvent, XMotionEvent};
use xlib::{ButtonPressMask, ButtonReleaseMask, Mod1Mask, PointerMotionMask}; use xlib::{
ButtonPressMask, ButtonReleaseMask, Mod1Mask, PointerMotionMask, XConfigureRequestEvent,
XCrossingEvent, XDestroyWindowEvent, XMapRequestEvent, XUnmapEvent,
};
use crate::{ use crate::{
clients::{Client, ClientKey, ClientState}, clients::{Client, ClientKey, ClientState},
@ -19,6 +22,7 @@ pub struct WindowManager {
xlib: XLib, xlib: XLib,
} }
#[derive(Debug)]
pub enum Direction { pub enum Direction {
Left, Left,
Right, Right,
@ -38,7 +42,7 @@ struct KeyBinding {
impl WindowManager { impl WindowManager {
pub fn new() -> Self { pub fn new() -> Self {
let clients = ClientState::with_virtualscreens(3); let clients = ClientState::with_virtualscreens(3);
let xlib = XLib::new().init(); let xlib = XLib::new();
Self { Self {
clients, clients,
@ -82,7 +86,7 @@ impl WindowManager {
)); ));
self.add_keybind(KeyBinding::new( self.add_keybind(KeyBinding::new(
self.xlib.make_key("T", Mod1Mask | ShiftMask), self.xlib.make_key("T", Mod1Mask),
|wm, _| wm.spawn("xterm", &[]), |wm, _| wm.spawn("xterm", &[]),
)); ));
@ -101,6 +105,8 @@ impl WindowManager {
|wm, _| wm.rotate_virtual_screen(Direction::Right), |wm, _| wm.rotate_virtual_screen(Direction::Right),
)); ));
self.xlib.init();
self self
} }
@ -113,9 +119,9 @@ impl WindowManager {
loop { loop {
let event = self.xlib.next_event(); let event = self.xlib.next_event();
self.handle_toggle_floating(&event);
self.handle_move_window(&event); self.handle_move_window(&event);
self.handle_resize_client(&event); self.handle_resize_client(&event);
self.handle_toggle_floating(&event);
match event.get_type() { match event.get_type() {
xlib::MapRequest => self.map_request(&event), xlib::MapRequest => self.map_request(&event),
@ -133,6 +139,8 @@ impl WindowManager {
fn quit(&self) -> ! { fn quit(&self) -> ! {
self.xlib.close_dpy(); self.xlib.close_dpy();
info!("Goodbye.");
std::process::exit(0); std::process::exit(0);
} }
@ -158,11 +166,13 @@ impl WindowManager {
fn handle_toggle_floating(&mut self, event: &XEvent) { fn handle_toggle_floating(&mut self, event: &XEvent) {
if event.get_type() == xlib::ButtonPress { if event.get_type() == xlib::ButtonPress {
let event = unsafe { &event.button }; let event: &XButtonEvent = event.as_ref();
let clean_mask = self.xlib.get_clean_mask(); let clean_mask = self.xlib.get_clean_mask();
if event.button == 2 && event.state & clean_mask == Mod1Mask & clean_mask { if event.button == 2 && event.state & clean_mask == Mod1Mask & clean_mask {
if self.clients.contains(&event.subwindow) { if self.clients.contains(&event.subwindow) {
info!("toggleing floating for {:?}", event.subwindow);
self.clients.toggle_floating(&event.subwindow); self.clients.toggle_floating(&event.subwindow);
self.arrange_clients(); self.arrange_clients();
@ -190,9 +200,7 @@ impl WindowManager {
&& self.clients.contains(&event.subwindow) && self.clients.contains(&event.subwindow)
{ {
// if client is tiled, set to floating // if client is tiled, set to floating
if self.clients.get(&event.subwindow).is_tiled() { if self.clients.set_floating(&event.subwindow) {
self.clients.toggle_floating(&event.subwindow);
self.arrange_clients(); self.arrange_clients();
} }
@ -215,12 +223,14 @@ impl WindowManager {
xlib::MotionNotify => { xlib::MotionNotify => {
let event: &XMotionEvent = event.as_ref(); let event: &XMotionEvent = event.as_ref();
if let Some(move_window) = &self.move_window { if let Some(move_window) = &mut self.move_window {
let (x, y) = ( let (x, y) = (
event.x - move_window.cached_cursor_position.0, event.x - move_window.cached_cursor_position.0,
event.y - move_window.cached_cursor_position.1, event.y - move_window.cached_cursor_position.1,
); );
move_window.cached_cursor_position = (event.x, event.y);
if let Some(client) = self.clients.get_mut(&move_window.key).into_option() { if let Some(client) = self.clients.get_mut(&move_window.key).into_option() {
let position = &mut client.position; let position = &mut client.position;
position.0 += x; position.0 += x;
@ -281,16 +291,19 @@ impl WindowManager {
xlib::MotionNotify => { xlib::MotionNotify => {
let event: &XMotionEvent = event.as_ref(); let event: &XMotionEvent = event.as_ref();
if let Some(resize_window) = &self.resize_window { if let Some(resize_window) = &mut self.resize_window {
let (x, y) = ( let (x, y) = (
event.x - resize_window.cached_cursor_position.0, event.x - resize_window.cached_cursor_position.0,
event.y - resize_window.cached_cursor_position.1, event.y - resize_window.cached_cursor_position.1,
); );
resize_window.cached_cursor_position = (event.x, event.y);
if let Some(client) = self.clients.get_mut(&resize_window.key).into_option() { if let Some(client) = self.clients.get_mut(&resize_window.key).into_option() {
let size = &mut client.size; let size = &mut client.size;
size.0 += x;
size.1 += y; size.0 = std::cmp::max(1, size.0 + x);
size.1 = std::cmp::max(1, size.1 + y);
self.xlib.resize_client(client); self.xlib.resize_client(client);
} }
@ -301,19 +314,19 @@ impl WindowManager {
} }
fn rotate_virtual_screen(&mut self, dir: Direction) { fn rotate_virtual_screen(&mut self, dir: Direction) {
info!("rotateing VS: {:?}", dir);
match dir { match dir {
Direction::Left => self.clients.rotate_left(), Direction::Left => self.clients.rotate_left(),
Direction::Right => self.clients.rotate_right(), Direction::Right => self.clients.rotate_right(),
} }
self.clients
.iter_current_screen()
.for_each(|(_, c)| self.xlib.move_resize_client(c));
self.clients self.clients
.iter_hidden() .iter_hidden()
.for_each(|(_, c)| self.xlib.hide_client(c)); .for_each(|(_, c)| self.xlib.hide_client(c));
self.arrange_clients();
// focus first client in all visible clients // focus first client in all visible clients
let to_focus = self.clients.iter_visible().next().map(|(k, _)| k).cloned(); let to_focus = self.clients.iter_visible().next().map(|(k, _)| k).cloned();
@ -324,11 +337,15 @@ impl WindowManager {
fn arrange_clients(&mut self) { fn arrange_clients(&mut self) {
let (width, height) = self.xlib.dimensions(); let (width, height) = self.xlib.dimensions();
self.clients.arrange_virtual_screen(width, height, None); self.clients.arrange_virtual_screen(width, height, Some(2));
self.clients self.clients
.iter_current_screen() .iter_current_screen()
.for_each(|(_, c)| self.xlib.move_resize_client(c)); .for_each(|(_, c)| self.xlib.move_resize_client(c));
self.clients
.iter_floating()
.for_each(|(_, c)| self.xlib.raise_client(c));
} }
fn focus_client<K>(&mut self, key: &K) fn focus_client<K>(&mut self, key: &K)
@ -337,13 +354,13 @@ impl WindowManager {
{ {
let (new, old) = self.clients.focus_client(key); let (new, old) = self.clients.focus_client(key);
if let Some(new) = new.into_option() {
self.xlib.focus_client(new);
}
if let Some(old) = old.into_option() { if let Some(old) = old.into_option() {
self.xlib.unfocus_client(old); self.xlib.unfocus_client(old);
} }
if let Some(new) = new.into_option() {
self.xlib.focus_client(new);
}
} }
#[allow(dead_code)] #[allow(dead_code)]
@ -363,20 +380,17 @@ impl WindowManager {
} }
fn map_request(&mut self, event: &XEvent) { fn map_request(&mut self, event: &XEvent) {
let event = unsafe { &event.map_request }; let event: &XMapRequestEvent = event.as_ref();
info!("MapRequest: {:?}", event);
if !self.clients.contains(&event.window) { if !self.clients.contains(&event.window) {
info!("MapRequest: new client"); info!("MapRequest: new client: {:?}", event.window);
self.new_client(event.window); self.new_client(event.window);
} }
} }
fn unmap_notify(&mut self, event: &XEvent) { fn unmap_notify(&mut self, event: &XEvent) {
let event = unsafe { &event.unmap }; let event: &XUnmapEvent = event.as_ref();
info!("UnmapNotify: {:?}", event);
self.clients.remove(&event.window); self.clients.remove(&event.window);
@ -384,8 +398,7 @@ impl WindowManager {
} }
fn destroy_notify(&mut self, event: &XEvent) { fn destroy_notify(&mut self, event: &XEvent) {
let event = unsafe { &event.destroy_window }; let event: &XDestroyWindowEvent = event.as_ref();
info!("DestroyNotify: {:?}", event);
self.clients.remove(&event.window); self.clients.remove(&event.window);
@ -393,8 +406,7 @@ impl WindowManager {
} }
fn configure_request(&mut self, event: &XEvent) { fn configure_request(&mut self, event: &XEvent) {
let event = unsafe { &event.configure_request }; let event: &XConfigureRequestEvent = event.as_ref();
info!("ConfigureRequest: {:?}", event);
match self.clients.get(&event.window).into_option() { match self.clients.get(&event.window).into_option() {
Some(client) => self.xlib.configure_client(client), Some(client) => self.xlib.configure_client(client),
@ -403,15 +415,13 @@ impl WindowManager {
} }
fn enter_notify(&mut self, event: &XEvent) { fn enter_notify(&mut self, event: &XEvent) {
let event = unsafe { &event.crossing }; let event: &XCrossingEvent = event.as_ref();
info!("EnterNotify: {:?}", event);
self.focus_client(&event.window); self.focus_client(&event.window);
} }
fn button_notify(&mut self, event: &XEvent) { fn button_notify(&mut self, event: &XEvent) {
let event = unsafe { &event.button }; let event: &XButtonEvent = event.as_ref();
info!("EnterNotify: {:?}", event);
self.focus_client(&event.subwindow); self.focus_client(&event.subwindow);
if let Some(client) = self.clients.get(&event.subwindow).into_option() { if let Some(client) = self.clients.get(&event.subwindow).into_option() {
@ -420,6 +430,7 @@ impl WindowManager {
} }
pub fn spawn(&self, command: &str, args: &[&str]) { pub fn spawn(&self, command: &str, args: &[&str]) {
info!("spawn: {:?} {:?}", command, args.join(" "));
match std::process::Command::new(command).args(args).spawn() { match std::process::Command::new(command).args(args).spawn() {
Ok(_) => {} Ok(_) => {}
Err(err) => { Err(err) => {

View file

@ -11,6 +11,8 @@ use x11::xlib::{
}; };
use xlib::GrabModeAsync; use xlib::GrabModeAsync;
use log::error;
use crate::clients::Client; use crate::clients::Client;
pub struct XLib { pub struct XLib {
@ -70,7 +72,7 @@ impl XLib {
} }
} }
pub fn init(self) -> Self { pub fn init(&mut self) {
unsafe { unsafe {
let mut window_attributes = let mut window_attributes =
std::mem::MaybeUninit::<xlib::XSetWindowAttributes>::zeroed().assume_init(); std::mem::MaybeUninit::<xlib::XSetWindowAttributes>::zeroed().assume_init();
@ -92,13 +94,19 @@ impl XLib {
xlib::XSelectInput(self.dpy(), self.root, window_attributes.event_mask); xlib::XSelectInput(self.dpy(), self.root, window_attributes.event_mask);
} }
self self.grab_global_keybinds(self.root);
} }
pub fn add_global_keybind(&mut self, key: KeyOrButton) { pub fn add_global_keybind(&mut self, key: KeyOrButton) {
self.global_keybinds.push(key); self.global_keybinds.push(key);
} }
fn grab_global_keybinds(&self, window: Window) {
for kb in self.global_keybinds.iter() {
self.grab_key_or_button(window, kb);
}
}
#[allow(dead_code)] #[allow(dead_code)]
pub fn remove_global_keybind(&mut self, key: &KeyOrButton) { pub fn remove_global_keybind(&mut self, key: &KeyOrButton) {
if self.global_keybinds.contains(key) { if self.global_keybinds.contains(key) {
@ -206,18 +214,22 @@ impl XLib {
stack_mode: 0, stack_mode: 0,
}; };
unsafe { if client.size.0 < 1 || client.size.1 < 1 {
xlib::XConfigureWindow( error!("client {:?} size is less than 1 pixel!", client);
self.dpy(), } else {
client.window, unsafe {
(xlib::CWY | xlib::CWX | xlib::CWHeight | xlib::CWWidth) as u32, xlib::XConfigureWindow(
&mut windowchanges, self.dpy(),
); client.window,
(xlib::CWY | xlib::CWX | xlib::CWHeight | xlib::CWWidth) as u32,
&mut windowchanges,
);
// I don't think I have to call this ~ // I don't think I have to call this ~
//self.configure(client); //self.configure_client(client);
xlib::XSync(self.dpy(), 0); xlib::XSync(self.dpy(), 0);
}
} }
} }
@ -232,15 +244,19 @@ impl XLib {
stack_mode: 0, stack_mode: 0,
}; };
unsafe { if client.size.0 < 1 || client.size.1 < 1 {
xlib::XConfigureWindow( error!("client {:?} size is less than 1 pixel!", client);
self.dpy(), } else {
client.window, unsafe {
(xlib::CWWidth | xlib::CWHeight) as u32, xlib::XConfigureWindow(
&mut wc, self.dpy(),
); client.window,
(xlib::CWX | xlib::CWY) as u32,
&mut wc,
);
xlib::XSync(self.dpy(), 0); xlib::XSync(self.dpy(), 0);
}
} }
} }
@ -255,15 +271,19 @@ impl XLib {
stack_mode: 0, stack_mode: 0,
}; };
unsafe { if client.size.0 < 1 || client.size.1 < 1 {
xlib::XConfigureWindow( error!("client {:?} size is less than 1 pixel!", client);
self.dpy(), } else {
client.window, unsafe {
(xlib::CWX | xlib::CWY) as u32, xlib::XConfigureWindow(
&mut wc, self.dpy(),
); client.window,
(xlib::CWWidth | xlib::CWHeight) as u32,
&mut wc,
);
xlib::XSync(self.dpy(), 0); xlib::XSync(self.dpy(), 0);
}
} }
} }
@ -278,15 +298,19 @@ impl XLib {
stack_mode: 0, stack_mode: 0,
}; };
unsafe { if client.size.0 < 1 || client.size.1 < 1 {
xlib::XConfigureWindow( error!("client {:?} size is less than 1 pixel!", client);
self.dpy(), } else {
client.window, unsafe {
(xlib::CWWidth | xlib::CWHeight) as u32, xlib::XConfigureWindow(
&mut wc, self.dpy(),
); client.window,
(xlib::CWX | xlib::CWY) as u32,
&mut wc,
);
xlib::XSync(self.dpy(), 0); xlib::XSync(self.dpy(), 0);
}
} }
} }
@ -352,9 +376,7 @@ impl XLib {
); );
} }
for kb in self.global_keybinds.iter() { self.grab_global_keybinds(window);
self.grab_key_or_button(window, kb);
}
} }
pub fn dimensions(&self) -> (i32, i32) { pub fn dimensions(&self) -> (i32, i32) {