fixed some bugs related to floating window stacking
This commit is contained in:
parent
95d7740119
commit
134e727b25
|
@ -1,3 +1,2 @@
|
|||
imports_granularity = "Crate"
|
||||
wrap_comments = true
|
||||
#wrap_comments = true
|
||||
max_width = 80
|
185
src/clients.rs
185
src/clients.rs
|
@ -1,7 +1,7 @@
|
|||
#![allow(dead_code)]
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::num::NonZeroI32;
|
||||
use std::{collections::HashMap, ops::Rem, usize};
|
||||
|
||||
use log::{error, info};
|
||||
|
||||
|
@ -113,31 +113,6 @@ mod client {
|
|||
self.to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
impl Borrow<Window> for Client {
|
||||
fn borrow(&self) -> &Window {
|
||||
&self.window
|
||||
}
|
||||
}
|
||||
|
||||
impl ClientKey for Rc<Client> {
|
||||
fn key(&self) -> u64 {
|
||||
self.window
|
||||
}
|
||||
}
|
||||
impl<'a> Borrow<dyn ClientKey + 'a> for Client {
|
||||
fn borrow(&self) -> &(dyn ClientKey + 'a) {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Borrow<dyn ClientKey + 'a> for Rc<Client> {
|
||||
fn borrow(&self) -> &(dyn ClientKey + 'a) {
|
||||
self
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
pub use client::*;
|
||||
|
@ -218,56 +193,15 @@ pub enum ClientEntry<T> {
|
|||
Vacant,
|
||||
}
|
||||
|
||||
impl<T> Into<Option<T>> for ClientEntry<T> {
|
||||
fn into(self) -> Option<T> {
|
||||
match self {
|
||||
Self::Vacant => None,
|
||||
Self::Tiled(client) | Self::Floating(client) => Some(client),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ClientEntry<T> {
|
||||
pub fn into_option(self) -> Option<T> {
|
||||
self.into()
|
||||
}
|
||||
|
||||
pub fn unwrap(self) -> T {
|
||||
self.into_option().unwrap()
|
||||
}
|
||||
|
||||
pub fn is_vacant(&self) -> bool {
|
||||
match self {
|
||||
ClientEntry::Vacant => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_floating(&self) -> bool {
|
||||
match self {
|
||||
ClientEntry::Floating(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_tiled(&self) -> bool {
|
||||
match self {
|
||||
ClientEntry::Tiled(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_occupied(&self) -> bool {
|
||||
!self.is_vacant()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ClientState {
|
||||
pub(self) clients: Clients,
|
||||
pub(self) floating_clients: Clients,
|
||||
focused: Option<ClientRef>,
|
||||
pub(self) virtual_screens: VecDeque<VirtualScreen>,
|
||||
|
||||
gap: i32,
|
||||
screen_size: (i32, i32),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -286,6 +220,8 @@ impl Default for ClientState {
|
|||
floating_clients: Default::default(),
|
||||
focused: None,
|
||||
virtual_screens: vss,
|
||||
gap: 0,
|
||||
screen_size: (1, 1),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -295,13 +231,24 @@ impl ClientState {
|
|||
Self::default()
|
||||
}
|
||||
|
||||
pub fn with_virtualscreens(num: usize) -> Self {
|
||||
pub fn with_gap(self, gap: i32) -> Self {
|
||||
Self { gap, ..self }
|
||||
}
|
||||
|
||||
pub fn with_screen_size(self, screen_size: (i32, i32)) -> Self {
|
||||
Self {
|
||||
screen_size,
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_virtualscreens(self, num: usize) -> Self {
|
||||
let mut vss = VecDeque::<VirtualScreen>::new();
|
||||
vss.resize_with(num, Default::default);
|
||||
|
||||
Self {
|
||||
virtual_screens: vss,
|
||||
..Default::default()
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -333,6 +280,9 @@ impl ClientState {
|
|||
|
||||
self.focus_client(&key);
|
||||
|
||||
// adding a client changes the liling layout, rearrange
|
||||
self.arrange_virtual_screen();
|
||||
|
||||
// TODO: eventually make this function return a `ClientEntry` instead of an `Option`.
|
||||
self.get(&key).into_option()
|
||||
}
|
||||
|
@ -351,6 +301,9 @@ impl ClientState {
|
|||
|
||||
self.clients.remove(&key.key());
|
||||
self.floating_clients.remove(&key.key());
|
||||
|
||||
// removing a client changes the liling layout, rearrange
|
||||
self.arrange_virtual_screen();
|
||||
}
|
||||
|
||||
pub fn contains<K>(&self, key: &K) -> bool
|
||||
|
@ -460,12 +413,18 @@ impl ClientState {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn rotate_right(&mut self) {
|
||||
self.virtual_screens.rotate_right(1);
|
||||
pub fn rotate_right(&mut self, n: Option<usize>) {
|
||||
self.virtual_screens
|
||||
.rotate_right(n.unwrap_or(1).rem(self.virtual_screens.len()));
|
||||
|
||||
self.arrange_virtual_screen();
|
||||
}
|
||||
|
||||
pub fn rotate_left(&mut self) {
|
||||
self.virtual_screens.rotate_left(1);
|
||||
pub fn rotate_left(&mut self, n: Option<usize>) {
|
||||
self.virtual_screens
|
||||
.rotate_left(n.unwrap_or(1).rem(self.virtual_screens.len()));
|
||||
|
||||
self.arrange_virtual_screen();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -508,6 +467,7 @@ impl ClientState {
|
|||
true => {
|
||||
self.floating_clients.insert(key, floating_client);
|
||||
}
|
||||
|
||||
false => {
|
||||
self.clients.insert(key, floating_client);
|
||||
if let Some(vs) = self.virtual_screens.front_mut() {
|
||||
|
@ -520,14 +480,22 @@ impl ClientState {
|
|||
error!("wtf? Client was present in tiled and floating list.")
|
||||
}
|
||||
};
|
||||
|
||||
// we added or removed a client from the tiling so the layout changed, rearrange
|
||||
self.arrange_virtual_screen();
|
||||
}
|
||||
|
||||
fn remove_from_virtual_screens<K>(&mut self, key: &K)
|
||||
where
|
||||
K: ClientKey,
|
||||
{
|
||||
if self.contains(key) {
|
||||
if let Some(vs) = self.get_mut_virtualscreen_for_client(key) {
|
||||
vs.remove(key);
|
||||
|
||||
// we removed a client so the layout changed, rearrange
|
||||
self.arrange_virtual_screen();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -578,7 +546,9 @@ impl ClientState {
|
|||
if focused == key.key() {
|
||||
(ClientEntry::Vacant, ClientEntry::Vacant)
|
||||
} else {
|
||||
// focus the new client and return reference to it and the previously focused client.
|
||||
// focus the new client and return reference to it
|
||||
// and the previously focused client.
|
||||
|
||||
self.focused = Some(key.key());
|
||||
(self.get(key), self.get(&focused))
|
||||
}
|
||||
|
@ -599,7 +569,8 @@ impl ClientState {
|
|||
}
|
||||
|
||||
/**
|
||||
sets `self.focused` to `None` and returns a reference to the previously focused Client if any.
|
||||
sets `self.focused` to `None` and returns a reference to
|
||||
the previously focused Client if any.
|
||||
*/
|
||||
pub fn unfocus(&mut self) -> ClientEntry<&Client> {
|
||||
match self.focused {
|
||||
|
@ -627,6 +598,8 @@ impl ClientState {
|
|||
{
|
||||
if let Some(vs) = self.get_mut_virtualscreen_for_client(key) {
|
||||
vs.switch_stack_for_client(key);
|
||||
|
||||
self.arrange_virtual_screen();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -635,13 +608,9 @@ impl ClientState {
|
|||
screen width and screen height.
|
||||
Optionally adds a gap between windows `gap.unwrap_or(0)` pixels wide.
|
||||
*/
|
||||
pub fn arrange_virtual_screen(
|
||||
&mut self,
|
||||
width: i32,
|
||||
height: i32,
|
||||
gap: Option<i32>,
|
||||
) {
|
||||
let gap = gap.unwrap_or(0);
|
||||
pub fn arrange_virtual_screen(&mut self) {
|
||||
let gap = self.gap;
|
||||
let (width, height) = self.screen_size;
|
||||
|
||||
// should be fine to unwrap since we will always have at least 1 virtual screen
|
||||
if let Some(vs) = self.virtual_screens.front_mut() {
|
||||
|
@ -692,7 +661,7 @@ impl ClientState {
|
|||
}
|
||||
}
|
||||
|
||||
info!("{:#?}", self);
|
||||
//info!("{:#?}", self);
|
||||
}
|
||||
|
||||
// Should have xlib send those changes back to the x server after this function
|
||||
|
@ -777,3 +746,47 @@ impl VirtualScreen {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Into<Option<T>> for ClientEntry<T> {
|
||||
fn into(self) -> Option<T> {
|
||||
match self {
|
||||
Self::Vacant => None,
|
||||
Self::Tiled(client) | Self::Floating(client) => Some(client),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ClientEntry<T> {
|
||||
pub fn into_option(self) -> Option<T> {
|
||||
self.into()
|
||||
}
|
||||
|
||||
pub fn unwrap(self) -> T {
|
||||
self.into_option().unwrap()
|
||||
}
|
||||
|
||||
pub fn is_vacant(&self) -> bool {
|
||||
match self {
|
||||
ClientEntry::Vacant => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_floating(&self) -> bool {
|
||||
match self {
|
||||
ClientEntry::Floating(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_tiled(&self) -> bool {
|
||||
match self {
|
||||
ClientEntry::Tiled(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_occupied(&self) -> bool {
|
||||
!self.is_vacant()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ fn main() {
|
|||
|
||||
log_prologue();
|
||||
|
||||
state::WindowManager::new(WMConfig::default()).init().run();
|
||||
state::WindowManager::new(WMConfig::default()).run();
|
||||
}
|
||||
|
||||
fn log_prologue() {
|
||||
|
|
67
src/state.rs
67
src/state.rs
|
@ -13,7 +13,7 @@ use xlib::{
|
|||
};
|
||||
|
||||
use crate::{
|
||||
clients::{Client, ClientKey, ClientState},
|
||||
clients::{Client, ClientEntry, ClientKey, ClientState},
|
||||
xlib::KeyOrButton,
|
||||
xlib::XLib,
|
||||
};
|
||||
|
@ -40,8 +40,8 @@ pub struct WindowManager {
|
|||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum Direction {
|
||||
Left,
|
||||
Right,
|
||||
Left(Option<usize>),
|
||||
Right(Option<usize>),
|
||||
}
|
||||
|
||||
enum MoveResizeInfo {
|
||||
|
@ -70,10 +70,13 @@ struct KeyBinding {
|
|||
|
||||
impl WindowManager {
|
||||
pub fn new(config: WMConfig) -> Self {
|
||||
let clients =
|
||||
ClientState::with_virtualscreens(config.num_virtualscreens);
|
||||
let xlib = XLib::new();
|
||||
|
||||
let clients = ClientState::new()
|
||||
.with_virtualscreens(config.num_virtualscreens)
|
||||
.with_gap(config.gap.unwrap_or(1))
|
||||
.with_screen_size(xlib.dimensions());
|
||||
|
||||
Self {
|
||||
clients,
|
||||
move_resize_window: MoveResizeInfo::None,
|
||||
|
@ -82,9 +85,10 @@ impl WindowManager {
|
|||
last_rotation: None,
|
||||
config,
|
||||
}
|
||||
.init()
|
||||
}
|
||||
|
||||
pub fn init(mut self) -> Self {
|
||||
fn init(mut self) -> Self {
|
||||
self.xlib.add_global_keybind(KeyOrButton::button(
|
||||
1,
|
||||
self.config.mod_key,
|
||||
|
@ -152,12 +156,12 @@ impl WindowManager {
|
|||
|
||||
self.add_keybind(KeyBinding::new(
|
||||
self.xlib.make_key("Left", self.config.mod_key),
|
||||
|wm, _| wm.rotate_virtual_screen(Direction::Left),
|
||||
|wm, _| wm.rotate_virtual_screen(Direction::Left(None)),
|
||||
));
|
||||
|
||||
self.add_keybind(KeyBinding::new(
|
||||
self.xlib.make_key("Right", self.config.mod_key),
|
||||
|wm, _| wm.rotate_virtual_screen(Direction::Right),
|
||||
|wm, _| wm.rotate_virtual_screen(Direction::Right(None)),
|
||||
));
|
||||
|
||||
self.add_keybind(KeyBinding::new(
|
||||
|
@ -177,12 +181,12 @@ impl WindowManager {
|
|||
|
||||
self.add_keybind(KeyBinding::new(
|
||||
self.xlib.make_key("J", self.config.mod_key | ShiftMask),
|
||||
|wm, _| wm.rotate_virtual_screen(Direction::Left),
|
||||
|wm, _| wm.rotate_virtual_screen(Direction::Left(None)),
|
||||
));
|
||||
|
||||
self.add_keybind(KeyBinding::new(
|
||||
self.xlib.make_key("K", self.config.mod_key | ShiftMask),
|
||||
|wm, _| wm.rotate_virtual_screen(Direction::Right),
|
||||
|wm, _| wm.rotate_virtual_screen(Direction::Right(None)),
|
||||
));
|
||||
|
||||
self.xlib.init();
|
||||
|
@ -262,8 +266,8 @@ impl WindowManager {
|
|||
self.last_rotation = Some(dir);
|
||||
|
||||
match dir {
|
||||
Direction::Left => self.clients.rotate_left(),
|
||||
Direction::Right => self.clients.rotate_right(),
|
||||
Direction::Left(n) => self.clients.rotate_left(n),
|
||||
Direction::Right(n) => self.clients.rotate_right(n),
|
||||
}
|
||||
|
||||
self.arrange_clients();
|
||||
|
@ -273,7 +277,7 @@ impl WindowManager {
|
|||
self.clients.iter_visible().next().map(|(k, _)| k).cloned();
|
||||
|
||||
if let Some(key) = to_focus {
|
||||
self.focus_client(&key);
|
||||
self.focus_client(&key, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -286,7 +290,7 @@ impl WindowManager {
|
|||
.cloned();
|
||||
|
||||
if let Some(k) = k {
|
||||
self.focus_client(&k);
|
||||
self.focus_client(&k, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -299,7 +303,7 @@ impl WindowManager {
|
|||
.cloned();
|
||||
|
||||
if let Some(k) = k {
|
||||
self.focus_client(&k);
|
||||
self.focus_client(&k, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -320,10 +324,6 @@ impl WindowManager {
|
|||
}
|
||||
|
||||
fn arrange_clients(&mut self) {
|
||||
let (width, height) = self.xlib.dimensions();
|
||||
self.clients
|
||||
.arrange_virtual_screen(width, height, self.config.gap);
|
||||
|
||||
self.clients
|
||||
.iter_visible()
|
||||
.for_each(|(_, c)| self.xlib.move_resize_client(c));
|
||||
|
@ -333,7 +333,7 @@ impl WindowManager {
|
|||
self.raise_floating_clients();
|
||||
}
|
||||
|
||||
fn focus_client<K>(&mut self, key: &K)
|
||||
fn focus_client<K>(&mut self, key: &K, try_raise: bool)
|
||||
where
|
||||
K: ClientKey,
|
||||
{
|
||||
|
@ -343,12 +343,19 @@ impl WindowManager {
|
|||
self.xlib.unfocus_client(old);
|
||||
}
|
||||
|
||||
if let Some(new) = new.into_option() {
|
||||
match new {
|
||||
ClientEntry::Floating(new) => {
|
||||
self.xlib.focus_client(new);
|
||||
|
||||
if try_raise {
|
||||
self.xlib.raise_client(new);
|
||||
}
|
||||
|
||||
self.raise_floating_clients();
|
||||
}
|
||||
ClientEntry::Tiled(new) => {
|
||||
self.xlib.focus_client(new);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn new_client(&mut self, window: Window) {
|
||||
|
@ -368,7 +375,7 @@ impl WindowManager {
|
|||
self.clients.insert(client).unwrap();
|
||||
self.xlib.map_window(window);
|
||||
|
||||
self.focus_client(&window);
|
||||
self.focus_client(&window, true);
|
||||
|
||||
self.arrange_clients();
|
||||
}
|
||||
|
@ -377,15 +384,12 @@ impl WindowManager {
|
|||
let event: &XMapRequestEvent = event.as_ref();
|
||||
|
||||
if !self.clients.contains(&event.window) {
|
||||
info!("MapRequest: new client: {:?}", event.window);
|
||||
|
||||
self.new_client(event.window);
|
||||
}
|
||||
}
|
||||
|
||||
fn unmap_notify(&mut self, event: &XEvent) {
|
||||
let event: &XUnmapEvent = event.as_ref();
|
||||
info!("unmap_notify: {:?}", event.window);
|
||||
|
||||
self.clients.remove(&event.window);
|
||||
|
||||
|
@ -394,7 +398,6 @@ impl WindowManager {
|
|||
|
||||
fn destroy_notify(&mut self, event: &XEvent) {
|
||||
let event: &XDestroyWindowEvent = event.as_ref();
|
||||
info!("destroy_notify: {:?}", event.window);
|
||||
|
||||
self.clients.remove(&event.window);
|
||||
|
||||
|
@ -413,7 +416,7 @@ impl WindowManager {
|
|||
fn enter_notify(&mut self, event: &XEvent) {
|
||||
let event: &XCrossingEvent = event.as_ref();
|
||||
|
||||
self.focus_client(&event.window);
|
||||
self.focus_client(&event.window, false);
|
||||
}
|
||||
|
||||
/// ensure event.subwindow refers to a valid client.
|
||||
|
@ -514,7 +517,7 @@ impl WindowManager {
|
|||
}
|
||||
|
||||
fn button_press(&mut self, event: &XButtonPressedEvent) {
|
||||
self.focus_client(&event.subwindow);
|
||||
self.focus_client(&event.subwindow, true);
|
||||
|
||||
match event.button {
|
||||
1 | 3 => match self.move_resize_window {
|
||||
|
@ -587,8 +590,8 @@ impl std::ops::Not for Direction {
|
|||
|
||||
fn not(self) -> Self::Output {
|
||||
match self {
|
||||
Direction::Left => Direction::Right,
|
||||
Direction::Right => Direction::Left,
|
||||
Direction::Left(n) => Direction::Right(n),
|
||||
Direction::Right(n) => Direction::Left(n),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -216,12 +216,10 @@ impl XLib {
|
|||
}
|
||||
|
||||
self.send_event(client, self.atoms.take_focus);
|
||||
|
||||
self.raise_client(client);
|
||||
}
|
||||
|
||||
pub fn unfocus_client(&self, client: &Client) {
|
||||
info!("unfocusing client: {:?}", client);
|
||||
//info!("unfocusing client: {:?}", client);
|
||||
unsafe {
|
||||
xlib::XSetInputFocus(
|
||||
self.dpy(),
|
||||
|
|
2
xinitrc
2
xinitrc
|
@ -4,4 +4,4 @@
|
|||
/usr/bin/xsetroot -solid darkslategrey
|
||||
/usr/bin/feh --bg-fill "/mnt/storage/rust/wm/starship.jpg"
|
||||
export RUST_BACKTRACE=1
|
||||
exec /mnt/storage/rust/wm/target/debug/wm >& /home/user/.local/portlights.log
|
||||
exec /mnt/storage/rust/wm/target/release/wm >& /home/user/.local/portlights.log
|
||||
|
|
Loading…
Reference in a new issue