mirror of
https://github.com/YaLTeR/niri.git
synced 2024-09-11 20:37:25 +03:00
Compare commits
12 Commits
f70e1018da
...
f4e2e5541b
Author | SHA1 | Date | |
---|---|---|---|
|
f4e2e5541b | ||
|
dfc2d452c5 | ||
|
66f23c3980 | ||
|
7a6ab31ad7 | ||
|
3d6040ceab | ||
|
c498ffcc34 | ||
|
7bcb649d7a | ||
|
176de91342 | ||
|
13911640b0 | ||
|
5da2ad89dd | ||
|
f76b3bb794 | ||
|
b12396d8a1 |
@ -60,8 +60,8 @@ pub struct Config {
|
||||
|
||||
#[derive(knuffel::Decode, Debug, Default, PartialEq)]
|
||||
pub struct Input {
|
||||
#[knuffel(child, default)]
|
||||
pub keyboard: Keyboard,
|
||||
#[knuffel(children(name = "keyboard"), default)]
|
||||
pub keyboards: Vec<Keyboard>,
|
||||
#[knuffel(child, default)]
|
||||
pub touchpad: Touchpad,
|
||||
#[knuffel(child, default)]
|
||||
@ -82,8 +82,28 @@ pub struct Input {
|
||||
pub workspace_auto_back_and_forth: bool,
|
||||
}
|
||||
|
||||
#[derive(knuffel::Decode, Debug, PartialEq, Eq)]
|
||||
impl Input {
|
||||
pub fn fallback_keyboard(&self) -> Keyboard {
|
||||
self.keyboards
|
||||
.iter()
|
||||
.find(|keyboard| keyboard.name.is_none())
|
||||
.cloned()
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn keyboard_named<K: AsRef<str>>(&self, name: K) -> Keyboard {
|
||||
self.keyboards
|
||||
.iter()
|
||||
.find(|keyboard| keyboard.name.as_deref() == Some(name.as_ref()))
|
||||
.cloned()
|
||||
.unwrap_or_else(|| self.fallback_keyboard())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(knuffel::Decode, Debug, PartialEq, Eq, Clone)]
|
||||
pub struct Keyboard {
|
||||
#[knuffel(argument, unwrap(argument))]
|
||||
pub name: Option<String>,
|
||||
#[knuffel(child, default)]
|
||||
pub xkb: Xkb,
|
||||
// The defaults were chosen to match wlroots and sway.
|
||||
@ -98,6 +118,7 @@ pub struct Keyboard {
|
||||
impl Default for Keyboard {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
name: None,
|
||||
xkb: Default::default(),
|
||||
repeat_delay: 600,
|
||||
repeat_rate: 25,
|
||||
@ -144,7 +165,7 @@ pub enum CenterFocusedColumn {
|
||||
OnOverflow,
|
||||
}
|
||||
|
||||
#[derive(knuffel::DecodeScalar, Debug, Default, PartialEq, Eq)]
|
||||
#[derive(knuffel::DecodeScalar, Debug, Default, PartialEq, Eq, Clone)]
|
||||
pub enum TrackLayout {
|
||||
/// The layout change is global.
|
||||
#[default]
|
||||
@ -2657,6 +2678,16 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
keyboard "test_keyboard" {
|
||||
repeat-delay 500
|
||||
repeat-rate 30
|
||||
track-layout "window"
|
||||
xkb {
|
||||
layout "us,ru"
|
||||
options "grp:win_space_toggle"
|
||||
}
|
||||
}
|
||||
|
||||
touchpad {
|
||||
tap
|
||||
dwt
|
||||
@ -2821,16 +2852,30 @@ mod tests {
|
||||
"##,
|
||||
Config {
|
||||
input: Input {
|
||||
keyboard: Keyboard {
|
||||
xkb: Xkb {
|
||||
layout: "us,ru".to_owned(),
|
||||
options: Some("grp:win_space_toggle".to_owned()),
|
||||
keyboards: vec![
|
||||
Keyboard {
|
||||
xkb: Xkb {
|
||||
layout: "us,ru".to_owned(),
|
||||
options: Some("grp:win_space_toggle".to_owned()),
|
||||
..Default::default()
|
||||
},
|
||||
repeat_delay: 600,
|
||||
repeat_rate: 25,
|
||||
track_layout: TrackLayout::Window,
|
||||
..Default::default()
|
||||
},
|
||||
repeat_delay: 600,
|
||||
repeat_rate: 25,
|
||||
track_layout: TrackLayout::Window,
|
||||
},
|
||||
Keyboard {
|
||||
xkb: Xkb {
|
||||
layout: "us,ru".to_owned(),
|
||||
options: Some("grp:win_space_toggle".to_owned()),
|
||||
..Default::default()
|
||||
},
|
||||
repeat_delay: 500,
|
||||
repeat_rate: 30,
|
||||
track_layout: TrackLayout::Window,
|
||||
name: Some("test_keyboard".to_owned()),
|
||||
},
|
||||
],
|
||||
touchpad: Touchpad {
|
||||
off: false,
|
||||
tap: true,
|
||||
@ -3315,7 +3360,7 @@ mod tests {
|
||||
#[test]
|
||||
fn default_repeat_params() {
|
||||
let config = Config::parse("config.kdl", "").unwrap();
|
||||
assert_eq!(config.input.keyboard.repeat_delay, 600);
|
||||
assert_eq!(config.input.keyboard.repeat_rate, 25);
|
||||
assert_eq!(config.input.fallback_keyboard().repeat_delay, 600);
|
||||
assert_eq!(config.input.fallback_keyboard().repeat_rate, 25);
|
||||
}
|
||||
}
|
||||
|
@ -287,7 +287,12 @@ impl State {
|
||||
Some(pos + target_geo.loc.to_f64())
|
||||
}
|
||||
|
||||
fn on_keyboard<I: InputBackend>(&mut self, event: I::KeyboardKeyEvent) {
|
||||
fn on_keyboard<I: InputBackend>(&mut self, event: I::KeyboardKeyEvent)
|
||||
where
|
||||
I::Device: 'static,
|
||||
{
|
||||
self.apply_keyboard_config::<I>(&event);
|
||||
|
||||
let comp_mod = self.backend.mod_key();
|
||||
|
||||
let serial = SERIAL_COUNTER.next_serial();
|
||||
@ -383,6 +388,57 @@ impl State {
|
||||
self.niri.bind_repeat_timer = Some(token);
|
||||
}
|
||||
|
||||
fn apply_keyboard_config<I: InputBackend>(&mut self, event: &I::KeyboardKeyEvent)
|
||||
where
|
||||
I::Device: 'static,
|
||||
{
|
||||
let device = event.device();
|
||||
|
||||
let keyboard_config =
|
||||
if let Some(input_device) = (&device as &dyn Any).downcast_ref::<input::Device>() {
|
||||
if let Some(cached) = self.niri.keyboard_device_cache.get(input_device).cloned() {
|
||||
cached
|
||||
} else {
|
||||
let keyboard_config = self
|
||||
.niri
|
||||
.config
|
||||
.borrow()
|
||||
.input
|
||||
.keyboard_named(device.name());
|
||||
|
||||
self.niri
|
||||
.keyboard_device_cache
|
||||
.insert(input_device.clone(), keyboard_config.clone());
|
||||
|
||||
keyboard_config
|
||||
}
|
||||
} else {
|
||||
self.niri.config.borrow().input.fallback_keyboard()
|
||||
};
|
||||
|
||||
if keyboard_config != self.niri.current_keyboard {
|
||||
let keyboard = self.niri.seat.get_keyboard().unwrap();
|
||||
|
||||
if keyboard_config.xkb != self.niri.current_keyboard.xkb {
|
||||
if let Err(err) = keyboard.set_xkb_config(self, keyboard_config.xkb.to_xkb_config())
|
||||
{
|
||||
warn!("error updating xkb config: {err:?}");
|
||||
}
|
||||
}
|
||||
|
||||
if keyboard_config.repeat_rate != self.niri.current_keyboard.repeat_rate
|
||||
|| keyboard_config.repeat_delay != self.niri.current_keyboard.repeat_delay
|
||||
{
|
||||
keyboard.change_repeat_info(
|
||||
keyboard_config.repeat_rate.into(),
|
||||
keyboard_config.repeat_delay.into(),
|
||||
);
|
||||
}
|
||||
|
||||
self.niri.current_keyboard = keyboard_config;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_bind(&mut self, bind: Bind) {
|
||||
let Some(cooldown) = bind.cooldown else {
|
||||
self.do_action(bind.action, bind.allow_when_locked);
|
||||
|
@ -4263,6 +4263,101 @@ mod tests {
|
||||
compute_working_area(&output, struts);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_window_height_recomputes_to_auto() {
|
||||
let ops = [
|
||||
Op::AddOutput(1),
|
||||
Op::AddWindow {
|
||||
id: 0,
|
||||
bbox: Rectangle::from_loc_and_size((0, 0), (100, 200)),
|
||||
min_max_size: Default::default(),
|
||||
},
|
||||
Op::AddWindow {
|
||||
id: 1,
|
||||
bbox: Rectangle::from_loc_and_size((0, 0), (100, 200)),
|
||||
min_max_size: Default::default(),
|
||||
},
|
||||
Op::ConsumeOrExpelWindowLeft,
|
||||
Op::AddWindow {
|
||||
id: 2,
|
||||
bbox: Rectangle::from_loc_and_size((0, 0), (100, 200)),
|
||||
min_max_size: Default::default(),
|
||||
},
|
||||
Op::ConsumeOrExpelWindowLeft,
|
||||
Op::SetWindowHeight(SizeChange::SetFixed(100)),
|
||||
Op::FocusWindowUp,
|
||||
Op::SetWindowHeight(SizeChange::SetFixed(200)),
|
||||
];
|
||||
|
||||
check_ops(&ops);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn one_window_in_column_becomes_weight_1() {
|
||||
let ops = [
|
||||
Op::AddOutput(1),
|
||||
Op::AddWindow {
|
||||
id: 0,
|
||||
bbox: Rectangle::from_loc_and_size((0, 0), (100, 200)),
|
||||
min_max_size: Default::default(),
|
||||
},
|
||||
Op::AddWindow {
|
||||
id: 1,
|
||||
bbox: Rectangle::from_loc_and_size((0, 0), (100, 200)),
|
||||
min_max_size: Default::default(),
|
||||
},
|
||||
Op::ConsumeOrExpelWindowLeft,
|
||||
Op::AddWindow {
|
||||
id: 2,
|
||||
bbox: Rectangle::from_loc_and_size((0, 0), (100, 200)),
|
||||
min_max_size: Default::default(),
|
||||
},
|
||||
Op::ConsumeOrExpelWindowLeft,
|
||||
Op::SetWindowHeight(SizeChange::SetFixed(100)),
|
||||
Op::Communicate(2),
|
||||
Op::FocusWindowUp,
|
||||
Op::SetWindowHeight(SizeChange::SetFixed(200)),
|
||||
Op::Communicate(1),
|
||||
Op::CloseWindow(0),
|
||||
Op::CloseWindow(1),
|
||||
];
|
||||
|
||||
check_ops(&ops);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn one_window_in_column_becomes_weight_1_after_fullscreen() {
|
||||
let ops = [
|
||||
Op::AddOutput(1),
|
||||
Op::AddWindow {
|
||||
id: 0,
|
||||
bbox: Rectangle::from_loc_and_size((0, 0), (100, 200)),
|
||||
min_max_size: Default::default(),
|
||||
},
|
||||
Op::AddWindow {
|
||||
id: 1,
|
||||
bbox: Rectangle::from_loc_and_size((0, 0), (100, 200)),
|
||||
min_max_size: Default::default(),
|
||||
},
|
||||
Op::ConsumeOrExpelWindowLeft,
|
||||
Op::AddWindow {
|
||||
id: 2,
|
||||
bbox: Rectangle::from_loc_and_size((0, 0), (100, 200)),
|
||||
min_max_size: Default::default(),
|
||||
},
|
||||
Op::ConsumeOrExpelWindowLeft,
|
||||
Op::SetWindowHeight(SizeChange::SetFixed(100)),
|
||||
Op::Communicate(2),
|
||||
Op::FocusWindowUp,
|
||||
Op::SetWindowHeight(SizeChange::SetFixed(200)),
|
||||
Op::Communicate(1),
|
||||
Op::CloseWindow(0),
|
||||
Op::FullscreenWindow(1),
|
||||
];
|
||||
|
||||
check_ops(&ops);
|
||||
}
|
||||
|
||||
fn arbitrary_spacing() -> impl Strategy<Value = f64> {
|
||||
// Give equal weight to:
|
||||
// - 0: the element is disabled
|
||||
|
@ -196,9 +196,12 @@ pub enum ColumnWidth {
|
||||
/// to fit the desired content, it can never become smaller than that when moving between monitors.
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum WindowHeight {
|
||||
/// Automatically computed height, evenly distributed across the column.
|
||||
Auto,
|
||||
/// Fixed height in logical pixels.
|
||||
/// Automatically computed *tile* height, distributed across the column according to weights.
|
||||
///
|
||||
/// This controls the tile height rather than the window height because it's easier in the auto
|
||||
/// height distribution algorithm.
|
||||
Auto { weight: f64 },
|
||||
/// Fixed *window* height in logical pixels.
|
||||
Fixed(f64),
|
||||
}
|
||||
|
||||
@ -312,6 +315,12 @@ impl From<PresetWidth> for ColumnWidth {
|
||||
}
|
||||
}
|
||||
|
||||
impl WindowHeight {
|
||||
const fn auto_1() -> Self {
|
||||
Self::Auto { weight: 1. }
|
||||
}
|
||||
}
|
||||
|
||||
impl TileData {
|
||||
pub fn new<W: LayoutElement>(tile: &Tile<W>, height: WindowHeight) -> Self {
|
||||
let mut rv = Self {
|
||||
@ -1106,6 +1115,13 @@ impl<W: LayoutElement> Workspace<W> {
|
||||
let tile = column.tiles.remove(window_idx);
|
||||
column.data.remove(window_idx);
|
||||
|
||||
// If one window is left, reset its weight to 1.
|
||||
if column.data.len() == 1 {
|
||||
if let WindowHeight::Auto { weight } = &mut column.data[0].height {
|
||||
*weight = 1.;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(output) = &self.output {
|
||||
tile.window().output_leave(output);
|
||||
}
|
||||
@ -2265,6 +2281,14 @@ impl<W: LayoutElement> Workspace<W> {
|
||||
let window = col.tiles.remove(tile_idx).into_window();
|
||||
col.data.remove(tile_idx);
|
||||
col.active_tile_idx = min(col.active_tile_idx, col.tiles.len() - 1);
|
||||
|
||||
// If one window is left, reset its weight to 1.
|
||||
if col.data.len() == 1 {
|
||||
if let WindowHeight::Auto { weight } = &mut col.data[0].height {
|
||||
*weight = 1.;
|
||||
}
|
||||
}
|
||||
|
||||
col.update_tile_sizes(false);
|
||||
self.data[col_idx].update(col);
|
||||
let width = col.width;
|
||||
@ -2937,7 +2961,7 @@ impl<W: LayoutElement> Column<W> {
|
||||
|
||||
fn add_tile(&mut self, tile: Tile<W>, animate: bool) {
|
||||
self.is_fullscreen = false;
|
||||
self.data.push(TileData::new(&tile, WindowHeight::Auto));
|
||||
self.data.push(TileData::new(&tile, WindowHeight::auto_1()));
|
||||
self.tiles.push(tile);
|
||||
self.update_tile_sizes(animate);
|
||||
}
|
||||
@ -3020,13 +3044,14 @@ impl<W: LayoutElement> Column<W> {
|
||||
// Compute the tile heights. Start by converting window heights to tile heights.
|
||||
let mut heights = zip(&self.tiles, &self.data)
|
||||
.map(|(tile, data)| match data.height {
|
||||
WindowHeight::Auto => WindowHeight::Auto,
|
||||
auto @ WindowHeight::Auto { .. } => auto,
|
||||
WindowHeight::Fixed(height) => {
|
||||
WindowHeight::Fixed(tile.tile_height_for_window_height(height.round().max(1.)))
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let mut height_left = self.working_area.size.h - self.options.gaps;
|
||||
let gaps_left = self.options.gaps * (self.tiles.len() + 1) as f64;
|
||||
let mut height_left = self.working_area.size.h - gaps_left;
|
||||
let mut auto_tiles_left = self.tiles.len();
|
||||
|
||||
// Subtract all fixed-height tiles.
|
||||
@ -3044,11 +3069,22 @@ impl<W: LayoutElement> Column<W> {
|
||||
*h = f64::max(*h, min_size.h);
|
||||
}
|
||||
|
||||
height_left -= *h + self.options.gaps;
|
||||
height_left -= *h;
|
||||
auto_tiles_left -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
let mut total_weight: f64 = heights
|
||||
.iter()
|
||||
.filter_map(|h| {
|
||||
if let WindowHeight::Auto { weight } = *h {
|
||||
Some(weight)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.sum();
|
||||
|
||||
// Iteratively try to distribute the remaining height, checking against tile min heights.
|
||||
// Pick an auto height according to the current sizes, then check if it satisfies all
|
||||
// remaining min heights. If not, allocate fixed height to those tiles and repeat the
|
||||
@ -3064,15 +3100,17 @@ impl<W: LayoutElement> Column<W> {
|
||||
// Wayland requires us to round the requested size for a window to integer logical
|
||||
// pixels, therefore we compute the remaining auto height dynamically.
|
||||
let mut height_left_2 = height_left;
|
||||
let mut auto_tiles_left_2 = auto_tiles_left;
|
||||
let mut total_weight_2 = total_weight;
|
||||
let mut unsatisfied_min = false;
|
||||
for ((h, tile), min_size) in zip(zip(&mut heights, &self.tiles), &min_size) {
|
||||
if matches!(h, WindowHeight::Fixed(_)) {
|
||||
continue;
|
||||
}
|
||||
let weight = match *h {
|
||||
WindowHeight::Auto { weight } => weight,
|
||||
WindowHeight::Fixed(_) => continue,
|
||||
};
|
||||
let factor = weight / total_weight_2;
|
||||
|
||||
// Compute the current auto height.
|
||||
let auto = height_left_2 / auto_tiles_left_2 as f64 - self.options.gaps;
|
||||
let auto = height_left_2 * factor;
|
||||
let mut auto = tile.tile_height_for_window_height(
|
||||
tile.window_height_for_tile_height(auto).round().max(1.),
|
||||
);
|
||||
@ -3081,13 +3119,14 @@ impl<W: LayoutElement> Column<W> {
|
||||
if min_size.h > 0. && min_size.h > auto {
|
||||
auto = min_size.h;
|
||||
*h = WindowHeight::Fixed(auto);
|
||||
height_left -= auto + self.options.gaps;
|
||||
height_left -= auto;
|
||||
total_weight -= weight;
|
||||
auto_tiles_left -= 1;
|
||||
unsatisfied_min = true;
|
||||
}
|
||||
|
||||
height_left_2 -= auto + self.options.gaps;
|
||||
auto_tiles_left_2 -= 1;
|
||||
height_left_2 -= auto;
|
||||
total_weight_2 -= weight;
|
||||
}
|
||||
|
||||
// If some min height was unsatisfied, then we allocated the tile more than the auto
|
||||
@ -3099,18 +3138,21 @@ impl<W: LayoutElement> Column<W> {
|
||||
|
||||
// All min heights were satisfied, fill them in.
|
||||
for (h, tile) in zip(&mut heights, &self.tiles) {
|
||||
if matches!(h, WindowHeight::Fixed(_)) {
|
||||
continue;
|
||||
}
|
||||
let weight = match *h {
|
||||
WindowHeight::Auto { weight } => weight,
|
||||
WindowHeight::Fixed(_) => continue,
|
||||
};
|
||||
let factor = weight / total_weight;
|
||||
|
||||
// Compute the current auto height.
|
||||
let auto = height_left / auto_tiles_left as f64 - self.options.gaps;
|
||||
let auto = height_left * factor;
|
||||
let auto = tile.tile_height_for_window_height(
|
||||
tile.window_height_for_tile_height(auto).round().max(1.),
|
||||
);
|
||||
|
||||
*h = WindowHeight::Fixed(auto);
|
||||
height_left -= auto + self.options.gaps;
|
||||
height_left -= auto;
|
||||
total_weight -= weight;
|
||||
auto_tiles_left -= 1;
|
||||
}
|
||||
|
||||
@ -3202,6 +3244,16 @@ impl<W: LayoutElement> Column<W> {
|
||||
assert_eq!(self.tiles.len(), 1);
|
||||
}
|
||||
|
||||
if self.tiles.len() == 1 {
|
||||
if let WindowHeight::Auto { weight } = self.data[0].height {
|
||||
assert_eq!(
|
||||
weight, 1.,
|
||||
"auto height weight must reset to 1 for a single window"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let mut found_fixed = false;
|
||||
for (tile, data) in zip(&self.tiles, &self.data) {
|
||||
assert!(Rc::ptr_eq(&self.options, &tile.options));
|
||||
assert_eq!(self.scale, tile.scale());
|
||||
@ -3216,6 +3268,14 @@ impl<W: LayoutElement> Column<W> {
|
||||
let rounded = size.to_physical_precise_round(scale).to_logical(scale);
|
||||
assert_abs_diff_eq!(size.w, rounded.w, epsilon = 1e-5);
|
||||
assert_abs_diff_eq!(size.h, rounded.h, epsilon = 1e-5);
|
||||
|
||||
if matches!(data.height, WindowHeight::Fixed(_)) {
|
||||
assert!(
|
||||
!found_fixed,
|
||||
"there can only be one fixed-height window in a column"
|
||||
);
|
||||
found_fixed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3309,10 +3369,19 @@ impl<W: LayoutElement> Column<W> {
|
||||
|
||||
fn set_window_height(&mut self, change: SizeChange, tile_idx: Option<usize>, animate: bool) {
|
||||
let tile_idx = tile_idx.unwrap_or(self.active_tile_idx);
|
||||
|
||||
// Start by converting all heights to automatic, since only one window in the column can be
|
||||
// fixed-height. If the current tile is already fixed, however, we can skip that step.
|
||||
// Which is not only for optimization, but also preserves automatic weights in case one
|
||||
// window is resized in such a way that other windows hit their min size, and then back.
|
||||
if !matches!(self.data[tile_idx].height, WindowHeight::Fixed(_)) {
|
||||
self.convert_heights_to_auto();
|
||||
}
|
||||
|
||||
let current = self.data[tile_idx].height;
|
||||
let tile = &self.tiles[tile_idx];
|
||||
let current_window_px = match current {
|
||||
WindowHeight::Auto => tile.window_size().h,
|
||||
WindowHeight::Auto { .. } => tile.window_size().h,
|
||||
WindowHeight::Fixed(height) => height,
|
||||
};
|
||||
let current_tile_px = tile.tile_height_for_window_height(current_window_px);
|
||||
@ -3361,10 +3430,32 @@ impl<W: LayoutElement> Column<W> {
|
||||
|
||||
fn reset_window_height(&mut self, tile_idx: Option<usize>, animate: bool) {
|
||||
let tile_idx = tile_idx.unwrap_or(self.active_tile_idx);
|
||||
self.data[tile_idx].height = WindowHeight::Auto;
|
||||
self.data[tile_idx].height = WindowHeight::auto_1();
|
||||
self.update_tile_sizes(animate);
|
||||
}
|
||||
|
||||
/// Converts all heights in the column to automatic, preserving the apparent heights.
|
||||
///
|
||||
/// All weights are recomputed to preserve the current tile heights while "centering" the
|
||||
/// weights at the median window height (it gets weight = 1).
|
||||
///
|
||||
/// One case where apparent heights will not be preserved is when the column is taller than the
|
||||
/// working area.
|
||||
fn convert_heights_to_auto(&mut self) {
|
||||
let heights: Vec<_> = self.tiles.iter().map(|tile| tile.tile_size().h).collect();
|
||||
|
||||
// Weights are invariant to multiplication: a column with weights 2, 2, 1 is equivalent to
|
||||
// a column with weights 4, 4, 2. So we find the median window height and use that as 1.
|
||||
let mut sorted = heights.clone();
|
||||
sorted.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap());
|
||||
let median = sorted[sorted.len() / 2];
|
||||
|
||||
for (data, height) in zip(&mut self.data, heights) {
|
||||
let weight = height / median;
|
||||
data.height = WindowHeight::Auto { weight };
|
||||
}
|
||||
}
|
||||
|
||||
fn set_fullscreen(&mut self, is_fullscreen: bool) {
|
||||
if self.is_fullscreen == is_fullscreen {
|
||||
return;
|
||||
@ -3398,7 +3489,7 @@ impl<W: LayoutElement> Column<W> {
|
||||
|
||||
// Chain with a dummy value to be able to get one past all tiles' Y.
|
||||
let dummy = TileData {
|
||||
height: WindowHeight::Auto,
|
||||
height: WindowHeight::auto_1(),
|
||||
size: Size::default(),
|
||||
interactively_resizing_by_left_edge: false,
|
||||
};
|
||||
|
28
src/niri.rs
28
src/niri.rs
@ -195,7 +195,9 @@ pub struct Niri {
|
||||
// When false, we're idling with monitors powered off.
|
||||
pub monitors_active: bool,
|
||||
|
||||
pub current_keyboard: niri_config::Keyboard,
|
||||
pub devices: HashSet<input::Device>,
|
||||
pub keyboard_device_cache: HashMap<input::Device, niri_config::Keyboard>,
|
||||
pub tablets: HashMap<input::Device, TabletData>,
|
||||
pub touch: HashSet<input::Device>,
|
||||
|
||||
@ -860,7 +862,7 @@ impl State {
|
||||
}
|
||||
}
|
||||
|
||||
if self.niri.config.borrow().input.keyboard.track_layout == TrackLayout::Window {
|
||||
if self.niri.current_keyboard.track_layout == TrackLayout::Window {
|
||||
let current_layout =
|
||||
keyboard.with_xkb_state(self, |context| context.active_layout());
|
||||
|
||||
@ -960,19 +962,23 @@ impl State {
|
||||
self.niri.cursor_texture_cache.clear();
|
||||
}
|
||||
|
||||
let default_keyboard = config.input.fallback_keyboard();
|
||||
|
||||
let current_keyboard = self.niri.current_keyboard.clone();
|
||||
|
||||
// We need &mut self to reload the xkb config, so just store it here.
|
||||
if config.input.keyboard.xkb != old_config.input.keyboard.xkb {
|
||||
reload_xkb = Some(config.input.keyboard.xkb.clone());
|
||||
if default_keyboard.xkb != current_keyboard.xkb {
|
||||
reload_xkb = Some(default_keyboard.xkb.clone());
|
||||
}
|
||||
|
||||
// Reload the repeat info.
|
||||
if config.input.keyboard.repeat_rate != old_config.input.keyboard.repeat_rate
|
||||
|| config.input.keyboard.repeat_delay != old_config.input.keyboard.repeat_delay
|
||||
if default_keyboard.repeat_rate != current_keyboard.repeat_rate
|
||||
|| default_keyboard.repeat_delay != current_keyboard.repeat_delay
|
||||
{
|
||||
let keyboard = self.niri.seat.get_keyboard().unwrap();
|
||||
keyboard.change_repeat_info(
|
||||
config.input.keyboard.repeat_rate.into(),
|
||||
config.input.keyboard.repeat_delay.into(),
|
||||
default_keyboard.repeat_rate.into(),
|
||||
default_keyboard.repeat_delay.into(),
|
||||
);
|
||||
}
|
||||
|
||||
@ -1609,9 +1615,9 @@ impl Niri {
|
||||
|
||||
let mut seat: Seat<State> = seat_state.new_wl_seat(&display_handle, backend.seat_name());
|
||||
seat.add_keyboard(
|
||||
config_.input.keyboard.xkb.to_xkb_config(),
|
||||
config_.input.keyboard.repeat_delay.into(),
|
||||
config_.input.keyboard.repeat_rate.into(),
|
||||
config_.input.fallback_keyboard().xkb.to_xkb_config(),
|
||||
config_.input.fallback_keyboard().repeat_delay.into(),
|
||||
config_.input.fallback_keyboard().repeat_rate.into(),
|
||||
)
|
||||
.unwrap();
|
||||
seat.add_pointer();
|
||||
@ -1727,7 +1733,9 @@ impl Niri {
|
||||
root_surface: HashMap::new(),
|
||||
monitors_active: true,
|
||||
|
||||
current_keyboard: Default::default(),
|
||||
devices: HashSet::new(),
|
||||
keyboard_device_cache: HashMap::new(),
|
||||
tablets: HashMap::new(),
|
||||
touch: HashSet::new(),
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user