Skip to content

Commit 2234551

Browse files
committed
feat: zoom view lock
1 parent 18e696d commit 2234551

File tree

10 files changed

+171
-59
lines changed

10 files changed

+171
-59
lines changed

niri-config/src/binds.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -368,10 +368,6 @@ pub enum Action {
368368
#[knuffel(skip)]
369369
UnsetWindowUrgent(u64),
370370
#[knuffel(skip)]
371-
ToggleZoom {
372-
output: Option<String>,
373-
},
374-
#[knuffel(skip)]
375371
LoadConfigFile,
376372
#[knuffel(skip)]
377373
MruAdvance {
@@ -393,6 +389,14 @@ pub enum Action {
393389
MruSetScope(MruScope),
394390
#[knuffel(skip)]
395391
MruCycleScope,
392+
#[knuffel(skip)]
393+
ToggleZoom {
394+
output: Option<String>,
395+
},
396+
#[knuffel(skip)]
397+
ToggleZoomFreeze {
398+
output: Option<String>,
399+
},
396400
}
397401

398402
impl From<niri_ipc::Action> for Action {
@@ -704,6 +708,7 @@ impl From<niri_ipc::Action> for Action {
704708
niri_ipc::Action::SetWindowUrgent { id } => Self::SetWindowUrgent(id),
705709
niri_ipc::Action::UnsetWindowUrgent { id } => Self::UnsetWindowUrgent(id),
706710
niri_ipc::Action::ToggleZoom { output } => Self::ToggleZoom { output },
711+
niri_ipc::Action::ToggleZoomFreeze { output } => Self::ToggleZoomFreeze { output },
707712
niri_ipc::Action::LoadConfigFile {} => Self::LoadConfigFile,
708713
}
709714
}

niri-config/src/output.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use knuffel::Decode;
88
use niri_ipc::{ConfiguredMode, HSyncPolarity, Transform, VSyncPolarity};
99

1010
use crate::gestures::HotCorners;
11-
use crate::utils::MergeWith;
11+
use crate::utils::{Flag, MergeWith};
1212
use crate::{Color, FloatOrInt, LayoutPart};
1313

1414
#[derive(Debug, Default, Clone, PartialEq)]
@@ -56,6 +56,8 @@ pub struct Zoom {
5656
pub movement: Option<niri_ipc::ZoomMovement>,
5757
#[knuffel(child, unwrap(argument))]
5858
pub threshold: Option<f64>,
59+
#[knuffel(child)]
60+
pub frozen: Option<Flag>,
5961
}
6062

6163
impl MergeWith<Zoom> for Zoom {
@@ -69,6 +71,9 @@ impl MergeWith<Zoom> for Zoom {
6971
if part.threshold.is_some() {
7072
self.threshold = part.threshold;
7173
}
74+
if part.frozen.is_some() {
75+
self.frozen = part.frozen;
76+
}
7277
}
7378
}
7479

niri-ipc/src/lib.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -941,6 +941,14 @@ pub enum Action {
941941
#[cfg_attr(feature = "clap", arg())]
942942
output: Option<String>,
943943
},
944+
/// Toggle zoom freeze for a monitor.
945+
///
946+
/// If no output is specified, uses the focused output.
947+
ToggleZoomFreeze {
948+
/// Output to toggle zoom freeze for. If not specified, uses the focused output.
949+
#[cfg_attr(feature = "clap", arg())]
950+
output: Option<String>,
951+
},
944952
/// Reload the config file.
945953
///
946954
/// Can be useful for scripts changing the config file, to avoid waiting the small duration for
@@ -1073,6 +1081,12 @@ pub enum ZoomAction {
10731081
#[cfg_attr(feature = "clap", arg())]
10741082
threshold: f64,
10751083
},
1084+
/// Set zoom frozen state.
1085+
Frozen {
1086+
/// Whether to freeze the zoom center.
1087+
#[cfg_attr(feature = "clap", arg(action = clap::ArgAction::Set, value_parser = clap::builder::BoolishValueParser::new(), value_name = "true|false"))]
1088+
frozen: bool,
1089+
},
10761090
}
10771091

10781092
/// Output actions that niri can perform.
@@ -1310,6 +1324,8 @@ pub struct Output {
13101324
///
13111325
/// Default is 0.15.
13121326
pub zoom_threshold: f64,
1327+
/// Whether the zoom center is frozen.
1328+
pub zoom_frozen: bool,
13131329
}
13141330

13151331
/// Output mode.

src/backend/headless.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ impl Headless {
112112
zoom_factor: 1.0,
113113
zoom_movement: niri_ipc::ZoomMovement::default(),
114114
zoom_threshold: 0.15,
115+
zoom_frozen: false,
115116
},
116117
);
117118

@@ -170,6 +171,7 @@ impl Headless {
170171
ipc_output.zoom_factor = mon.zoom_factor;
171172
ipc_output.zoom_movement = mon.zoom_movement;
172173
ipc_output.zoom_threshold = mon.zoom_threshold;
174+
ipc_output.zoom_frozen = mon.zoom_frozen;
173175
}
174176
}
175177
}

src/backend/tty.rs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2153,15 +2153,19 @@ impl Tty {
21532153
let logical = output.map(logical_output);
21542154

21552155
// Get zoom state from the monitor if available.
2156-
let (zoom_factor, zoom_movement, zoom_threshold) = output
2156+
let (zoom_factor, zoom_movement, zoom_threshold, zoom_frozen) = output
21572157
.and_then(|o| niri.layout.monitor_for_output(o))
2158-
.map_or((1.0, niri_ipc::ZoomMovement::default(), 0.15), |mon| {
2159-
(
2160-
mon.zoom_factor,
2161-
mon.zoom_movement,
2162-
mon.zoom_threshold,
2163-
)
2164-
});
2158+
.map_or(
2159+
(1.0, niri_ipc::ZoomMovement::default(), 0.15, false),
2160+
|mon| {
2161+
(
2162+
mon.zoom_factor,
2163+
mon.zoom_movement,
2164+
mon.zoom_threshold,
2165+
mon.zoom_frozen,
2166+
)
2167+
},
2168+
);
21652169

21662170
let id = device.known_crtcs.get(&crtc).map(|info| info.id);
21672171
let id = id.unwrap_or_else(|| {
@@ -2184,6 +2188,7 @@ impl Tty {
21842188
zoom_factor,
21852189
zoom_movement,
21862190
zoom_threshold,
2191+
zoom_frozen,
21872192
};
21882193

21892194
ipc_outputs.insert(id, ipc_output);

src/backend/winit.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ impl Winit {
9292
zoom_factor: 1.0,
9393
zoom_movement: niri_ipc::ZoomMovement::default(),
9494
zoom_threshold: 0.15,
95+
zoom_frozen: false,
9596
},
9697
)])));
9798

@@ -281,6 +282,7 @@ impl Winit {
281282
output.zoom_factor = mon.zoom_factor;
282283
output.zoom_movement = mon.zoom_movement;
283284
output.zoom_threshold = mon.zoom_threshold;
285+
output.zoom_frozen = mon.zoom_frozen;
284286
}
285287
}
286288
}

src/input/mod.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2393,6 +2393,18 @@ impl State {
23932393
self.niri.queue_redraw_all();
23942394
}
23952395
}
2396+
Action::ToggleZoomFreeze { output } => {
2397+
let target_output = match output {
2398+
Some(name) => self.niri.output_by_name_match(&name).cloned(),
2399+
None => self.niri.layout.active_output().cloned(),
2400+
};
2401+
if let Some(output) = target_output {
2402+
if let Some(monitor) = self.niri.layout.monitor_for_output_mut(&output) {
2403+
monitor.zoom_frozen = !monitor.zoom_frozen;
2404+
}
2405+
self.niri.queue_redraw_all();
2406+
}
2407+
}
23962408
}
23972409
}
23982410

@@ -2645,7 +2657,23 @@ impl State {
26452657
// When cursor exits visible zoomed area, pan by cursor delta scaled by zoom
26462658
for monitor in self.niri.layout.monitors_mut() {
26472659
let zoom = monitor.zoom_factor;
2660+
2661+
// Skip if zoom center is frozen
2662+
if monitor.zoom_frozen {
2663+
continue;
2664+
}
2665+
26482666
if zoom <= 1.0 || monitor.zoom_movement != ZoomMovement::EdgePushed {
2667+
// No zoom or not in EdgePushed mode
2668+
// Just update zoom center to current cursor position
2669+
monitor.zoom_center = new_pos
2670+
- self
2671+
.niri
2672+
.global_space
2673+
.output_geometry(monitor.output())
2674+
.unwrap()
2675+
.loc
2676+
.to_f64();
26492677
continue;
26502678
}
26512679

src/ipc/client.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,7 @@ fn print_output(output: Output) -> anyhow::Result<()> {
571571
zoom_factor,
572572
zoom_movement,
573573
zoom_threshold,
574+
zoom_frozen,
574575
..
575576
} = output;
576577

@@ -655,14 +656,15 @@ fn print_output(output: Output) -> anyhow::Result<()> {
655656
println!(" Transform: {transform}");
656657
}
657658

658-
print!(" Zoom: {zoom_factor}x");
659+
println!(" Zoom:");
660+
println!(" Factor: {:.2}", zoom_factor);
659661
let m = match zoom_movement {
660662
niri_ipc::ZoomMovement::Cursor => "cursor-centered",
661663
niri_ipc::ZoomMovement::EdgePushed => "edge-pushed",
662664
};
663-
print!(", {m}");
664-
print!(", threshold: {:.2}", zoom_threshold);
665-
println!();
665+
println!(" Movement: {m}");
666+
println!(" Threshold: {:.2} px", zoom_threshold);
667+
println!(" Frozen: {}", if zoom_frozen { "true" } else { "false" });
666668

667669
println!(" Available modes:");
668670
for (idx, mode) in modes.into_iter().enumerate() {

src/layout/monitor.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ pub struct Monitor<W: LayoutElement> {
9898
pub zoom_movement: ZoomMovement,
9999
/// Cursor zoom threshold for edge-pushed behavior (fraction of output size, default 0.15).
100100
pub zoom_threshold: f64,
101+
/// Whether the cursor_zoom center is currently frozen (not updated with cursor movements).
102+
pub zoom_frozen: bool,
101103
}
102104

103105
#[derive(Debug)]
@@ -361,6 +363,7 @@ impl<W: LayoutElement> Monitor<W> {
361363
zoom_center: Point::from((0., 0.)),
362364
zoom_movement: ZoomMovement::default(),
363365
zoom_threshold: 0.15,
366+
zoom_frozen: false,
364367
}
365368
}
366369

@@ -1401,6 +1404,9 @@ impl<W: LayoutElement> Monitor<W> {
14011404
if let Some(threshold) = zoom.threshold {
14021405
self.zoom_threshold = threshold.clamp(0.0, 1.0);
14031406
}
1407+
if let Some(frozen) = zoom.frozen {
1408+
self.zoom_frozen = frozen.0;
1409+
}
14041410
}
14051411
}
14061412

0 commit comments

Comments
 (0)