Diff
diff --git a/src/client/mod.rs b/src/client/mod.rs
new file mode 100644
index 0000000..38c0645
--- /dev/null
+++ b/src/client/mod.rs
@@ -0,0 +1,88 @@
+use x11rb::protocol::xproto::Window;
+
+pub type TagMask = u32;
+
+#[derive(Debug, Clone)]
+pub struct Client {
+ pub name: String,
+ pub min_aspect: f32,
+ pub max_aspect: f32,
+ pub x_position: i16,
+ pub y_position: i16,
+ pub width: u16,
+ pub height: u16,
+ pub old_x_position: i16,
+ pub old_y_position: i16,
+ pub old_width: u16,
+ pub old_height: u16,
+ pub base_width: i32,
+ pub base_height: i32,
+ pub increment_width: i32,
+ pub increment_height: i32,
+ pub max_width: i32,
+ pub max_height: i32,
+ pub min_width: i32,
+ pub min_height: i32,
+ pub hints_valid: bool,
+ pub border_width: u16,
+ pub old_border_width: u16,
+ pub tags: TagMask,
+ pub is_fixed: bool,
+ pub is_floating: bool,
+ pub is_urgent: bool,
+ pub never_focus: bool,
+ pub old_state: bool,
+ pub is_fullscreen: bool,
+ pub next: Option<Window>,
+ pub stack_next: Option<Window>,
+ pub monitor_index: usize,
+ pub window: Window,
+}
+
+impl Client {
+ pub fn new(window: Window, monitor_index: usize, tags: TagMask) -> Self {
+ Self {
+ name: String::new(),
+ min_aspect: 0.0,
+ max_aspect: 0.0,
+ x_position: 0,
+ y_position: 0,
+ width: 0,
+ height: 0,
+ old_x_position: 0,
+ old_y_position: 0,
+ old_width: 0,
+ old_height: 0,
+ base_width: 0,
+ base_height: 0,
+ increment_width: 0,
+ increment_height: 0,
+ max_width: 0,
+ max_height: 0,
+ min_width: 0,
+ min_height: 0,
+ hints_valid: false,
+ border_width: 0,
+ old_border_width: 0,
+ tags,
+ is_fixed: false,
+ is_floating: false,
+ is_urgent: false,
+ never_focus: false,
+ old_state: false,
+ is_fullscreen: false,
+ next: None,
+ stack_next: None,
+ monitor_index,
+ window,
+ }
+ }
+
+ pub fn width_with_border(&self) -> u16 {
+ self.width.saturating_add(2 * self.border_width)
+ }
+
+ pub fn height_with_border(&self) -> u16 {
+ self.height.saturating_add(2 * self.border_width)
+ }
+}
diff --git a/src/config/lua_api.rs b/src/config/lua_api.rs
index 3b67657..5057830 100644
--- a/src/config/lua_api.rs
+++ b/src/config/lua_api.rs
@@ -252,11 +252,6 @@ fn register_client_module(lua: &Lua, parent: &Table) -> Result<(), ConfigError>
create_action_table(lua, "SwapDirection", Value::Integer(dir_int))
})?;
- let smart_move = lua.create_function(|lua, dir: String| {
- let dir_int = direction_string_to_int(&dir)?;
- create_action_table(lua, "SmartMoveWin", Value::Integer(dir_int))
- })?;
-
let exchange = lua.create_function(|lua, ()| {
create_action_table(lua, "ExchangeClient", Value::Nil)
})?;
@@ -267,7 +262,6 @@ fn register_client_module(lua: &Lua, parent: &Table) -> Result<(), ConfigError>
client_table.set("focus_stack", focus_stack)?;
client_table.set("focus_direction", focus_direction)?;
client_table.set("swap_direction", swap_direction)?;
- client_table.set("smart_move", smart_move)?;
client_table.set("exchange", exchange)?;
parent.set("client", client_table)?;
@@ -720,7 +714,6 @@ fn string_to_action(s: &str) -> mlua::Result<KeyAction> {
"CycleLayout" => Ok(KeyAction::CycleLayout),
"MoveToTag" => Ok(KeyAction::MoveToTag),
"FocusMonitor" => Ok(KeyAction::FocusMonitor),
- "SmartMoveWin" => Ok(KeyAction::SmartMoveWin),
"ExchangeClient" => Ok(KeyAction::ExchangeClient),
"ShowKeybindOverlay" => Ok(KeyAction::ShowKeybindOverlay),
_ => Err(mlua::Error::RuntimeError(format!("unknown action '{}'. this is an internal error, please report it", s))),
diff --git a/src/keyboard/handlers.rs b/src/keyboard/handlers.rs
index f392042..79a44cd 100644
--- a/src/keyboard/handlers.rs
+++ b/src/keyboard/handlers.rs
@@ -28,7 +28,6 @@ pub enum KeyAction {
CycleLayout,
MoveToTag,
FocusMonitor,
- SmartMoveWin,
ExchangeClient,
ShowKeybindOverlay,
None,
diff --git a/src/lib.rs b/src/lib.rs
index 34ae1bb..3a8ce7d 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,4 +1,5 @@
pub mod bar;
+pub mod client;
pub mod config;
pub mod errors;
pub mod keyboard;
diff --git a/src/monitor.rs b/src/monitor.rs
index 1693cac..f73c06e 100644
--- a/src/monitor.rs
+++ b/src/monitor.rs
@@ -7,31 +7,73 @@ type WmResult<T> = Result<T, WmError>;
#[derive(Debug, Clone)]
pub struct Monitor {
- pub x: i32,
- pub y: i32,
- pub width: u32,
- pub height: u32,
- pub selected_tags: u32,
- pub focused_window: Option<Window>,
+ pub layout_symbol: String,
+ pub master_factor: f32,
+ pub num_master: i32,
+ pub monitor_number: usize,
+ pub bar_y_position: i32,
+ pub screen_x: i32,
+ pub screen_y: i32,
+ pub screen_width: i32,
+ pub screen_height: i32,
+ pub window_area_x: i32,
+ pub window_area_y: i32,
+ pub window_area_width: i32,
+ pub window_area_height: i32,
+ pub gap_inner_horizontal: i32,
+ pub gap_inner_vertical: i32,
+ pub gap_outer_horizontal: i32,
+ pub gap_outer_vertical: i32,
+ pub selected_tags_index: usize,
+ pub selected_layout_index: usize,
+ pub tagset: [u32; 2],
+ pub show_bar: bool,
+ pub top_bar: bool,
+ pub clients_head: Option<Window>,
+ pub selected_client: Option<Window>,
+ pub stack_head: Option<Window>,
+ pub bar_window: Option<Window>,
+ pub layout_indices: [usize; 2],
}
impl Monitor {
pub fn new(x: i32, y: i32, width: u32, height: u32) -> Self {
Self {
- x,
- y,
- width,
- height,
- selected_tags: 1,
- focused_window: None,
+ layout_symbol: String::from("[]"),
+ master_factor: 0.55,
+ num_master: 1,
+ monitor_number: 0,
+ bar_y_position: 0,
+ screen_x: x,
+ screen_y: y,
+ screen_width: width as i32,
+ screen_height: height as i32,
+ window_area_x: x,
+ window_area_y: y,
+ window_area_width: width as i32,
+ window_area_height: height as i32,
+ gap_inner_horizontal: 3,
+ gap_inner_vertical: 3,
+ gap_outer_horizontal: 3,
+ gap_outer_vertical: 3,
+ selected_tags_index: 0,
+ selected_layout_index: 0,
+ tagset: [1, 1],
+ show_bar: true,
+ top_bar: true,
+ clients_head: None,
+ selected_client: None,
+ stack_head: None,
+ bar_window: None,
+ layout_indices: [0, 1],
}
}
pub fn contains_point(&self, x: i32, y: i32) -> bool {
- x >= self.x
- && x < self.x + self.width as i32
- && y >= self.y
- && y < self.y + self.height as i32
+ x >= self.screen_x
+ && x < self.screen_x + self.screen_width
+ && y >= self.screen_y
+ && y < self.screen_y + self.screen_height
}
}
@@ -77,10 +119,10 @@ pub fn detect_monitors(
let height_in_pixels = screen_info.height as u32;
let is_duplicate_monitor = monitors.iter().any(|monitor| {
- monitor.x == x_position
- && monitor.y == y_position
- && monitor.width == width_in_pixels
- && monitor.height == height_in_pixels
+ monitor.screen_x == x_position
+ && monitor.screen_y == y_position
+ && monitor.screen_width == width_in_pixels as i32
+ && monitor.screen_height == height_in_pixels as i32
});
if !is_duplicate_monitor {
@@ -98,8 +140,8 @@ pub fn detect_monitors(
monitors = fallback_monitors();
}
- monitors.sort_by(|a, b| match a.y.cmp(&b.y) {
- std::cmp::Ordering::Equal => a.x.cmp(&b.x),
+ monitors.sort_by(|a, b| match a.screen_y.cmp(&b.screen_y) {
+ std::cmp::Ordering::Equal => a.screen_x.cmp(&b.screen_x),
other => other,
});
diff --git a/src/overlay/keybind.rs b/src/overlay/keybind.rs
index e0d9fdf..a530eae 100644
--- a/src/overlay/keybind.rs
+++ b/src/overlay/keybind.rs
@@ -216,7 +216,6 @@ impl KeybindOverlay {
KeyAction::ChangeLayout => "Change Layout".to_string(),
KeyAction::CycleLayout => "Cycle Through Layouts".to_string(),
KeyAction::FocusMonitor => "Focus Next Monitor".to_string(),
- KeyAction::SmartMoveWin => "Smart Move Window".to_string(),
KeyAction::ExchangeClient => "Exchange Client Windows".to_string(),
KeyAction::None => "No Action".to_string(),
}
diff --git a/src/window_manager.rs b/src/window_manager.rs
index fdc00b4..d245a72 100644
--- a/src/window_manager.rs
+++ b/src/window_manager.rs
@@ -1,5 +1,6 @@
use crate::Config;
use crate::bar::Bar;
+use crate::client::{Client, TagMask};
use crate::errors::WmError;
use crate::keyboard::{self, Arg, KeyAction, handlers};
use crate::layout::GapConfig;
@@ -7,7 +8,7 @@ use crate::layout::tiling::TilingLayout;
use crate::layout::{Layout, LayoutBox, LayoutType, layout_from_str, next_layout};
use crate::monitor::{Monitor, detect_monitors};
use crate::overlay::{ErrorOverlay, KeybindOverlay, Overlay};
-use std::collections::HashSet;
+use std::collections::{HashMap, HashSet};
use std::process::Command;
use x11rb::cursor::Handle as CursorHandle;
@@ -18,8 +19,6 @@ use x11rb::rust_connection::RustConnection;
const DEFAULT_FLOAT_WIDTH_RATIO: f32 = 0.5;
const DEFAULT_FLOAT_HEIGHT_RATIO: f32 = 0.5;
-const SMART_MOVE_STEP_RATIO_VERTICAL: i32 = 4;
-const SMART_MOVE_STEP_RATIO_HORIZONTAL: i32 = 6;
#[derive(Debug, Clone, Copy)]
pub struct CachedGeometry {
@@ -30,7 +29,6 @@ pub struct CachedGeometry {
pub border_width: u16,
}
-pub type TagMask = u32;
pub fn tag_mask(tag: usize) -> TagMask {
1 << tag
}
@@ -112,14 +110,15 @@ pub struct WindowManager {
root: Window,
screen: Screen,
windows: Vec<Window>,
+ clients: HashMap<Window, Client>,
layout: LayoutBox,
- window_tags: std::collections::HashMap<Window, TagMask>,
- window_monitor: std::collections::HashMap<Window, usize>,
- window_geometry_cache: std::collections::HashMap<Window, CachedGeometry>,
+ window_tags: HashMap<Window, TagMask>,
+ window_monitor: HashMap<Window, usize>,
+ window_geometry_cache: HashMap<Window, CachedGeometry>,
gaps_enabled: bool,
floating_windows: HashSet<Window>,
fullscreen_windows: HashSet<Window>,
- floating_geometry_before_fullscreen: std::collections::HashMap<Window, (i16, i16, u16, u16, u16)>,
+ floating_geometry_before_fullscreen: HashMap<Window, (i16, i16, u16, u16, u16)>,
bars: Vec<Bar>,
tab_bars: Vec<crate::tab_bar::TabBar>,
show_bar: bool,
@@ -221,9 +220,9 @@ impl WindowManager {
&config,
display,
&font,
- monitor.x as i16,
- monitor.y as i16,
- monitor.width as u16,
+ monitor.screen_x as i16,
+ monitor.screen_y as i16,
+ monitor.screen_width as u16,
)?;
bars.push(bar);
}
@@ -237,9 +236,9 @@ impl WindowManager {
screen_number,
display,
&font,
- (monitor.x + config.gap_outer_horizontal as i32) as i16,
- (monitor.y as f32 + bar_height + config.gap_outer_vertical as f32) as i16,
- monitor.width.saturating_sub(2 * config.gap_outer_horizontal) as u16,
+ (monitor.screen_x + config.gap_outer_horizontal as i32) as i16,
+ (monitor.screen_y as f32 + bar_height + config.gap_outer_vertical as f32) as i16,
+ monitor.screen_width.saturating_sub(2 * config.gap_outer_horizontal as i32) as u16,
config.scheme_occupied,
config.scheme_selected,
)?;
@@ -269,14 +268,15 @@ impl WindowManager {
root,
screen,
windows: Vec::new(),
+ clients: HashMap::new(),
layout: Box::new(TilingLayout),
- window_tags: std::collections::HashMap::new(),
- window_monitor: std::collections::HashMap::new(),
- window_geometry_cache: std::collections::HashMap::new(),
+ window_tags: HashMap::new(),
+ window_monitor: HashMap::new(),
+ window_geometry_cache: HashMap::new(),
gaps_enabled,
floating_windows: HashSet::new(),
fullscreen_windows: HashSet::new(),
- floating_geometry_before_fullscreen: std::collections::HashMap::new(),
+ floating_geometry_before_fullscreen: HashMap::new(),
bars,
tab_bars,
show_bar: true,
@@ -443,7 +443,7 @@ impl WindowManager {
Ok(self
.monitors
.get(self.selected_monitor)
- .map(|m| m.selected_tags)
+ .map(|m| m.tagset[m.selected_tags_index])
.unwrap_or(tag_mask(0)))
}
@@ -524,15 +524,18 @@ impl WindowManager {
if let Some(focused) = self
.monitors
.get(self.selected_monitor)
- .and_then(|m| m.focused_window)
+ .and_then(|m| m.selected_client)
{
if self.floating_windows.contains(&focused) {
self.floating_windows.remove(&focused);
+ if let Some(client) = self.clients.get_mut(&focused) {
+ client.is_floating = false;
+ }
let selected_tags = self
.monitors
.get(self.selected_monitor)
- .map(|m| m.selected_tags)
+ .map(|m| m.tagset[m.selected_tags_index])
.unwrap_or(tag_mask(0));
self.window_tags.insert(focused, selected_tags);
@@ -564,6 +567,9 @@ impl WindowManager {
)?;
self.floating_windows.insert(focused);
+ if let Some(client) = self.clients.get_mut(&focused) {
+ client.is_floating = true;
+ }
self.apply_layout()?;
self.connection.flush()?;
}
@@ -571,239 +577,11 @@ impl WindowManager {
Ok(())
}
- fn collect_visible_floating_geometries(&mut self) -> Vec<(Window, CachedGeometry)> {
- let focused_window = self.monitors.get(self.selected_monitor)
- .and_then(|monitor| monitor.focused_window)
- .unwrap_or(0);
- let selected_monitor_index = self.selected_monitor;
-
- let candidate_windows: Vec<Window> = self.windows.iter()
- .filter(|&&window| {
- window != focused_window
- && self.floating_windows.contains(&window)
- && self.is_window_visible(window)
- && self.window_monitor.get(&window).copied().unwrap_or(0) == selected_monitor_index
- })
- .copied()
- .collect();
-
- candidate_windows.into_iter()
- .filter_map(|window| {
- self.get_or_query_geometry(window).ok().map(|geometry| (window, geometry))
- })
- .collect()
- }
-
- fn calculate_smart_move_position(
- &self,
- current_x: i32,
- current_y: i32,
- current_width: i32,
- current_height: i32,
- direction: i32,
- monitor: &Monitor,
- other_geometries: &[(Window, CachedGeometry)],
- ) -> (i32, i32) {
- let (gap_inner_horizontal, gap_inner_vertical, gap_outer_horizontal, gap_outer_vertical) = if self.gaps_enabled {
- (
- self.config.gap_inner_horizontal as i32,
- self.config.gap_inner_vertical as i32,
- self.config.gap_outer_horizontal as i32,
- self.config.gap_outer_vertical as i32,
- )
- } else {
- (0, 0, 0, 0)
- };
-
- match direction {
- 0 => {
- let mut target_position = i32::MIN;
- let current_top = current_y;
- let mut new_y_position = current_y - (monitor.height as i32 / SMART_MOVE_STEP_RATIO_VERTICAL);
-
- for (_other_window, other_geometry) in other_geometries {
- let other_x = other_geometry.x_position as i32;
- let other_y = other_geometry.y_position as i32;
- let other_width = other_geometry.width as i32;
- let other_height = other_geometry.height as i32;
-
- if current_x + current_width < other_x || current_x > other_x + other_width {
- continue;
- }
-
- let other_bottom = other_y + other_height + gap_inner_vertical;
- if current_top > other_bottom && new_y_position < other_bottom {
- target_position = target_position.max(other_bottom);
- }
- }
-
- if target_position != i32::MIN {
- new_y_position = target_position;
- }
- new_y_position = new_y_position.max(monitor.y as i32 + gap_outer_vertical);
- (current_x, new_y_position)
- }
- 1 => {
- let mut target_position = i32::MAX;
- let current_bottom = current_y + current_height;
- let mut new_y_position = current_y + (monitor.height as i32 / SMART_MOVE_STEP_RATIO_VERTICAL);
-
- for (_other_window, other_geometry) in other_geometries {
- let other_x = other_geometry.x_position as i32;
- let other_y = other_geometry.y_position as i32;
- let other_width = other_geometry.width as i32;
-
- if current_x + current_width < other_x || current_x > other_x + other_width {
- continue;
- }
-
- let other_top = other_y - gap_inner_vertical;
- if current_bottom < other_top && (new_y_position + current_height) > other_top {
- target_position = target_position.min(other_top - current_height);
- }
- }
-
- if target_position != i32::MAX {
- new_y_position = target_position;
- }
- new_y_position = new_y_position.min(monitor.y as i32 + monitor.height as i32 - current_height - gap_outer_vertical);
- (current_x, new_y_position)
- }
- 2 => {
- let mut target_position = i32::MIN;
- let current_left = current_x;
- let mut new_x_position = current_x - (monitor.width as i32 / SMART_MOVE_STEP_RATIO_HORIZONTAL);
-
- for (_other_window, other_geometry) in other_geometries {
- let other_x = other_geometry.x_position as i32;
- let other_y = other_geometry.y_position as i32;
- let other_width = other_geometry.width as i32;
- let other_height = other_geometry.height as i32;
-
- if current_y + current_height < other_y || current_y > other_y + other_height {
- continue;
- }
-
- let other_right = other_x + other_width + gap_inner_horizontal;
- if current_left > other_right && new_x_position < other_right {
- target_position = target_position.max(other_right);
- }
- }
-
- if target_position != i32::MIN {
- new_x_position = target_position;
- }
- new_x_position = new_x_position.max(monitor.x as i32 + gap_outer_horizontal);
- (new_x_position, current_y)
- }
- 3 => {
- let mut target_position = i32::MAX;
- let current_right = current_x + current_width;
- let mut new_x_position = current_x + (monitor.width as i32 / SMART_MOVE_STEP_RATIO_HORIZONTAL);
-
- for (_other_window, other_geometry) in other_geometries {
- let other_x = other_geometry.x_position as i32;
- let other_y = other_geometry.y_position as i32;
- let other_height = other_geometry.height as i32;
-
- if current_y + current_height < other_y || current_y > other_y + other_height {
- continue;
- }
-
- let other_left = other_x - gap_inner_horizontal;
- if current_right < other_left && (new_x_position + current_width) > other_left {
- target_position = target_position.min(other_left - current_width);
- }
- }
-
- if target_position != i32::MAX {
- new_x_position = target_position;
- }
- new_x_position = new_x_position.min(monitor.x as i32 + monitor.width as i32 - current_width - gap_outer_horizontal);
- (new_x_position, current_y)
- }
- _ => (current_x, current_y),
- }
- }
-
- fn smart_move_window(&mut self, direction: i32) -> WmResult<()> {
- let focused = match self
- .monitors
- .get(self.selected_monitor)
- .and_then(|monitor| monitor.focused_window)
- {
- Some(window) => window,
- None => return Ok(()),
- };
-
- if !self.floating_windows.contains(&focused) {
- let float_width = (self.screen.width_in_pixels as f32 * DEFAULT_FLOAT_WIDTH_RATIO) as u32;
- let float_height = (self.screen.height_in_pixels as f32 * DEFAULT_FLOAT_HEIGHT_RATIO) as u32;
- let border_width = self.config.border_width;
- let center_x_position = ((self.screen.width_in_pixels - float_width as u16) / 2) as i32;
- let center_y_position = ((self.screen.height_in_pixels - float_height as u16) / 2) as i32;
-
- self.connection.configure_window(
- focused,
- &ConfigureWindowAux::new()
- .x(center_x_position)
- .y(center_y_position)
- .width(float_width)
- .height(float_height)
- .border_width(border_width)
- .stack_mode(StackMode::ABOVE),
- )?;
- self.floating_windows.insert(focused);
- }
-
- let current_geometry = self.get_or_query_geometry(focused)?;
- let current_x = current_geometry.x_position as i32;
- let current_y = current_geometry.y_position as i32;
- let current_width = current_geometry.width as i32;
- let current_height = current_geometry.height as i32;
-
- let other_geometries = self.collect_visible_floating_geometries();
-
- let monitor = match self.monitors.get(self.selected_monitor) {
- Some(monitor) => monitor,
- None => return Ok(()),
- };
-
- let (new_x_position, new_y_position) = self.calculate_smart_move_position(
- current_x,
- current_y,
- current_width,
- current_height,
- direction,
- monitor,
- &other_geometries,
- );
-
- self.connection.configure_window(
- focused,
- &ConfigureWindowAux::new()
- .x(new_x_position)
- .y(new_y_position)
- .stack_mode(StackMode::ABOVE),
- )?;
-
- self.update_geometry_cache(focused, CachedGeometry {
- x_position: new_x_position as i16,
- y_position: new_y_position as i16,
- width: current_width as u16,
- height: current_height as u16,
- border_width: current_geometry.border_width,
- });
-
- self.connection.flush()?;
- Ok(())
- }
-
fn exchange_client(&mut self, direction: i32) -> WmResult<()> {
let focused = match self
.monitors
.get(self.selected_monitor)
- .and_then(|m| m.focused_window)
+ .and_then(|m| m.selected_client)
{
Some(win) => win,
None => return Ok(()),
@@ -942,7 +720,7 @@ impl WindowManager {
&self.connection,
&self.font,
self.display,
- monitor.selected_tags,
+ monitor.tagset[monitor.selected_tags_index],
occupied_tags,
draw_blocks,
&layout_symbol,
@@ -968,7 +746,7 @@ impl WindowManager {
return false;
}
if let Some(&tags) = self.window_tags.get(&window) {
- (tags & monitor.selected_tags) != 0
+ (tags & monitor.tagset[monitor.selected_tags_index]) != 0
} else {
false
}
@@ -976,7 +754,7 @@ impl WindowManager {
.copied()
.collect();
- let focused_window = monitor.focused_window;
+ let focused_window = monitor.selected_client;
tab_bar.draw(
&self.connection,
@@ -1003,7 +781,7 @@ impl WindowManager {
if let Some(focused) = self
.monitors
.get(self.selected_monitor)
- .and_then(|m| m.focused_window)
+ .and_then(|m| m.selected_client)
{
self.kill_client(focused)?;
}
@@ -1045,12 +823,6 @@ impl WindowManager {
self.toggle_floating()?;
}
- KeyAction::SmartMoveWin => {
- if let Arg::Int(direction) = arg {
- self.smart_move_window(*direction)?;
- }
- }
-
KeyAction::ExchangeClient => {
if let Arg::Int(direction) = arg {
self.exchange_client(*direction)?;
@@ -1109,8 +881,8 @@ impl WindowManager {
&self.connection,
&self.font,
&self.config.keybindings,
- monitor.width as u16,
- monitor.height as u16,
+ monitor.screen_width as u16,
+ monitor.screen_height as u16,
)?;
}
KeyAction::None => {}
@@ -1150,7 +922,7 @@ impl WindowManager {
if let Some(&tags) = self.window_tags.get(&window) {
let monitor = self.monitors.get(window_mon);
- let selected_tags = monitor.map(|m| m.selected_tags).unwrap_or(0);
+ let selected_tags = monitor.map(|m| m.tagset[m.selected_tags_index]).unwrap_or(0);
(tags & selected_tags) != 0
} else {
false
@@ -1158,31 +930,41 @@ impl WindowManager {
}
fn visible_windows(&self) -> Vec<Window> {
- self.windows
- .iter()
- .filter(|&&w| self.is_window_visible(w))
- .copied()
- .collect()
+ let mut result = Vec::new();
+ for monitor in &self.monitors {
+ let mut current = monitor.clients_head;
+ while let Some(window) = current {
+ if let Some(client) = self.clients.get(&window) {
+ let visible_tags = client.tags & monitor.tagset[monitor.selected_tags_index];
+ if visible_tags != 0 {
+ result.push(window);
+ }
+ current = client.next;
+ } else {
+ break;
+ }
+ }
+ }
+ result
}
fn visible_windows_on_monitor(&self, monitor_index: usize) -> Vec<Window> {
- self.windows
- .iter()
- .filter(|&&w| {
- let window_mon = self.window_monitor.get(&w).copied().unwrap_or(0);
- if window_mon != monitor_index {
- return false;
- }
- if let Some(&tags) = self.window_tags.get(&w) {
- let monitor = self.monitors.get(monitor_index);
- let selected_tags = monitor.map(|m| m.selected_tags).unwrap_or(0);
- (tags & selected_tags) != 0
+ let mut result = Vec::new();
+ if let Some(monitor) = self.monitors.get(monitor_index) {
+ let mut current = monitor.clients_head;
+ while let Some(window) = current {
+ if let Some(client) = self.clients.get(&window) {
+ let visible_tags = client.tags & monitor.tagset[monitor.selected_tags_index];
+ if visible_tags != 0 {
+ result.push(window);
+ }
+ current = client.next;
} else {
- false
+ break;
}
- })
- .copied()
- .collect()
+ }
+ }
+ result
}
fn get_monitor_at_point(&self, x: i32, y: i32) -> Option<usize> {
@@ -1215,7 +997,7 @@ impl WindowManager {
}
if let Some(monitor) = self.monitors.get_mut(self.selected_monitor) {
- monitor.selected_tags = tag_mask(tag_index);
+ monitor.tagset[monitor.selected_tags_index] = tag_mask(tag_index);
}
self.save_selected_tags()?;
@@ -1237,7 +1019,7 @@ impl WindowManager {
let selected_tags = self
.monitors
.get(self.selected_monitor)
- .map(|m| m.selected_tags)
+ .map(|m| m.tagset[m.selected_tags_index])
.unwrap_or(tag_mask(0));
let desktop = selected_tags.trailing_zeros();
@@ -1264,7 +1046,7 @@ impl WindowManager {
if let Some(focused) = self
.monitors
.get(self.selected_monitor)
- .and_then(|m| m.focused_window)
+ .and_then(|m| m.selected_client)
{
let mask = tag_mask(tag_index);
self.window_tags.insert(focused, mask);
@@ -1291,7 +1073,7 @@ impl WindowManager {
let current = self
.monitors
.get(self.selected_monitor)
- .and_then(|m| m.focused_window);
+ .and_then(|m| m.selected_client);
let next_window = if let Some(current) = current {
if let Some(current_index) = visible.iter().position(|&w| w == current) {
@@ -1373,7 +1155,7 @@ impl WindowManager {
let focused_window = match self
.monitors
.get(self.selected_monitor)
- .and_then(|monitor| monitor.focused_window)
+ .and_then(|monitor| monitor.selected_client)
{
Some(window) => window,
None => return Ok(()),
@@ -1390,7 +1172,7 @@ impl WindowManager {
let focused_window = match self
.monitors
.get(self.selected_monitor)
- .and_then(|monitor| monitor.focused_window)
+ .and_then(|monitor| monitor.selected_client)
{
Some(window) => window,
None => return Ok(()),
@@ -1615,18 +1397,18 @@ impl WindowManager {
(0, 0)
};
- let window_x = monitor.x + outer_gap_h as i32;
- let window_y = monitor.y + outer_gap_v as i32;
- let window_width = monitor.width.saturating_sub(2 * outer_gap_h).saturating_sub(2 * border_width);
- let window_height = monitor.height.saturating_sub(2 * outer_gap_v).saturating_sub(2 * border_width);
+ let window_x = monitor.screen_x + outer_gap_h as i32;
+ let window_y = monitor.screen_y + outer_gap_v as i32;
+ let window_width = monitor.screen_width.saturating_sub(2 * outer_gap_h as i32).saturating_sub(2 * border_width as i32);
+ let window_height = monitor.screen_height.saturating_sub(2 * outer_gap_v as i32).saturating_sub(2 * border_width as i32);
self.connection.configure_window(
window,
&x11rb::protocol::xproto::ConfigureWindowAux::new()
.x(window_x)
.y(window_y)
- .width(window_width)
- .height(window_height),
+ .width(window_width as u32)
+ .height(window_height as u32),
)?;
}
self.connection.flush()?;
@@ -1699,6 +1481,9 @@ impl WindowManager {
)?;
self.fullscreen_windows.insert(window);
+ if let Some(client) = self.clients.get_mut(&window) {
+ client.is_fullscreen = true;
+ }
let monitor_idx = *self.window_monitor.get(&window).unwrap_or(&self.selected_monitor);
let monitor = &self.monitors[monitor_idx];
@@ -1713,10 +1498,10 @@ impl WindowManager {
window,
&x11rb::protocol::xproto::ConfigureWindowAux::new()
.border_width(0)
- .x(monitor.x)
- .y(monitor.y)
- .width(monitor.width as u32)
- .height(monitor.height as u32)
+ .x(monitor.screen_x)
+ .y(monitor.screen_y)
+ .width(monitor.screen_width as u32)
+ .height(monitor.screen_height as u32)
.stack_mode(x11rb::protocol::xproto::StackMode::ABOVE),
)?;
@@ -1734,6 +1519,9 @@ impl WindowManager {
)?;
self.fullscreen_windows.remove(&window);
+ if let Some(client) = self.clients.get_mut(&window) {
+ client.is_fullscreen = false;
+ }
self.connection.configure_window(
window,
@@ -1854,7 +1642,7 @@ impl WindowManager {
let parent_tags = self.window_tags.get(&parent).copied().unwrap_or_else(|| {
self.monitors
.get(self.selected_monitor)
- .map(|m| m.selected_tags)
+ .map(|m| m.tagset[m.selected_tags_index])
.unwrap_or(tag_mask(0))
});
let parent_monitor = self.window_monitor.get(&parent).copied().unwrap_or(self.selected_monitor);
@@ -1862,7 +1650,7 @@ impl WindowManager {
} else {
let selected_tags = self.monitors
.get(self.selected_monitor)
- .map(|m| m.selected_tags)
+ .map(|m| m.tagset[m.selected_tags_index])
.unwrap_or(tag_mask(0));
(selected_tags, self.selected_monitor)
};
@@ -1870,14 +1658,29 @@ impl WindowManager {
let monitor = self.monitors[monitor_index].clone();
let border_width = self.config.border_width;
- if window_x + (window_width as i32) + (2 * border_width as i32) > monitor.x + monitor.width as i32 {
- window_x = monitor.x + monitor.width as i32 - (window_width as i32) - (2 * border_width as i32);
+ let mut client = Client::new(window, monitor_index, window_tags);
+ client.x_position = window_x as i16;
+ client.y_position = window_y as i16;
+ client.width = window_width as u16;
+ client.height = window_height as u16;
+ client.old_x_position = window_x as i16;
+ client.old_y_position = window_y as i16;
+ client.old_width = window_width as u16;
+ client.old_height = window_height as u16;
+ client.border_width = border_width as u16;
+ client.old_border_width = border_width as u16;
+
+ self.update_size_hints(&mut client)?;
+ self.update_window_title(&mut client)?;
+
+ if window_x + (window_width as i32) + (2 * border_width as i32) > monitor.screen_x + monitor.screen_width as i32 {
+ window_x = monitor.screen_x + monitor.screen_width as i32 - (window_width as i32) - (2 * border_width as i32);
}
- if window_y + (window_height as i32) + (2 * border_width as i32) > monitor.y + monitor.height as i32 {
- window_y = monitor.y + monitor.height as i32 - (window_height as i32) - (2 * border_width as i32);
+ if window_y + (window_height as i32) + (2 * border_width as i32) > monitor.screen_y + monitor.screen_height as i32 {
+ window_y = monitor.screen_y + monitor.screen_height as i32 - (window_height as i32) - (2 * border_width as i32);
}
- window_x = window_x.max(monitor.x);
- window_y = window_y.max(monitor.y);
+ window_x = window_x.max(monitor.screen_x);
+ window_y = window_y.max(monitor.screen_y);
let is_transient = transient_parent.is_some();
let is_dialog = self.is_dialog_window(window);
@@ -1905,6 +1708,12 @@ impl WindowManager {
),
)?;
+ client.is_floating = is_transient || is_dialog;
+
+ self.clients.insert(window, client);
+ self.attach(window, monitor_index);
+ self.attach_stack(window, monitor_index);
+
self.windows.push(window);
self.window_tags.insert(window, window_tags);
self.window_monitor.insert(window, monitor_index);
@@ -1918,13 +1727,13 @@ impl WindowManager {
let parent_center_y = parent_geom.y as i32 + (parent_geom.height as i32 / 2);
(parent_center_x, parent_center_y)
} else {
- let monitor_center_x = monitor.x + (monitor.width as i32 / 2);
- let monitor_center_y = monitor.y + (monitor.height as i32 / 2);
+ let monitor_center_x = monitor.screen_x + (monitor.screen_width as i32 / 2);
+ let monitor_center_y = monitor.screen_y + (monitor.screen_height as i32 / 2);
(monitor_center_x, monitor_center_y)
}
} else {
- let monitor_center_x = monitor.x + (monitor.width as i32 / 2);
- let monitor_center_y = monitor.y + (monitor.height as i32 / 2);
+ let monitor_center_x = monitor.screen_x + (monitor.screen_width as i32 / 2);
+ let monitor_center_y = monitor.screen_y + (monitor.screen_height as i32 / 2);
(monitor_center_x, monitor_center_y)
};
@@ -1932,11 +1741,11 @@ impl WindowManager {
let positioned_y = center_y - (window_height as i32 / 2);
let clamped_x = positioned_x
- .max(monitor.x)
- .min(monitor.x + monitor.width as i32 - window_width as i32);
+ .max(monitor.screen_x)
+ .min(monitor.screen_x + monitor.screen_width as i32 - window_width as i32);
let clamped_y = positioned_y
- .max(monitor.y)
- .min(monitor.y + monitor.height as i32 - window_height as i32);
+ .max(monitor.screen_y)
+ .min(monitor.screen_y + monitor.screen_height as i32 - window_height as i32);
self.update_geometry_cache(window, CachedGeometry {
x_position: clamped_x as i16,
@@ -1965,17 +1774,17 @@ impl WindowManager {
.and_then(|idx| self.monitors.get(idx))
.unwrap_or(&monitor);
- let float_width = (cursor_monitor.width as f32 * 0.6) as u32;
- let float_height = (cursor_monitor.height as f32 * 0.6) as u32;
+ let float_width = (cursor_monitor.screen_width as f32 * 0.6) as u32;
+ let float_height = (cursor_monitor.screen_height as f32 * 0.6) as u32;
let spawn_x = pointer.root_x as i32 - (float_width as i32 / 2);
let spawn_y = pointer.root_y as i32 - (float_height as i32 / 2);
let clamped_x = spawn_x
- .max(cursor_monitor.x)
- .min(cursor_monitor.x + cursor_monitor.width as i32 - float_width as i32);
+ .max(cursor_monitor.screen_x)
+ .min(cursor_monitor.screen_x + cursor_monitor.screen_width as i32 - float_width as i32);
let clamped_y = spawn_y
- .max(cursor_monitor.y)
- .min(cursor_monitor.y + cursor_monitor.height as i32 - float_height as i32);
+ .max(cursor_monitor.screen_y)
+ .min(cursor_monitor.screen_y + cursor_monitor.screen_height as i32 - float_height as i32);
self.connection.configure_window(
window,
@@ -1988,6 +1797,9 @@ impl WindowManager {
.stack_mode(StackMode::ABOVE),
)?;
+ if let Some(client) = self.clients.get_mut(&window) {
+ client.is_floating = true;
+ }
self.floating_windows.insert(window);
}
}
@@ -2013,7 +1825,7 @@ impl WindowManager {
let old_focused = self.previous_focused;
if let Some(monitor) = self.monitors.get_mut(self.selected_monitor) {
- monitor.focused_window = Some(window);
+ monitor.selected_client = Some(window);
}
self.connection
@@ -2410,7 +2222,7 @@ impl WindowManager {
{
return false;
}
- let monitor_tags = self.monitors.get(monitor_index).map(|m| m.selected_tags).unwrap_or(0);
+ let monitor_tags = self.monitors.get(monitor_index).map(|m| m.tagset[m.selected_tags_index]).unwrap_or(0);
if let Some(&tags) = self.window_tags.get(&window) {
(tags & monitor_tags) != 0
} else {
@@ -2474,18 +2286,18 @@ impl WindowManager {
if value_mask.contains(ConfigWindow::X) {
let mut x = event.x as i32;
- x = x.max(monitor.x);
- if x + event.width as i32 + 2 * border_width as i32 > monitor.x + monitor.width as i32 {
- x = monitor.x + monitor.width as i32 - event.width as i32 - 2 * border_width as i32;
+ x = x.max(monitor.screen_x);
+ if x + event.width as i32 + 2 * border_width as i32 > monitor.screen_x + monitor.screen_width as i32 {
+ x = monitor.screen_x + monitor.screen_width as i32 - event.width as i32 - 2 * border_width as i32;
}
config = config.x(x);
}
if value_mask.contains(ConfigWindow::Y) {
let mut y = event.y as i32;
- y = y.max(monitor.y);
- if y + event.height as i32 + 2 * border_width as i32 > monitor.y + monitor.height as i32 {
- y = monitor.y + monitor.height as i32 - event.height as i32 - 2 * border_width as i32;
+ y = y.max(monitor.screen_y);
+ if y + event.height as i32 + 2 * border_width as i32 > monitor.screen_y + monitor.screen_height as i32 {
+ y = monitor.screen_y + monitor.screen_height as i32 - event.height as i32 - 2 * border_width as i32;
}
config = config.y(y);
}
@@ -2510,9 +2322,9 @@ impl WindowManager {
let final_x = if value_mask.contains(ConfigWindow::X) {
let mut x = event.x as i32;
- x = x.max(monitor.x);
- if x + event.width as i32 + 2 * border_width as i32 > monitor.x + monitor.width as i32 {
- x = monitor.x + monitor.width as i32 - event.width as i32 - 2 * border_width as i32;
+ x = x.max(monitor.screen_x);
+ if x + event.width as i32 + 2 * border_width as i32 > monitor.screen_x + monitor.screen_width as i32 {
+ x = monitor.screen_x + monitor.screen_width as i32 - event.width as i32 - 2 * border_width as i32;
}
x as i16
} else {
@@ -2521,9 +2333,9 @@ impl WindowManager {
let final_y = if value_mask.contains(ConfigWindow::Y) {
let mut y = event.y as i32;
- y = y.max(monitor.y);
- if y + event.height as i32 + 2 * border_width as i32 > monitor.y + monitor.height as i32 {
- y = monitor.y + monitor.height as i32 - event.height as i32 - 2 * border_width as i32;
+ y = y.max(monitor.screen_y);
+ if y + event.height as i32 + 2 * border_width as i32 > monitor.screen_y + monitor.screen_height as i32 {
+ y = monitor.screen_y + monitor.screen_height as i32 - event.height as i32 - 2 * border_width as i32;
}
y as i16
} else {
@@ -2606,31 +2418,21 @@ impl WindowManager {
}
};
- let monitor_selected_tags = monitor.selected_tags;
- let monitor_x = monitor.x;
- let monitor_y = monitor.y;
- let monitor_width = monitor.width;
- let monitor_height = monitor.height;
-
- let visible: Vec<Window> = self
- .windows
- .iter()
- .filter(|&&window| {
- let window_monitor_index = self.window_monitor.get(&window).copied().unwrap_or(0);
- if window_monitor_index != monitor_index
- || self.floating_windows.contains(&window)
- || self.fullscreen_windows.contains(&window)
- {
- return false;
- }
- if let Some(&tags) = self.window_tags.get(&window) {
- (tags & monitor_selected_tags) != 0
- } else {
- false
- }
- })
- .copied()
- .collect();
+ let monitor_x = monitor.screen_x;
+ let monitor_y = monitor.screen_y;
+ let monitor_width = monitor.screen_width;
+ let monitor_height = monitor.screen_height;
+
+ let mut visible: Vec<Window> = Vec::new();
+ let mut current = self.next_tiled(monitor.clients_head, monitor);
+ while let Some(window) = current {
+ visible.push(window);
+ if let Some(client) = self.clients.get(&window) {
+ current = self.next_tiled(client.next, monitor);
+ } else {
+ break;
+ }
+ }
let bar_height = if self.show_bar {
self.bars
@@ -2640,11 +2442,11 @@ impl WindowManager {
} else {
0
};
- let usable_height = monitor_height.saturating_sub(bar_height);
+ let usable_height = monitor_height.saturating_sub(bar_height as i32);
let geometries = self
.layout
- .arrange(&visible, monitor_width, usable_height, &gaps);
+ .arrange(&visible, monitor_width as u32, usable_height as u32, &gaps);
for (window, geometry) in visible.iter().zip(geometries.iter()) {
let adjusted_width = geometry.width.saturating_sub(2 * border_width);
@@ -2700,9 +2502,9 @@ impl WindowManager {
0.0
};
- let tab_bar_x = (monitor.x + outer_horizontal as i32) as i16;
- let tab_bar_y = (monitor.y as f32 + bar_height + outer_vertical as f32) as i16;
- let tab_bar_width = monitor.width.saturating_sub(2 * outer_horizontal) as u16;
+ let tab_bar_x = (monitor.screen_x + outer_horizontal as i32) as i16;
+ let tab_bar_y = (monitor.screen_y as f32 + bar_height + outer_vertical as f32) as i16;
+ let tab_bar_width = monitor.screen_width.saturating_sub(2 * outer_horizontal as i32) as u16;
if let Err(e) = self.tab_bars[monitor_index].reposition(
&self.connection,
@@ -2730,7 +2532,7 @@ impl WindowManager {
}
if let Some(monitor) = self.monitors.get(monitor_index) {
if let Some(&tags) = self.window_tags.get(&window) {
- return (tags & monitor.selected_tags) != 0;
+ return (tags & monitor.tagset[monitor.selected_tags_index]) != 0;
}
}
false
@@ -2812,9 +2614,300 @@ impl WindowManager {
Ok(())
}
+ fn update_size_hints(&mut self, client: &mut Client) -> WmResult<()> {
+ let size_hints = self.connection
+ .get_property(
+ false,
+ client.window,
+ x11rb::protocol::xproto::AtomEnum::WM_NORMAL_HINTS,
+ x11rb::protocol::xproto::AtomEnum::WM_SIZE_HINTS,
+ 0,
+ 18,
+ )?
+ .reply()?;
+
+ if size_hints.value.is_empty() {
+ client.hints_valid = false;
+ return Ok(());
+ }
+
+ if size_hints.value.len() < 18 * 4 {
+ client.hints_valid = false;
+ return Ok(());
+ }
+
+ let read_u32 = |offset: usize| -> u32 {
+ let bytes = &size_hints.value[offset * 4..(offset + 1) * 4];
+ u32::from_ne_bytes([bytes[0], bytes[1], bytes[2], bytes[3]])
+ };
+
+ let flags = read_u32(0);
+
+ const P_SIZE: u32 = 1 << 3;
+ const P_MIN_SIZE: u32 = 1 << 4;
+ const P_MAX_SIZE: u32 = 1 << 5;
+ const P_RESIZE_INC: u32 = 1 << 6;
+ const P_ASPECT: u32 = 1 << 7;
+ const P_BASE_SIZE: u32 = 1 << 8;
+
+ if flags & P_BASE_SIZE != 0 {
+ client.base_width = read_u32(8) as i32;
+ client.base_height = read_u32(9) as i32;
+ } else if flags & P_MIN_SIZE != 0 {
+ client.base_width = read_u32(5) as i32;
+ client.base_height = read_u32(6) as i32;
+ } else {
+ client.base_width = 0;
+ client.base_height = 0;
+ }
+
+ if flags & P_RESIZE_INC != 0 {
+ client.increment_width = read_u32(10) as i32;
+ client.increment_height = read_u32(11) as i32;
+ } else {
+ client.increment_width = 0;
+ client.increment_height = 0;
+ }
+
+ if flags & P_MAX_SIZE != 0 {
+ client.max_width = read_u32(7) as i32;
+ client.max_height = read_u32(8) as i32;
+ } else {
+ client.max_width = 0;
+ client.max_height = 0;
+ }
+
+ if flags & P_MIN_SIZE != 0 {
+ client.min_width = read_u32(5) as i32;
+ client.min_height = read_u32(6) as i32;
+ } else if flags & P_SIZE != 0 {
+ client.min_width = read_u32(3) as i32;
+ client.min_height = read_u32(4) as i32;
+ } else {
+ client.min_width = 0;
+ client.min_height = 0;
+ }
+
+ if flags & P_ASPECT != 0 {
+ client.min_aspect = (read_u32(12) as f32) / (read_u32(13) as f32).max(1.0);
+ client.max_aspect = (read_u32(14) as f32) / (read_u32(15) as f32).max(1.0);
+ } else {
+ client.min_aspect = 0.0;
+ client.max_aspect = 0.0;
+ }
+
+ client.is_fixed = client.max_width > 0
+ && client.max_height > 0
+ && client.max_width == client.min_width
+ && client.max_height == client.min_height;
+
+ client.hints_valid = true;
+ Ok(())
+ }
+
+ fn update_window_title(&mut self, client: &mut Client) -> WmResult<()> {
+ let name = self.connection
+ .get_property(
+ false,
+ client.window,
+ x11rb::protocol::xproto::AtomEnum::WM_NAME,
+ x11rb::protocol::xproto::AtomEnum::STRING,
+ 0,
+ 256,
+ )?
+ .reply()?;
+
+ if !name.value.is_empty() {
+ if let Ok(title) = String::from_utf8(name.value.clone()) {
+ client.name = title;
+ }
+ }
+
+ Ok(())
+ }
+
+ fn apply_size_hints(&self, client: &Client, mut width: i32, mut height: i32) -> (i32, i32) {
+ if !client.hints_valid {
+ return (width.max(1), height.max(1));
+ }
+
+ if client.min_width > 0 {
+ width = width.max(client.min_width);
+ }
+ if client.min_height > 0 {
+ height = height.max(client.min_height);
+ }
+
+ if client.max_width > 0 {
+ width = width.min(client.max_width);
+ }
+ if client.max_height > 0 {
+ height = height.min(client.max_height);
+ }
+
+ if client.increment_width > 0 {
+ width -= client.base_width;
+ width -= width % client.increment_width;
+ width += client.base_width;
+ }
+ if client.increment_height > 0 {
+ height -= client.base_height;
+ height -= height % client.increment_height;
+ height += client.base_height;
+ }
+
+ if client.min_aspect > 0.0 || client.max_aspect > 0.0 {
+ let actual_aspect = width as f32 / height as f32;
+
+ if client.max_aspect > 0.0 && actual_aspect > client.max_aspect {
+ width = (height as f32 * client.max_aspect) as i32;
+ } else if client.min_aspect > 0.0 && actual_aspect < client.min_aspect {
+ height = (width as f32 / client.min_aspect) as i32;
+ }
+ }
+
+ (width.max(1), height.max(1))
+ }
+
+ fn next_tiled(&self, start: Option<Window>, monitor: &Monitor) -> Option<Window> {
+ let mut current = start;
+ while let Some(window) = current {
+ if let Some(client) = self.clients.get(&window) {
+ let visible_tags = client.tags & monitor.tagset[monitor.selected_tags_index];
+ if visible_tags != 0 && !client.is_floating {
+ return Some(window);
+ }
+ current = client.next;
+ } else {
+ break;
+ }
+ }
+ None
+ }
+
+ fn count_tiled(&self, monitor: &Monitor) -> usize {
+ let mut count = 0;
+ let mut current = monitor.clients_head;
+ while let Some(window) = current {
+ if let Some(client) = self.clients.get(&window) {
+ let visible_tags = client.tags & monitor.tagset[monitor.selected_tags_index];
+ if visible_tags != 0 && !client.is_floating {
+ count += 1;
+ }
+ current = client.next;
+ } else {
+ break;
+ }
+ }
+ count
+ }
+
+ fn attach(&mut self, window: Window, monitor_index: usize) {
+ if let Some(monitor) = self.monitors.get_mut(monitor_index) {
+ if let Some(client) = self.clients.get_mut(&window) {
+ client.next = monitor.clients_head;
+ monitor.clients_head = Some(window);
+ }
+ }
+ }
+
+ fn detach(&mut self, window: Window) {
+ let monitor_index = self.clients.get(&window).map(|c| c.monitor_index);
+ if let Some(monitor_index) = monitor_index {
+ if let Some(monitor) = self.monitors.get_mut(monitor_index) {
+ if monitor.clients_head == Some(window) {
+ if let Some(client) = self.clients.get(&window) {
+ monitor.clients_head = client.next;
+ }
+ } else {
+ let mut current = monitor.clients_head;
+ while let Some(current_window) = current {
+ if let Some(current_client) = self.clients.get(¤t_window) {
+ if current_client.next == Some(window) {
+ let new_next = self.clients.get(&window).and_then(|c| c.next);
+ if let Some(current_client_mut) = self.clients.get_mut(¤t_window) {
+ current_client_mut.next = new_next;
+ }
+ break;
+ }
+ current = current_client.next;
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ fn attach_stack(&mut self, window: Window, monitor_index: usize) {
+ if let Some(monitor) = self.monitors.get_mut(monitor_index) {
+ if let Some(client) = self.clients.get_mut(&window) {
+ client.stack_next = monitor.stack_head;
+ monitor.stack_head = Some(window);
+ }
+ }
+ }
+
+ fn detach_stack(&mut self, window: Window) {
+ let monitor_index = self.clients.get(&window).map(|c| c.monitor_index);
+ if let Some(monitor_index) = monitor_index {
+ if let Some(monitor) = self.monitors.get_mut(monitor_index) {
+ if monitor.stack_head == Some(window) {
+ if let Some(client) = self.clients.get(&window) {
+ monitor.stack_head = client.stack_next;
+ }
+ let should_update_selected = monitor.selected_client == Some(window);
+ let mut new_selected: Option<Window> = None;
+ if should_update_selected {
+ let mut stack_current = monitor.stack_head;
+ while let Some(stack_window) = stack_current {
+ if let Some(stack_client) = self.clients.get(&stack_window) {
+ if self.is_window_visible(stack_window) {
+ new_selected = Some(stack_window);
+ break;
+ }
+ stack_current = stack_client.stack_next;
+ } else {
+ break;
+ }
+ }
+ }
+ if should_update_selected {
+ if let Some(monitor) = self.monitors.get_mut(monitor_index) {
+ monitor.selected_client = new_selected;
+ }
+ }
+ } else {
+ let mut current = monitor.stack_head;
+ while let Some(current_window) = current {
+ if let Some(current_client) = self.clients.get(¤t_window) {
+ if current_client.stack_next == Some(window) {
+ let new_stack_next = self.clients.get(&window).and_then(|c| c.stack_next);
+ if let Some(current_client_mut) = self.clients.get_mut(¤t_window) {
+ current_client_mut.stack_next = new_stack_next;
+ }
+ break;
+ }
+ current = current_client.stack_next;
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
fn remove_window(&mut self, window: Window) -> WmResult<()> {
let initial_count = self.windows.len();
+ if self.clients.contains_key(&window) {
+ self.detach(window);
+ self.detach_stack(window);
+ self.clients.remove(&window);
+ }
+
self.windows.retain(|&w| w != window);
self.window_tags.remove(&window);
self.window_monitor.remove(&window);
@@ -2825,13 +2918,13 @@ impl WindowManager {
let focused = self
.monitors
.get(self.selected_monitor)
- .and_then(|m| m.focused_window);
+ .and_then(|m| m.selected_client);
if focused == Some(window) {
let visible = self.visible_windows_on_monitor(self.selected_monitor);
if let Some(&new_win) = visible.last() {
self.set_focus(new_win)?;
} else if let Some(monitor) = self.monitors.get_mut(self.selected_monitor) {
- monitor.focused_window = None;
+ monitor.selected_client = None;
}
}
diff --git a/templates/oxwm.lua b/templates/oxwm.lua
index d7023d1..4b16123 100644
--- a/templates/oxwm.lua
+++ b/templates/oxwm.lua
@@ -148,11 +148,6 @@ function oxwm.client.focus_direction(direction) end
---@return table Action table for keybinding
function oxwm.client.swap_direction(direction) end
----Smart move window (move or swap)
----@param direction "up"|"down"|"left"|"right" Direction to move
----@return table Action table for keybinding
-function oxwm.client.smart_move(direction) end
-
---Focus stack (next/previous window)
---@param dir integer Direction (1 for next, -1 for previous)
---@return table Action table for keybinding