Skip to content

Commit 0e3e91f

Browse files
authored
Merge pull request #309 from Gigas002/feature/freeze-option
feature: add freeze option
2 parents 48cdf07 + c37cacd commit 0e3e91f

10 files changed

Lines changed: 95 additions & 53 deletions

File tree

config.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
# output = "HDMI-A-1"
44
# should contain cursor?
55
cursor = false
6+
# freeze screen when selecting region (--geometry) or point (--color)? false = live selection (like --no-freeze)
7+
freeze = true
68
# delay in milliseconds before taking the screenshot; omit or leave unset for no delay
79
# delay = 0
810
# should copy screenshot to clipborad?

docs/wayshot.1.scd

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,17 @@ Wayshot - Screenshot tool for compositors implementing zwlr_screencopy_v1 such a
4343
List all active toplevel windows.
4444

4545
*--color*
46-
Freeze the screen, click a pixel, and print its RGBA and hex color value.
46+
Click a pixel and print its RGBA and hex color value. By default the screen is frozen first;
47+
use *--no-freeze* to pick on the live display.
4748

4849
_Requires the_ *color_picker* _feature (enabled by default)._
4950

5051
## Capture target (what to capture)
5152

5253
*-g*, *--geometry*
53-
Interactively select a screen region. The screen is frozen before the selection overlay is
54-
shown, giving a stable view for precise selection.
54+
Interactively select a screen region. By default the screen is frozen before the selection
55+
overlay is shown, giving a stable view for precise selection. Use *--no-freeze* to select on
56+
the live display and capture after selection.
5557

5658
_Requires the_ *selector* _feature (enabled by default)._
5759

@@ -73,6 +75,11 @@ Wayshot - Screenshot tool for compositors implementing zwlr_screencopy_v1 such a
7375
*-c*, *--cursor*
7476
Include the cursor in the screenshot.
7577

78+
*--no-freeze*
79+
Do not freeze the screen when selecting a region (*--geometry*) or a point (*--color*).
80+
Selection is done on the live display; the capture is taken after selection.
81+
Config: *[base] freeze* (see wayshot(5)).
82+
7683
*--delay* _MS_
7784
Wait _MS_ milliseconds before taking the screenshot. No delay when omitted.
7885
Config: *[base] delay* (see wayshot(5)).

docs/wayshot.5.scd

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,15 @@ This section documents the *[base]* table of the configuration file
3838

3939
Default: _false_
4040

41+
*freeze* = _true_ | _false_
42+
43+
When true, freeze the screen before interactive region (*--geometry*) or point (*--color*)
44+
selection, giving a stable view. When false, selection is on the live display and the
45+
capture is taken after selection (same as *--no-freeze*). CLI *--no-freeze* takes
46+
precedence.
47+
48+
Default: _true_
49+
4150
*delay* = _<integer>_ | _unset_
4251

4352
Number of milliseconds to wait before taking the screenshot. When unset or omitted, no delay is applied.

wayshot/src/cli.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,11 @@ pub struct Cli {
9696
#[arg(short, long)]
9797
pub cursor: bool,
9898

99+
/// Do not freeze the screen when selecting a region (geometry) or a point (color picker).
100+
/// Selection happens on the live display; the capture is taken after selection.
101+
#[arg(long)]
102+
pub no_freeze: bool,
103+
99104
/// Wait this many milliseconds before taking the screenshot. No delay when unset.
100105
#[arg(long, value_name = "MS")]
101106
pub delay: Option<u32>,

wayshot/src/color_picker.rs

Lines changed: 11 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,36 +2,21 @@
22
33
use eyre::Result;
44
use libwayshot::WayshotConnection;
5-
use libwaysip::WaySip;
65

7-
use crate::utils::waysip_to_region;
6+
use crate::utils::get_region_point;
87

9-
/// Freeze the screen, let the user click a pixel, and print its color.
10-
pub fn pick(conn: &WayshotConnection) -> Result<()> {
11-
let image = conn
12-
.screenshot_freeze(
13-
|w_conn| {
14-
let info = WaySip::new()
15-
.with_connection(w_conn.conn.clone())
16-
.with_selection_type(libwaysip::SelectionType::Point)
17-
.get()
18-
.map_err(|e| libwayshot::Error::FreezeCallbackError(e.to_string()))?
19-
.ok_or_else(|| {
20-
libwayshot::Error::FreezeCallbackError(
21-
"Failed to capture the point".to_string(),
22-
)
23-
})?;
24-
waysip_to_region(
25-
libwaysip::Size {
26-
width: 1,
27-
height: 1,
28-
},
29-
info.left_top_point(),
30-
)
31-
},
8+
/// Let the user click a pixel and print its color. When `freeze` is true, the screen is frozen first.
9+
pub fn pick(conn: &WayshotConnection, freeze: bool) -> Result<()> {
10+
let image = (if freeze {
11+
conn.screenshot_freeze(
12+
|w_conn| get_region_point(w_conn).map_err(libwayshot::Error::FreezeCallbackError),
3213
false,
3314
)?
34-
.to_rgba8();
15+
} else {
16+
let region = get_region_point(conn).map_err(|e| eyre::eyre!("{e}"))?;
17+
conn.screenshot(region, false)?
18+
})
19+
.to_rgba8();
3520

3621
let [r, g, b, a] = image.get_pixel(0, 0).0;
3722
println!("RGBA : R:{r}, G:{g}, B:{b}, A:{a}");

wayshot/src/config.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ impl Config {
4343
pub struct Base {
4444
pub output: Option<String>,
4545
pub cursor: Option<bool>,
46+
pub freeze: Option<bool>,
4647
pub delay: Option<u32>,
4748
pub clipboard: Option<bool>,
4849
pub file: Option<bool>,
@@ -56,6 +57,7 @@ impl Default for Base {
5657
Base {
5758
output: None,
5859
cursor: Some(false),
60+
freeze: Some(true),
5961
delay: None,
6062
clipboard: Some(false),
6163
file: Some(true),

wayshot/src/screenshot.rs

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
1+
#[cfg(feature = "selector")]
2+
use crate::utils::get_region_area;
13
use dialoguer::{FuzzySelect, theme::ColorfulTheme};
24
use eyre::{Result, bail};
35
use libwayshot::WayshotConnection;
4-
#[cfg(feature = "selector")]
5-
use libwaysip::WaySip;
6-
7-
#[cfg(feature = "selector")]
8-
use crate::utils::waysip_to_region;
96

107
/// Describes what was captured, used to build the notification body.
118
#[derive(Debug, Clone)]
@@ -39,10 +36,11 @@ pub fn capture(
3936
conn: &WayshotConnection,
4037
mode: &CaptureMode,
4138
cursor: bool,
39+
freeze: bool,
4240
) -> Result<(image::DynamicImage, ShotResult)> {
4341
match mode {
4442
#[cfg(feature = "selector")]
45-
CaptureMode::Geometry => capture_geometry(conn, cursor),
43+
CaptureMode::Geometry => capture_geometry(conn, cursor, freeze),
4644
CaptureMode::Toplevel(name) => capture_toplevel_by_name(conn, name, cursor),
4745
CaptureMode::ChooseToplevel => capture_toplevel_interactive(conn, cursor),
4846
CaptureMode::Output(name) => capture_output_by_name(conn, name, cursor),
@@ -56,21 +54,17 @@ pub fn capture(
5654
fn capture_geometry(
5755
conn: &WayshotConnection,
5856
cursor: bool,
57+
freeze: bool,
5958
) -> Result<(image::DynamicImage, ShotResult)> {
60-
let image = conn.screenshot_freeze(
61-
|w_conn| {
62-
let info = WaySip::new()
63-
.with_connection(w_conn.conn.clone())
64-
.with_selection_type(libwaysip::SelectionType::Area)
65-
.get()
66-
.map_err(|e| libwayshot::Error::FreezeCallbackError(e.to_string()))?
67-
.ok_or_else(|| {
68-
libwayshot::Error::FreezeCallbackError("No area selected".to_string())
69-
})?;
70-
waysip_to_region(info.size(), info.left_top_point())
71-
},
72-
cursor,
73-
)?;
59+
let image = if freeze {
60+
conn.screenshot_freeze(
61+
|w_conn| get_region_area(w_conn).map_err(libwayshot::Error::FreezeCallbackError),
62+
cursor,
63+
)?
64+
} else {
65+
let region = get_region_area(conn).map_err(|e| eyre::eyre!("{e}"))?;
66+
conn.screenshot(region, cursor)?
67+
};
7468
Ok((image, ShotResult::Area))
7569
}
7670

wayshot/src/settings.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ pub(crate) struct AppSettings {
3333
pub(crate) command: Command,
3434
/// Whether to render the cursor in the captured image.
3535
pub(crate) cursor: bool,
36+
/// When true, freeze the screen before region/point selection; when false, select on live display.
37+
pub(crate) freeze: bool,
3638
/// Milliseconds to wait before capturing; `None` means no delay.
3739
pub(crate) delay: Option<u32>,
3840
/// Final encoding format, after resolving extension / flag / config precedence.
@@ -61,6 +63,10 @@ impl AppSettings {
6163
// Either the --cursor flag or config `cursor = true` enables cursor capture.
6264
let cursor = cli.cursor || base.cursor.unwrap_or_default();
6365

66+
// ── Freeze ─────────────────────────────────────────────────────────────
67+
// Freeze screen before selection; false when CLI --no-freeze or config freeze = false.
68+
let freeze = !cli.no_freeze && base.freeze.unwrap_or(true);
69+
6470
// ── Delay ─────────────────────────────────────────────────────────────
6571
// Wait N ms before capture; CLI overrides config; None = no delay.
6672
let delay = cli.delay.or(base.delay);
@@ -132,6 +138,7 @@ impl AppSettings {
132138
AppSettings {
133139
command,
134140
cursor,
141+
freeze,
135142
delay,
136143
encoding,
137144
file,

wayshot/src/utils.rs

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use std::{
1717
};
1818

1919
use chrono::Local;
20-
#[cfg(feature = "selector")]
20+
#[cfg(any(feature = "selector", feature = "color_picker"))]
2121
use libwayshot::{
2222
Result as WayshotResult,
2323
region::{LogicalRegion, Position, Region, Size},
@@ -63,7 +63,7 @@ pub fn print_completions(shell: crate::cli::Shell) {
6363

6464
// ─── Region helpers ───────────────────────────────────────────────────────────
6565

66-
#[cfg(feature = "selector")]
66+
#[cfg(any(feature = "selector", feature = "color_picker"))]
6767
pub fn waysip_to_region(
6868
size: libwaysip::Size,
6969
position: libwaysip::Position,
@@ -87,6 +87,37 @@ pub fn waysip_to_region(
8787
})
8888
}
8989

90+
/// Run WaySip area selection and return the chosen region. Used for both freeze and live paths.
91+
#[cfg(feature = "selector")]
92+
pub fn get_region_area(conn: &libwayshot::WayshotConnection) -> Result<LogicalRegion, String> {
93+
let info = libwaysip::WaySip::new()
94+
.with_connection(conn.conn.clone())
95+
.with_selection_type(libwaysip::SelectionType::Area)
96+
.get()
97+
.map_err(|e| e.to_string())?
98+
.ok_or_else(|| "No area selected".to_string())?;
99+
waysip_to_region(info.size(), info.left_top_point()).map_err(|e| e.to_string())
100+
}
101+
102+
/// Run WaySip point selection and return a 1×1 region. Used for both freeze and live paths.
103+
#[cfg(feature = "color_picker")]
104+
pub fn get_region_point(conn: &libwayshot::WayshotConnection) -> Result<LogicalRegion, String> {
105+
let info = libwaysip::WaySip::new()
106+
.with_connection(conn.conn.clone())
107+
.with_selection_type(libwaysip::SelectionType::Point)
108+
.get()
109+
.map_err(|e| e.to_string())?
110+
.ok_or_else(|| "Failed to capture the point".to_string())?;
111+
waysip_to_region(
112+
libwaysip::Size {
113+
width: 1,
114+
height: 1,
115+
},
116+
info.left_top_point(),
117+
)
118+
.map_err(|e| e.to_string())
119+
}
120+
90121
// ─── Encoding format ──────────────────────────────────────────────────────────
91122

92123
/// Supported image encoding formats.

wayshot/src/wayshot.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,12 @@ fn main() -> Result<()> {
6363
Ok(())
6464
}
6565
#[cfg(feature = "color_picker")]
66-
Command::ColorPicker => color_picker::pick(&connection),
66+
Command::ColorPicker => color_picker::pick(&connection, settings.freeze),
6767
Command::Screenshot(mode) => {
6868
if let Some(ms) = settings.delay {
6969
std::thread::sleep(Duration::from_millis(ms as u64));
7070
}
71-
let result = screenshot::capture(&connection, &mode, settings.cursor);
71+
let result = screenshot::capture(&connection, &mode, settings.cursor, settings.freeze);
7272
match result {
7373
Ok((image_buffer, shot_result)) => {
7474
let encoded = utils::encode_image(

0 commit comments

Comments
 (0)