Skip to content
2 changes: 1 addition & 1 deletion src/backend/wayland/backend/event_loop/capture.rs
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,7 @@ fn resolve_board_capture_outcome(
mime_type: image.format.mime_type,
width: image.width,
height: image.height,
bytes: image.bytes,
bytes: image.bytes.into(),
};
state
.input_state
Expand Down
2 changes: 1 addition & 1 deletion src/backend/wayland/backend/state_init/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ pub(super) fn build_session_options(
let default_base = paths::data_dir()
.unwrap_or_else(|| config_dir.to_path_buf())
.join("wayscriber");
let display = display_env.clone().unwrap_or_else(|| "default".to_string());
let display = display_env.unwrap_or_else(|| "default".to_string());
session_options = Some(session::SessionOptions::new(default_base, display));
}
if let Some(options) = session_options.as_mut() {
Expand Down
2 changes: 1 addition & 1 deletion src/backend/wayland/clipboard/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ pub(super) fn decode_clipboard_image(mime_type: &str, bytes: Vec<u8>) -> Clipboa
mime_type: canonical_image_mime_type(format).to_string(),
width: dimensions.0,
height: dimensions.1,
bytes,
bytes: bytes.into(),
})
}

Expand Down
2 changes: 1 addition & 1 deletion src/backend/wayland/clipboard/transfer/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ fn clipboard_terminal_outcomes_keep_their_existing_event_loop_policy() {
mime_type: "image/png".to_string(),
width: 1,
height: 1,
bytes: vec![0, 0, 0, 255],
bytes: vec![0, 0, 0, 255].into(),
}),
},
Some(42),
Expand Down
7 changes: 4 additions & 3 deletions src/backend/wayland/handlers/pointer/cursor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -373,9 +373,10 @@ impl WaylandState {
let Some(pointer) = self.current_pointer() else {
return;
};
let serial = pointer
.data::<PointerData>()
.and_then(|data| data.latest_button_serial().or(data.latest_enter_serial()));
let serial = pointer.data::<PointerData>().and_then(|data| {
data.latest_button_serial()
.or_else(|| data.latest_enter_serial())
});
let Some(serial) = serial else {
return;
};
Expand Down
2 changes: 1 addition & 1 deletion src/backend/wayland/state/clipboard/session_paste/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ fn test_image(bytes: usize) -> EmbeddedImage {
mime_type: "image/png".to_string(),
width: 16,
height: 16,
bytes: vec![7; bytes],
bytes: vec![7; bytes].into(),
}
}

Expand Down
4 changes: 1 addition & 3 deletions src/backend/wayland/state/core/output/focus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,7 @@ impl WaylandState {
}

let surface_current_output = self.surface.current_output();
let current_output = surface_current_output
.clone()
.or_else(|| self.preferred_fullscreen_output());
let current_output = surface_current_output.or_else(|| self.preferred_fullscreen_output());
let current_index = current_output
.as_ref()
.and_then(|current| outputs.iter().position(|output| output == current))
Expand Down
3 changes: 1 addition & 2 deletions src/backend/wayland/state/core/output/transition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@ impl WaylandState {
physical_output_identity: Option<String>,
reason: &str,
) {
let Some(current_options) = self.session_options().cloned() else {
let Some(mut staged_options) = self.session_options().cloned() else {
return;
};
let mut staged_options = current_options.clone();
let changed = staged_options.set_output_identity(physical_output_identity.as_deref());
let same_epoch_pending = self
.session
Expand Down
48 changes: 40 additions & 8 deletions src/backend/wayland/state/region_capture/window_snap.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::backend::wayland::state::WaylandState;
use crate::backend::wayland::state::screen_image::{ScreenSourceToken, screen_rect_for_image_rect};
use crate::capture::window_geometry::{WindowQueryContext, WindowTarget};
use crate::input::state::{RegionInputSource, RegionPurposeTag};
use crate::input::state::{RegionInputSource, RegionPurposeTag, RegionSelection};
use crate::screen_pixels::{ImagePixelRect, ImagePoint};
use crate::util::Rect;

Expand Down Expand Up @@ -38,15 +38,19 @@ impl WindowSnapTarget {
self.image_rect
}

#[cfg(test)]
pub(in crate::backend::wayland) const fn screen_rect(&self) -> Rect {
self.screen_rect
}
}

#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq)]
enum WindowSnapAvailability {
Pending(WindowSnapQueryStage),
Ready(Vec<WindowSnapTarget>),
Ready {
targets: Vec<WindowSnapTarget>,
display_selections: Vec<RegionSelection>,
},
}

#[derive(Debug, Clone, PartialEq, Eq)]
Expand Down Expand Up @@ -121,7 +125,7 @@ impl WindowSnapSession {
}

pub(super) fn is_ready(&self) -> bool {
matches!(self.availability, WindowSnapAvailability::Ready(_))
matches!(self.availability, WindowSnapAvailability::Ready { .. })
}

pub(super) const fn mode_active(&self) -> bool {
Expand All @@ -131,7 +135,16 @@ impl WindowSnapSession {
pub(super) fn targets(&self) -> &[WindowSnapTarget] {
match &self.availability {
WindowSnapAvailability::Pending(_) => &[],
WindowSnapAvailability::Ready(targets) => targets,
WindowSnapAvailability::Ready { targets, .. } => targets,
}
}

pub(super) fn display_selections(&self) -> &[RegionSelection] {
match &self.availability {
WindowSnapAvailability::Pending(_) => &[],
WindowSnapAvailability::Ready {
display_selections, ..
} => display_selections,
}
}

Expand Down Expand Up @@ -251,10 +264,27 @@ pub(super) fn apply_window_query_completion(
let current = session
.as_mut()
.expect("a correlated pending window-snap session still exists");
current.availability = WindowSnapAvailability::Ready(mapped);
let display_selections = mapped
.iter()
.map(|target| region_selection_for_rect(target.screen_rect))
.collect();
current.availability = WindowSnapAvailability::Ready {
targets: mapped,
display_selections,
};
WindowQueryApply::Ready
}

fn region_selection_for_rect(rect: Rect) -> RegionSelection {
RegionSelection {
start: (f64::from(rect.x), f64::from(rect.y)),
end: (
f64::from(rect.x) + f64::from(rect.width),
f64::from(rect.y) + f64::from(rect.height),
),
}
}

fn map_window_target(source: ScreenSourceToken, target: WindowTarget) -> Option<WindowSnapTarget> {
let logical_rect = target.logical_rect;
let first = output_logical_to_image_point(source, (logical_rect.x, logical_rect.y))?;
Expand Down Expand Up @@ -300,11 +330,13 @@ impl WaylandState {
.is_some_and(WindowSnapSession::mode_active)
}

pub(in crate::backend::wayland) fn region_window_snap_targets(&self) -> &[WindowSnapTarget] {
pub(in crate::backend::wayland) fn region_window_snap_display_selections(
&self,
) -> &[RegionSelection] {
self.data
.window_snap
.as_ref()
.map(WindowSnapSession::targets)
.map(WindowSnapSession::display_selections)
.unwrap_or_default()
}

Expand Down
7 changes: 7 additions & 0 deletions src/backend/wayland/state/region_capture/window_snap/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,13 @@ fn output_logical_window_maps_to_authoritative_pixels_then_zoomed_screen() {
let mapped = &session.targets()[0];
assert_eq!(mapped.image_rect().size(), (450, 300));
assert_eq!(mapped.screen_rect(), Rect::new(150, 75, 450, 300).unwrap());
assert_eq!(
session.display_selections(),
&[RegionSelection {
start: (150.0, 75.0),
end: (600.0, 375.0),
}]
);
}

#[test]
Expand Down
18 changes: 2 additions & 16 deletions src/backend/wayland/state/render/ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -447,32 +447,18 @@ impl WaylandState {
(image_point.x, image_point.y),
)
});
let window_targets: Vec<_> = self
.region_window_snap_targets()
.iter()
.map(|target| {
let rect = target.screen_rect();
crate::input::state::RegionSelection {
start: (f64::from(rect.x), f64::from(rect.y)),
end: (
f64::from(rect.x + rect.width),
f64::from(rect.y + rect.height),
),
}
})
.collect();
let window = crate::ui::RegionCaptureWindowVisual {
available: self.region_window_snap_available(),
active: self.region_window_snap_active(),
targets: &window_targets,
targets: self.region_window_snap_display_selections(),
highlighted_target: self.region_window_snap_highlighted_index(),
};

crate::ui::render_region_capture_picker(
ctx,
width,
height,
crate::ui::RegionCapturePickerVisual {
&crate::ui::RegionCapturePickerVisual {
selection,
pointer,
measurement: measurement.as_deref(),
Expand Down
2 changes: 1 addition & 1 deletion src/backend/wayland/state/toolbar/events/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ pub(super) fn populate_session_snapshot(
) {
let active_path = options.map(|options| options.session_file_path());
snapshot.active_session_name = active_path.as_deref().map(session_display_name);
snapshot.active_session_path = active_path.clone();
// Recents are only read (from the catalog on disk) while the top strip's
// Session popover is up.
snapshot.recent_sessions = if snapshot.session_popover_open {
recent_session_snapshots(active_path.as_deref())
} else {
Vec::new()
};
snapshot.active_session_path = active_path;
}

fn session_display_name(path: &Path) -> String {
Expand Down
5 changes: 4 additions & 1 deletion src/backend/wayland/state/toolbar/visibility/pointer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,10 @@ impl WaylandState {
// Hide the cursor while dragging with pointer lock to avoid visual jitter.
if pointer
.data::<PointerData>()
.and_then(|data| data.latest_button_serial().or(data.latest_enter_serial()))
.and_then(|data| {
data.latest_button_serial()
.or_else(|| data.latest_enter_serial())
})
.is_some()
{
self.hide_pointer_cursor();
Expand Down
4 changes: 3 additions & 1 deletion src/backend/wayland/toolbar/main/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ impl ToolbarSurfaceManager {
return;
}
self.top.set_ui_scale(snapshot.toolbar_scale);
let top_hover = hover.or(self.top_hover).or(self.top.focused_hover());
let top_hover = hover
.or(self.top_hover)
.or_else(|| self.top.focused_hover());
let top_hover_start = self.top_hover_start;
if let Err(err) = self.top.render(
shm,
Expand Down
47 changes: 36 additions & 11 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,12 +257,23 @@ impl Cli {
}

fn validate(&self) -> Result<(), String> {
self.validate_runtime_capabilities_command()?;
self.validate_session_flag_pairs()?;
self.validate_overlay_commands()?;
self.validate_session_commands()?;
self.validate_catalog_commands()
}

fn validate_runtime_capabilities_command(&self) -> Result<(), String> {
if self.runtime_capabilities
&& (self.selects_a_launch_command() || self.about || self.check_update)
{
return Err("--runtime-capabilities conflicts with launch flags".to_string());
}
Ok(())
}

fn validate_session_flag_pairs(&self) -> Result<(), String> {
if self.exit_after_capture && self.no_exit_after_capture {
return Err(conflict("--exit-after-capture", "--no-exit-after-capture"));
}
Expand Down Expand Up @@ -294,7 +305,10 @@ impl Cli {
if self.rename_session.is_some() && self.selects_overlay_option() {
return Err("--rename-session conflicts with overlay/daemon options".to_string());
}
Ok(())
}

fn validate_overlay_commands(&self) -> Result<(), String> {
if self.freeze_on_show && !self.daemon {
return Err("--freeze-on-show requires --daemon".to_string());
}
Expand All @@ -308,16 +322,7 @@ impl Cli {
return Err("--freeze-on-show conflicts with overlay/session commands".to_string());
}

let overlay_action_count = [
self.daemon_action.is_some(),
self.light_toggle,
self.light_draw_toggle,
self.light_draw_on,
self.light_draw_off,
]
.into_iter()
.filter(|selected| *selected)
.count();
let overlay_action_count = self.overlay_action_count();

if self.session_file.is_some() {
if overlay_action_count > 0 {
Expand Down Expand Up @@ -388,7 +393,23 @@ impl Cli {
{
return Err("daemon overlay actions cannot be combined with launch flags".to_string());
}
Ok(())
}

fn overlay_action_count(&self) -> usize {
[
self.daemon_action.is_some(),
self.light_toggle,
self.light_draw_toggle,
self.light_draw_on,
self.light_draw_off,
]
.into_iter()
.filter(|selected| *selected)
.count()
}

fn validate_session_commands(&self) -> Result<(), String> {
if self.clear_session && (self.daemon || self.active) {
return Err("--clear-session conflicts with --daemon/--active".to_string());
}
Expand Down Expand Up @@ -429,6 +450,10 @@ impl Cli {
.to_string(),
);
}
Ok(())
}

fn validate_catalog_commands(&self) -> Result<(), String> {
if self.about && (self.selects_a_launch_command() || self.check_update) {
return Err("--about conflicts with the selected command".to_string());
}
Expand Down Expand Up @@ -496,7 +521,7 @@ fn value_after(args: &[String], index: usize, name: &str) -> Result<String, Stri
fn attached_short_value(arg: &str, value_start: usize, name: &str) -> Result<String, String> {
let value = arg[value_start..]
.strip_prefix('=')
.unwrap_or(&arg[value_start..]);
.unwrap_or_else(|| &arg[value_start..]);
if value.is_empty() {
return Err(format!("{name} requires a value"));
}
Expand Down
5 changes: 5 additions & 0 deletions src/config/tests/validate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,7 @@ fn validate_clamps_preset_fields() {
show_status_bar: None,
drag_tools: None,
});
config.presets.slot_5 = config.presets.slot_1.clone();

config.validate_and_clamp();

Expand All @@ -536,6 +537,10 @@ fn validate_clamps_preset_fields() {
assert_eq!(preset.arrow_length, Some(50.0));
assert_eq!(preset.arrow_angle, Some(15.0));
assert_eq!(preset.polygon_sides, Some(3));
assert_eq!(
config.presets.slot_5, config.presets.slot_1,
"the same validation must reach the last supported slot"
);
}

#[test]
Expand Down
Loading
Loading