diff --git a/src/access.rs b/src/access.rs index beae8c8..e4989c0 100644 --- a/src/access.rs +++ b/src/access.rs @@ -28,10 +28,11 @@ pub(crate) struct AccessDialogOptions { grant_label: Option, icon: Option, //(ID returned with the response, choices (ID, label), label, initial selection or "" meaning the portal should choose) + #[allow(clippy::type_complexity)] choices: Option, String)>>, } -pub static ACCESS_ID: Lazy = Lazy::new(|| window::Id::unique()); +pub static ACCESS_ID: Lazy = Lazy::new(window::Id::unique); #[derive(zvariant::SerializeDict, zvariant::Type, Debug, Clone)] #[zvariant(signature = "a{sv}")] @@ -52,6 +53,7 @@ impl Access { #[zbus::dbus_interface(name = "")] impl Access { + #[allow(clippy::too_many_arguments)] async fn access_dialog( &self, handle: zvariant::ObjectPath<'_>, @@ -252,7 +254,8 @@ pub fn update_args( tokio::spawn(async move { let _ = args .tx - .send(PortalResponse::Cancelled::); + .send(PortalResponse::Cancelled::) + .await; }); } diff --git a/src/app.rs b/src/app.rs index 9e7b91e..1ca7f1d 100644 --- a/src/app.rs +++ b/src/app.rs @@ -220,9 +220,10 @@ impl cosmic::Application for CosmicPortal { } } + #[allow(clippy::collapsible_match)] fn subscription(&self) -> cosmic::iced_futures::Subscription { Subscription::batch(vec![ - subscription::portal_subscription(self.wayland_helper.clone()).map(|e| Msg::Portal(e)), + subscription::portal_subscription(self.wayland_helper.clone()).map(Msg::Portal), listen_with(|e, _| match e { cosmic::iced_core::Event::PlatformSpecific( cosmic::iced_core::event::PlatformSpecific::Wayland(w_e), diff --git a/src/buffer.rs b/src/buffer.rs index abf5fda..045beae 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -21,7 +21,7 @@ pub fn create_memfd(width: u32, height: u32) -> OwnedFd { // TODO: BSD support using shm_open let name = unsafe { CStr::from_bytes_with_nul_unchecked(b"pipewire-screencopy\0") }; let fd = rustix::fs::memfd_create(name, rustix::fs::MemfdFlags::CLOEXEC).unwrap(); // XXX - rustix::fs::ftruncate(&fd, (width * height * 4) as _); + rustix::fs::ftruncate(&fd, (width * height * 4) as _).unwrap(); fd } diff --git a/src/main.rs b/src/main.rs index 3f026b2..ed16f6e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ use cosmic::cosmic_theme::palette::Srgba; use std::collections::HashMap; -use zbus::zvariant::{self, Array, Dict, OwnedValue, Value}; +use zbus::zvariant::{self, OwnedValue}; mod access; mod app; diff --git a/src/screencast_thread.rs b/src/screencast_thread.rs index 30ccf14..99d1ef5 100644 --- a/src/screencast_thread.rs +++ b/src/screencast_thread.rs @@ -14,17 +14,12 @@ use pipewire::{ stream::{StreamRef, StreamState}, sys::pw_buffer, }; -use std::{ - ffi::c_void, - io, iter, - os::fd::{BorrowedFd, IntoRawFd}, - slice, -}; +use std::{ffi::c_void, io, iter, os::fd::IntoRawFd, slice}; use tokio::sync::oneshot; use wayland_client::protocol::{wl_buffer, wl_output, wl_shm}; use crate::{ - buffer::{self, Dmabuf, Plane}, + buffer, wayland::{CaptureSource, DmabufHelper, Session, WaylandHelper}, }; @@ -169,7 +164,7 @@ impl StreamData { // Similar to xdg-desktop-portal-wlr let modifiers = iter::once(default) .chain(alternatives) - .filter_map(|x| gbm::Modifier::try_from(*x as u64).ok()) + .map(|x| gbm::Modifier::from(*x as u64)) .collect::>(); if let Some((modifier, plane_count)) = self.choose_modifier(&modifiers) { self.modifier = modifier; @@ -185,7 +180,9 @@ impl StreamData { .iter() .map(|x| Pod::from_bytes(x.as_slice()).unwrap()) .collect(); - stream.update_params(&mut params); + if let Err(err) = stream.update_params(&mut params) { + log::error!("failed to update pipewire params: {}", err); + } } } } @@ -271,7 +268,10 @@ impl StreamData { let buffer = unsafe { stream.dequeue_raw_buffer() }; if !buffer.is_null() { let wl_buffer = unsafe { &*((*buffer).user_data as *const wl_buffer::WlBuffer) }; - block_on(self.session.capture_wl_buffer(&wl_buffer)); + if let Err(err) = block_on(self.session.capture_wl_buffer(wl_buffer)) { + log::error!("screencopy failed: {:?}", err); + // TODO terminate screencasting? + } unsafe { stream.queue_raw_buffer(buffer) }; } } @@ -293,7 +293,7 @@ fn start_stream( let context = pipewire::context::Context::new(&loop_)?; let core = context.connect(None)?; - let name = format!("cosmic-screenshot"); // XXX randomize? + let name = "cosmic-screenshot".to_string(); // XXX randomize? let (node_id_tx, node_id_rx) = oneshot::channel(); diff --git a/src/screenshot.rs b/src/screenshot.rs index 163c020..ba32386 100644 --- a/src/screenshot.rs +++ b/src/screenshot.rs @@ -122,7 +122,8 @@ impl Screenshot { .capture_output_toplevels_shm(&output, false) .await .into_iter() - .filter_map(|img| img.image().ok().map(|img| Arc::new(img))) + .filter_map(|img| img.image().ok()) + .map(Arc::new) .collect(); map.insert(name.clone(), frame); } @@ -153,7 +154,7 @@ impl Screenshot { pub fn save_rgba(img: &RgbaImage, path: &PathBuf) -> anyhow::Result<()> { let mut encoder = - png::Encoder::new(std::fs::File::create(&path)?, img.width(), img.height()); + png::Encoder::new(std::fs::File::create(path)?, img.width(), img.height()); encoder.set_color(png::ColorType::Rgba); encoder.set_depth(png::BitDepth::Eight); let mut writer = encoder.write_header()?; @@ -277,7 +278,7 @@ impl Screenshot { ], ) .await?; - let doc_id = doc_ids.get(0).unwrap(); + let doc_id = doc_ids.first().unwrap(); let mut doc_path = mount_point.as_ref().to_path_buf(); doc_path.push(&**doc_id); @@ -465,7 +466,7 @@ pub(crate) fn view(portal: &CosmicPortal, id: window::Id) -> cosmic::Element cosmic::Command cosmic::Command { let action = cmd(); - return data_device::action(action); + data_device::action(action) } Msg::WindowChosen(name, i) => { if let Some(args) = portal.screenshot_args.as_mut() { @@ -670,94 +671,89 @@ pub fn update_msg(portal: &mut CosmicPortal, msg: Msg) -> cosmic::Command cosmic::Command { - match msg { - args => { - let Args { - handle, - app_id, - parent_window, - options, - output_images: images, - tx, - choice, - action, - location, - toplevel_images, - } = &args; - - if portal.outputs.len() != images.len() { - log::error!( - "Screenshot output count mismatch: {} != {}", - portal.outputs.len(), - images.len() - ); - log::warn!("Screenshot outputs: {:?}", portal.outputs); - log::warn!("Screenshot images: {:?}", images.keys().collect::>()); - return cosmic::Command::none(); - } - - // update output bg sources - if let Ok(c) = cosmic::cosmic_config::Config::new_state( - cosmic_bg_config::NAME, - cosmic_bg_config::state::State::version(), - ) { - let bg_state = match cosmic_bg_config::state::State::get_entry(&c) { - Ok(state) => state, - Err((err, s)) => { - log::error!("Failed to get bg config state: {:?}", err); - s - } - }; - for o in &mut portal.outputs { - let source = bg_state.wallpapers.iter().find(|s| s.0 == o.name); - o.bg_source = Some(source.cloned().map(|s| s.1).unwrap_or_else(|| { - cosmic_bg_config::Source::Path( - "/usr/share/backgrounds/pop/kate-hazen-COSMIC-desktop-wallpaper.png" - .into(), - ) - })); - } - } else { - log::error!("Failed to get bg config state"); - for o in &mut portal.outputs { - o.bg_source = Some(cosmic_bg_config::Source::Path( - "/usr/share/backgrounds/pop/kate-hazen-COSMIC-desktop-wallpaper.png".into(), - )); - } - } - portal.location_options = vec![fl!("save-to", "pictures"), fl!("save-to", "documents")]; +pub fn update_args(portal: &mut CosmicPortal, args: Args) -> cosmic::Command { + let Args { + handle, + app_id, + parent_window, + options, + output_images: images, + tx, + choice, + action, + location, + toplevel_images, + } = &args; + + if portal.outputs.len() != images.len() { + log::error!( + "Screenshot output count mismatch: {} != {}", + portal.outputs.len(), + images.len() + ); + log::warn!("Screenshot outputs: {:?}", portal.outputs); + log::warn!("Screenshot images: {:?}", images.keys().collect::>()); + return cosmic::Command::none(); + } - if portal.screenshot_args.replace(args).is_none() { - // iterate over outputs and create a layer surface for each - let cmds: Vec<_> = portal - .outputs - .iter() - .filter_map( - |OutputState { - output, id, name, .. - }| { - Some(get_layer_surface(SctkLayerSurfaceSettings { - id: *id, - layer: Layer::Overlay, - keyboard_interactivity: KeyboardInteractivity::Exclusive, - pointer_interactivity: true, - anchor: Anchor::all(), - output: IcedOutput::Output(output.clone()), - namespace: "screenshot".to_string(), - size: Some((None, None)), - exclusive_zone: -1, - size_limits: Limits::NONE.min_height(1.0).min_width(1.0), - ..Default::default() - })) - }, - ) - .collect(); - cosmic::Command::batch(cmds) - } else { - log::info!("Existing screenshot args updated"); - cosmic::Command::none() + // update output bg sources + if let Ok(c) = cosmic::cosmic_config::Config::new_state( + cosmic_bg_config::NAME, + cosmic_bg_config::state::State::version(), + ) { + let bg_state = match cosmic_bg_config::state::State::get_entry(&c) { + Ok(state) => state, + Err((err, s)) => { + log::error!("Failed to get bg config state: {:?}", err); + s } + }; + for o in &mut portal.outputs { + let source = bg_state.wallpapers.iter().find(|s| s.0 == o.name); + o.bg_source = Some(source.cloned().map(|s| s.1).unwrap_or_else(|| { + cosmic_bg_config::Source::Path( + "/usr/share/backgrounds/pop/kate-hazen-COSMIC-desktop-wallpaper.png".into(), + ) + })); } + } else { + log::error!("Failed to get bg config state"); + for o in &mut portal.outputs { + o.bg_source = Some(cosmic_bg_config::Source::Path( + "/usr/share/backgrounds/pop/kate-hazen-COSMIC-desktop-wallpaper.png".into(), + )); + } + } + portal.location_options = vec![fl!("save-to", "pictures"), fl!("save-to", "documents")]; + + if portal.screenshot_args.replace(args).is_none() { + // iterate over outputs and create a layer surface for each + let cmds: Vec<_> = portal + .outputs + .iter() + .map( + |OutputState { + output, id, name, .. + }| { + get_layer_surface(SctkLayerSurfaceSettings { + id: *id, + layer: Layer::Overlay, + keyboard_interactivity: KeyboardInteractivity::Exclusive, + pointer_interactivity: true, + anchor: Anchor::all(), + output: IcedOutput::Output(output.clone()), + namespace: "screenshot".to_string(), + size: Some((None, None)), + exclusive_zone: -1, + size_limits: Limits::NONE.min_height(1.0).min_width(1.0), + ..Default::default() + }) + }, + ) + .collect(); + cosmic::Command::batch(cmds) + } else { + log::info!("Existing screenshot args updated"); + cosmic::Command::none() } } diff --git a/src/wayland/mod.rs b/src/wayland/mod.rs index 6c6ae0d..5f8d6e3 100644 --- a/src/wayland/mod.rs +++ b/src/wayland/mod.rs @@ -34,7 +34,7 @@ use std::{ unix::{fs::MetadataExt, net::UnixStream}, }, process, - sync::{Arc, Condvar, Mutex, MutexGuard, Weak}, + sync::{Arc, Condvar, Mutex, Weak}, thread, }; use wayland_client::{ @@ -131,7 +131,7 @@ impl AppData { let Some(o) = self .workspace_state .workspace_groups() - .into_iter() + .iter() .find_map(|wg| { wg.workspaces.iter().find_map(|w| { info.workspace @@ -155,7 +155,7 @@ impl AppData { std::collections::HashMap::new(), |mut map, (outputs, toplevel)| { for o in outputs { - map.entry(o).or_insert_with(Vec::new).push(toplevel.clone()); + map.entry(o).or_default().push(toplevel.clone()); } map }, @@ -166,7 +166,6 @@ impl AppData { #[derive(Default)] struct SessionState { formats: Option, - res: Option>>, } struct SessionInner { @@ -314,8 +313,6 @@ impl WaylandHelper { output: &wl_output::WlOutput, overlay_cursor: bool, ) -> Vec> { - use std::ffi::CStr; - // get the active workspace for this output // get the toplevels for that workspace // capture each toplevel @@ -350,12 +347,12 @@ impl WaylandHelper { CaptureSource::Output(o) => { self.inner .output_source_manager - .create_source(&o, &self.inner.qh, ()) + .create_source(o, &self.inner.qh, ()) } CaptureSource::Toplevel(t) => { self.inner .toplevel_source_manager - .create_source(&t, &self.inner.qh, ()) + .create_source(t, &self.inner.qh, ()) } }; @@ -393,17 +390,12 @@ impl WaylandHelper { // XXX error type? // TODO: way to get cursor metadata? - use std::ffi::CStr; - let name = unsafe { CStr::from_bytes_with_nul_unchecked(b"pipewire-screencopy\0") }; - let fd = rustix::fs::memfd_create(name, rustix::fs::MemfdFlags::CLOEXEC).unwrap(); // XXX - let session = self.capture_source_session(source, overlay_cursor); // TODO: Check that format has been advertised in `Formats` let (width, height) = session.wait_for_formats(|formats| formats.buffer_size); - let buf_len = width * 4 * height; - if let Err(err) = rustix::fs::ftruncate(&fd, buf_len as _) {}; + let fd = buffer::create_memfd(width, height); let buffer = self.create_shm_buffer(&fd, width, height, width * 4, wl_shm::Format::Abgr8888); @@ -768,6 +760,7 @@ impl ScreencopySessionDataExt for SessionData { struct FrameData { frame_data: ScreencopyFrameData, + #[allow(clippy::type_complexity)] sender: Mutex< Option>>>, >, diff --git a/src/widget/rectangle_selection.rs b/src/widget/rectangle_selection.rs index 2aa2815..262ee41 100644 --- a/src/widget/rectangle_selection.rs +++ b/src/widget/rectangle_selection.rs @@ -99,14 +99,13 @@ impl RectangleSelection { (inner_rect.bottom - inner_rect.top).abs() as f32, ), ); - let inner_rect = Rectangle::new( + Rectangle::new( Point::new( inner_rect.x - self.output_rect.left as f32, inner_rect.y - self.output_rect.top as f32, ), inner_rect.size(), - ); - inner_rect + ) } fn drag_state(&self, cursor: mouse::Cursor) -> DragState { diff --git a/src/widget/screenshot.rs b/src/widget/screenshot.rs index 84fb00c..afe1893 100644 --- a/src/widget/screenshot.rs +++ b/src/widget/screenshot.rs @@ -64,7 +64,7 @@ pub struct MyImage(Arc); impl AsRef<[u8]> for MyImage { fn as_ref(&self) -> &[u8] { - &self.0.as_bytes() + self.0.as_bytes() } } @@ -183,7 +183,7 @@ where cosmic::iced_style::container::Appearance { background: Some(match color { cosmic_bg_config::Color::Single(c) => Background::Color( - cosmic::iced::Color::new(c[0], c[1], c[2], 1.0).into(), + cosmic::iced::Color::new(c[0], c[1], c[2], 1.0), ), cosmic_bg_config::Color::Gradient(cosmic_bg_config::Gradient { colors, @@ -527,7 +527,7 @@ impl<'a, Msg> cosmic::widget::Widget let tree = &tree.children[i]; child .as_widget() - .draw(&tree, renderer, theme, style, layout, cursor, viewport); + .draw(tree, renderer, theme, style, layout, cursor, viewport); } } }