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
*~
/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
[dependencies]
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

@ -3,6 +3,8 @@
use std::collections::HashMap;
use std::num::NonZeroI32;
use log::{debug, error};
use crate::util::BuildIdentityHasher;
mod client {
@ -282,7 +284,7 @@ impl ClientState {
self.clients.insert(key, client);
if let Some(vs) = self.virtual_screens.front_mut() {
vs.aux.push(key);
vs.insert(&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
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
where
K: ClientKey,
{
if self.get(key).is_floating() {
if self.get(key).is_tiled() {
self.toggle_floating(key);
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)
where
K: ClientKey,
@ -404,10 +410,12 @@ impl ClientState {
(None, Some(client)) => {
self.clients.insert(key, client);
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)
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));
}
}
vs.switch_stack_for_client(key);
}
}
@ -554,7 +536,7 @@ impl ClientState {
// 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() && !vs.master.is_empty()));
let width = width / (1 + i32::from(!vs.aux.is_empty()));
// make sure we dont devide by 0
let master_height = height
@ -583,7 +565,7 @@ impl ClientState {
.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);
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
@ -617,6 +601,15 @@ impl VirtualScreen {
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)
where
K: ClientKey,
@ -628,6 +621,23 @@ impl VirtualScreen {
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
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 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::{
clients::{Client, ClientKey, ClientState},
@ -19,6 +22,7 @@ pub struct WindowManager {
xlib: XLib,
}
#[derive(Debug)]
pub enum Direction {
Left,
Right,
@ -38,7 +42,7 @@ struct KeyBinding {
impl WindowManager {
pub fn new() -> Self {
let clients = ClientState::with_virtualscreens(3);
let xlib = XLib::new().init();
let xlib = XLib::new();
Self {
clients,
@ -82,7 +86,7 @@ impl WindowManager {
));
self.add_keybind(KeyBinding::new(
self.xlib.make_key("T", Mod1Mask | ShiftMask),
self.xlib.make_key("T", Mod1Mask),
|wm, _| wm.spawn("xterm", &[]),
));
@ -101,6 +105,8 @@ impl WindowManager {
|wm, _| wm.rotate_virtual_screen(Direction::Right),
));
self.xlib.init();
self
}
@ -113,9 +119,9 @@ impl WindowManager {
loop {
let event = self.xlib.next_event();
self.handle_toggle_floating(&event);
self.handle_move_window(&event);
self.handle_resize_client(&event);
self.handle_toggle_floating(&event);
match event.get_type() {
xlib::MapRequest => self.map_request(&event),
@ -133,6 +139,8 @@ impl WindowManager {
fn quit(&self) -> ! {
self.xlib.close_dpy();
info!("Goodbye.");
std::process::exit(0);
}
@ -158,11 +166,13 @@ impl WindowManager {
fn handle_toggle_floating(&mut self, event: &XEvent) {
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();
if event.button == 2 && event.state & clean_mask == Mod1Mask & clean_mask {
if self.clients.contains(&event.subwindow) {
info!("toggleing floating for {:?}", event.subwindow);
self.clients.toggle_floating(&event.subwindow);
self.arrange_clients();
@ -190,9 +200,7 @@ impl WindowManager {
&& self.clients.contains(&event.subwindow)
{
// if client is tiled, set to floating
if self.clients.get(&event.subwindow).is_tiled() {
self.clients.toggle_floating(&event.subwindow);
if self.clients.set_floating(&event.subwindow) {
self.arrange_clients();
}
@ -215,12 +223,14 @@ impl WindowManager {
xlib::MotionNotify => {
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) = (
event.x - move_window.cached_cursor_position.0,
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() {
let position = &mut client.position;
position.0 += x;
@ -281,16 +291,19 @@ impl WindowManager {
xlib::MotionNotify => {
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) = (
event.x - resize_window.cached_cursor_position.0,
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() {
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);
}
@ -301,19 +314,19 @@ impl WindowManager {
}
fn rotate_virtual_screen(&mut self, dir: Direction) {
info!("rotateing VS: {:?}", dir);
match dir {
Direction::Left => self.clients.rotate_left(),
Direction::Right => self.clients.rotate_right(),
}
self.clients
.iter_current_screen()
.for_each(|(_, c)| self.xlib.move_resize_client(c));
self.clients
.iter_hidden()
.for_each(|(_, c)| self.xlib.hide_client(c));
self.arrange_clients();
// focus first client in all visible clients
let to_focus = self.clients.iter_visible().next().map(|(k, _)| k).cloned();
@ -324,11 +337,15 @@ impl WindowManager {
fn arrange_clients(&mut self) {
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
.iter_current_screen()
.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)
@ -337,13 +354,13 @@ impl WindowManager {
{
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() {
self.xlib.unfocus_client(old);
}
if let Some(new) = new.into_option() {
self.xlib.focus_client(new);
}
}
#[allow(dead_code)]
@ -363,20 +380,17 @@ impl WindowManager {
}
fn map_request(&mut self, event: &XEvent) {
let event = unsafe { &event.map_request };
info!("MapRequest: {:?}", event);
let event: &XMapRequestEvent = event.as_ref();
if !self.clients.contains(&event.window) {
info!("MapRequest: new client");
info!("MapRequest: new client: {:?}", event.window);
self.new_client(event.window);
}
}
fn unmap_notify(&mut self, event: &XEvent) {
let event = unsafe { &event.unmap };
info!("UnmapNotify: {:?}", event);
let event: &XUnmapEvent = event.as_ref();
self.clients.remove(&event.window);
@ -384,8 +398,7 @@ impl WindowManager {
}
fn destroy_notify(&mut self, event: &XEvent) {
let event = unsafe { &event.destroy_window };
info!("DestroyNotify: {:?}", event);
let event: &XDestroyWindowEvent = event.as_ref();
self.clients.remove(&event.window);
@ -393,8 +406,7 @@ impl WindowManager {
}
fn configure_request(&mut self, event: &XEvent) {
let event = unsafe { &event.configure_request };
info!("ConfigureRequest: {:?}", event);
let event: &XConfigureRequestEvent = event.as_ref();
match self.clients.get(&event.window).into_option() {
Some(client) => self.xlib.configure_client(client),
@ -403,15 +415,13 @@ impl WindowManager {
}
fn enter_notify(&mut self, event: &XEvent) {
let event = unsafe { &event.crossing };
info!("EnterNotify: {:?}", event);
let event: &XCrossingEvent = event.as_ref();
self.focus_client(&event.window);
}
fn button_notify(&mut self, event: &XEvent) {
let event = unsafe { &event.button };
info!("EnterNotify: {:?}", event);
let event: &XButtonEvent = event.as_ref();
self.focus_client(&event.subwindow);
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]) {
info!("spawn: {:?} {:?}", command, args.join(" "));
match std::process::Command::new(command).args(args).spawn() {
Ok(_) => {}
Err(err) => {

View file

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