v0.2
This commit is contained in:
parent
7d5aae9cbe
commit
4235fff343
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,3 +1,5 @@
|
|||
/target
|
||||
*~
|
||||
/Cargo.lock
|
||||
/.cargo/
|
||||
/wmlog
|
||||
|
|
|
@ -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"
|
|
@ -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
|
||||
|
|
81
src/state.rs
81
src/state.rs
|
@ -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) => {
|
||||
|
|
100
src/xlib.rs
100
src/xlib.rs
|
@ -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) {
|
||||
|
|
Loading…
Reference in a new issue