From d362ee060c601483d9ac9d9baba8e4dc324752dd Mon Sep 17 00:00:00 2001 From: elwrcl Date: Fri, 2 Oct 2026 16:19:38 +0300 Subject: [PATCH] feat: space switches region mode to window mode --- src/main.rs | 13 ++++++++++--- src/region.rs | 24 +++++++++++++++++++----- src/selection.rs | 15 +++++++++++++++ src/wayland.rs | 11 +++++++---- 4 files changed, 51 insertions(+), 12 deletions(-) diff --git a/src/main.rs b/src/main.rs index 0707a9b..0da8f80 100644 --- a/src/main.rs +++ b/src/main.rs @@ -172,9 +172,16 @@ fn capture(mode: Mode, common: Common) -> Result<()> { } Mode::Region => { let frames = wl.capture(&outputs, cursor)?; - let (i, r) = region::select(&mut wl, &outputs, &frames)?; - let source = frames[i].output.name.clone(); - (vec![(frames[i].to_rgba(r), None)], 0, source) + match region::select(&mut wl, &outputs, &frames)? { + region::Choice::Region(i, r) => { + let source = frames[i].output.name.clone(); + (vec![(frames[i].to_rgba(r), None)], 0, source) + } + region::Choice::Window => { + drop(frames); + window_shot(&outputs, cursor)? + } + } } Mode::Window => window_shot(&outputs, cursor)?, }; diff --git a/src/region.rs b/src/region.rs index 97f950f..ec5b769 100644 --- a/src/region.rs +++ b/src/region.rs @@ -30,8 +30,14 @@ use crate::wayland::{Output, State, Wayland}; pub const NAMESPACE: &str = "valw-overlay"; -/// What the user picked: an output index and a rectangle in its frame. -pub type Picked = (usize, PixelRect); +/// What the user chose in the overlay. +#[derive(Debug, PartialEq)] +pub enum Choice { + /// A rectangle in the frame of output `.0`. + Region(usize, PixelRect), + /// Space before dragging: capture a window instead. + Window, +} /// One full-screen layer surface per output, showing the frozen frame. pub struct Overlay { @@ -39,7 +45,7 @@ pub struct Overlay { selection: Selection, /// Output where the current drag started; the capture is clipped to it. anchor: Option, - outcome: Option>, + outcome: Option>, } struct Surface { @@ -59,7 +65,8 @@ struct Surface { /// Shows the overlay on every output and waits for a selection. /// Returns `Cancelled` if the user presses Esc or right-clicks. -pub fn select(wl: &mut Wayland, outputs: &[Output], frames: &[Frame]) -> Result { +/// Space before dragging returns `Choice::Window`. +pub fn select(wl: &mut Wayland, outputs: &[Output], frames: &[Frame]) -> Result { let qh = wl.queue.handle(); let s = &wl.state; let layer_shell = s @@ -162,6 +169,13 @@ impl Overlay { self.outcome.get_or_insert(None); } + pub fn switch_to_window(&mut self) { + if self.selection.allows_window_switch() { + tracing::info!("switching to window mode"); + self.outcome.get_or_insert(Some(Choice::Window)); + } + } + fn index_of(&self, surface: &WlSurface) -> Option { self.surfaces .iter() @@ -266,7 +280,7 @@ impl Overlay { "selected {rect:?} on {} (logical {start:?} to {end:?})", s.geom.name ); - self.outcome = Some(Some((a, rect))); + self.outcome = Some(Some(Choice::Region(a, rect))); } // A click, not a drag: clear it and keep waiting. None => self.redraw(a, qh), diff --git a/src/selection.rs b/src/selection.rs index ca329f9..19ca896 100644 --- a/src/selection.rs +++ b/src/selection.rs @@ -38,6 +38,11 @@ impl Selection { Selection::Idle => None, } } + + /// Space switches to window mode only before a drag starts. + pub fn allows_window_switch(&self) -> bool { + *self == Selection::Idle + } } /// The part of the drag from `a` to `b` that falls on `out`, in the physical @@ -77,6 +82,16 @@ pub fn resolve( mod tests { use super::*; + #[test] + fn window_switch_only_before_a_drag() { + let mut s = Selection::default(); + assert!(s.allows_window_switch()); + s.press(Point { x: 1.0, y: 1.0 }); + assert!(!s.allows_window_switch(), "Space during a drag is reserved"); + s.release(Point { x: 5.0, y: 5.0 }); + assert!(s.allows_window_switch()); + } + fn out(name: &str, x: i32, y: i32, width: i32, height: i32) -> OutputGeom { OutputGeom { name: name.into(), diff --git a/src/wayland.rs b/src/wayland.rs index 934d022..a5b986b 100644 --- a/src/wayland.rs +++ b/src/wayland.rs @@ -449,10 +449,13 @@ impl KeyboardHandler for State { _: u32, event: KeyEvent, ) { - if event.keysym == Keysym::Escape - && let Some(overlay) = &mut self.overlay - { - overlay.cancel(); + let Some(overlay) = &mut self.overlay else { + return; + }; + match event.keysym { + Keysym::Escape => overlay.cancel(), + Keysym::space => overlay.switch_to_window(), + _ => {} } }