From db3bfd9e5f0059c0f57c4db972fb7d42f5b6c392 Mon Sep 17 00:00:00 2001 From: Ottatop Date: Sun, 14 Jul 2024 15:45:02 -0500 Subject: [PATCH] api/rust: Add error handling --- Cargo.lock | 12 +- README.md | 2 +- api/rust/Cargo.toml | 2 + api/rust/pinnacle-api-macros/src/lib.rs | 29 ++++- api/rust/src/input.rs | 79 ++++++++----- api/rust/src/layout.rs | 24 +++- api/rust/src/lib.rs | 16 +++ api/rust/src/output.rs | 114 +++++++++++-------- api/rust/src/pinnacle.rs | 8 +- api/rust/src/process.rs | 18 ++- api/rust/src/render.rs | 15 ++- api/rust/src/snowcap/integration.rs | 2 + api/rust/src/tag.rs | 69 ++++++----- api/rust/src/window.rs | 145 +++++++++++++----------- snowcap | 2 +- 15 files changed, 339 insertions(+), 198 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9be8caa..463c785 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3087,6 +3087,8 @@ dependencies = [ "tokio-stream", "tonic", "tower", + "tracing", + "tracing-subscriber", "xkbcommon", ] @@ -3860,10 +3862,12 @@ dependencies = [ "from_variants", "futures", "snowcap-api-defs", + "thiserror", "tokio", "tokio-stream", "tonic", "tower", + "tracing", "xdg", "xkbcommon", ] @@ -4087,18 +4091,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.61" +version = "1.0.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +checksum = "f2675633b1499176c2dff06b0856a27976a8f9d436737b4cf4f312d4d91d8bbb" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.61" +version = "1.0.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +checksum = "d20468752b09f49e909e55a5d338caa8bedf615594e9d80bc4c565d30faf798c" dependencies = [ "proc-macro2", "quote", diff --git a/README.md b/README.md index 0fad382..3f005a2 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ In the future, Snowcap will be used for everything Awesome uses its widget syste # Dependencies You will need: -- [Rust](https://www.rust-lang.org/) 1.75 or newer +- [Rust](https://www.rust-lang.org/) 1.76 or newer - The following external dependencies: - `libwayland` - `libxkbcommon` diff --git a/api/rust/Cargo.toml b/api/rust/Cargo.toml index 46c725f..f9cc25d 100644 --- a/api/rust/Cargo.toml +++ b/api/rust/Cargo.toml @@ -23,6 +23,8 @@ rand = "0.8.5" bitflags = { workspace = true } snowcap-api = { path = "../../snowcap/api/rust", optional = true } indexmap = { workspace = true } +tracing = { workspace = true } +tracing-subscriber = { workspace = true } [features] default = ["snowcap"] diff --git a/api/rust/pinnacle-api-macros/src/lib.rs b/api/rust/pinnacle-api-macros/src/lib.rs index 2da521b..444d50c 100644 --- a/api/rust/pinnacle-api-macros/src/lib.rs +++ b/api/rust/pinnacle-api-macros/src/lib.rs @@ -79,8 +79,10 @@ pub fn config( let options = macro_input.options; let mut has_internal_tokio = false; + let mut has_internal_tracing = false; let mut internal_tokio = true; + let mut internal_tracing = true; for name_value in options.iter() { if name_value.path.get_ident() == Some(&Ident::new("internal_tokio", Span::call_site())) { @@ -103,9 +105,26 @@ pub fn config( compile_error!("expected `true` or `false`"); } .into(); + } else if name_value.path.get_ident() + == Some(&Ident::new("internal_tracing", Span::call_site())) + { + if has_internal_tracing { + return quote_spanned! {name_value.path.span()=> + compile_error!("`internal_tracing` defined twice, remove this one"); + } + .into(); + } + + has_internal_tracing = true; + if let Expr::Lit(lit) = &name_value.value { + if let Lit::Bool(bool) = &lit.lit { + internal_tracing = bool.value; + continue; + } + } } else { return quote_spanned! {name_value.path.span()=> - compile_error!("expected valid option (currently only `internal_tokio`)"); + compile_error!("expected valid option (`internal_tokio` or `internal_tracing`)"); } .into(); } @@ -117,10 +136,18 @@ pub fn config( } }); + let tracing_fn = internal_tracing.then(|| { + quote! { + ::pinnacle_api::set_default_tracing_subscriber(); + } + }); + quote! { #(#attrs)* #tokio_attr #vis #sig { + #tracing_fn + ::pinnacle_api::connect().await.unwrap(); #(#stmts)* diff --git a/api/rust/src/input.rs b/api/rust/src/input.rs index c8c84be..3d868e5 100644 --- a/api/rust/src/input.rs +++ b/api/rust/src/input.rs @@ -18,6 +18,7 @@ use pinnacle_api_defs::pinnacle::input::{ }, }; use tokio_stream::StreamExt; +use tracing::error; use xkbcommon::xkb::Keysym; use crate::block_on_tokio; @@ -169,16 +170,20 @@ impl Input { let keybind_info: Option = keybind_info.into(); - let mut stream = block_on_tokio(crate::input().set_keybind(SetKeybindRequest { + let mut stream = match block_on_tokio(crate::input().set_keybind(SetKeybindRequest { modifiers, key: Some(input::v0alpha1::set_keybind_request::Key::RawCode( key.into_keysym().raw(), )), group: keybind_info.clone().and_then(|info| info.group), description: keybind_info.clone().and_then(|info| info.description), - })) - .unwrap() - .into_inner(); + })) { + Ok(stream) => stream.into_inner(), + Err(err) => { + error!("Failed to set keybind: {err}"); + return; + } + }; tokio::spawn(async move { while let Some(Ok(_response)) = stream.next().await { @@ -218,13 +223,17 @@ impl Input { mut action: impl FnMut() + 'static + Send, ) { let modifiers = mods.into_iter().map(|modif| modif as i32).collect(); - let mut stream = block_on_tokio(crate::input().set_mousebind(SetMousebindRequest { + let mut stream = match block_on_tokio(crate::input().set_mousebind(SetMousebindRequest { modifiers, button: Some(button as u32), edge: Some(edge as i32), - })) - .unwrap() - .into_inner(); + })) { + Ok(stream) => stream.into_inner(), + Err(err) => { + error!("Failed to set keybind: {err}"); + return; + } + }; tokio::spawn(async move { while let Some(Ok(_response)) = stream.next().await { @@ -236,12 +245,17 @@ impl Input { /// Get all keybinds and their information. pub fn keybind_descriptions(&self) -> impl Iterator { - let descriptions = - block_on_tokio(crate::input().keybind_descriptions(KeybindDescriptionsRequest {})) - .unwrap(); - let descriptions = descriptions.into_inner(); + let descriptions = match block_on_tokio( + crate::input().keybind_descriptions(KeybindDescriptionsRequest {}), + ) { + Ok(descs) => descs.into_inner().descriptions, + Err(err) => { + error!("Failed to get keybind descriptions: {err}"); + Vec::new() + } + }; - descriptions.descriptions.into_iter().map(|desc| { + descriptions.into_iter().map(|desc| { let mods = desc.modifiers().flat_map(|m| match m { input::v0alpha1::Modifier::Unspecified => None, input::v0alpha1::Modifier::Shift => Some(Mod::Shift), @@ -277,14 +291,15 @@ impl Input { /// }); /// ``` pub fn set_xkb_config(&self, xkb_config: XkbConfig) { - block_on_tokio(crate::input().set_xkb_config(SetXkbConfigRequest { + if let Err(err) = block_on_tokio(crate::input().set_xkb_config(SetXkbConfigRequest { rules: xkb_config.rules.map(String::from), variant: xkb_config.variant.map(String::from), layout: xkb_config.layout.map(String::from), model: xkb_config.model.map(String::from), options: xkb_config.options.map(String::from), - })) - .unwrap(); + })) { + error!("Failed to set xkb config: {err}"); + } } /// Set the keyboard's repeat rate. @@ -302,11 +317,12 @@ impl Input { /// input.set_repeat_rate(25, 500); /// ``` pub fn set_repeat_rate(&self, rate: i32, delay: i32) { - block_on_tokio(crate::input().set_repeat_rate(SetRepeatRateRequest { + if let Err(err) = block_on_tokio(crate::input().set_repeat_rate(SetRepeatRateRequest { rate: Some(rate), delay: Some(delay), - })) - .unwrap(); + })) { + error!("Failed to set repeat rate: {err}"); + } } /// Set a libinput setting. @@ -357,12 +373,13 @@ impl Input { LibinputSetting::Tap(enable) => Setting::Tap(enable), }; - block_on_tokio( - crate::input().set_libinput_setting(SetLibinputSettingRequest { + if let Err(err) = block_on_tokio(crate::input().set_libinput_setting( + SetLibinputSettingRequest { setting: Some(setting), - }), - ) - .unwrap(); + }, + )) { + error!("Failed to set libinput setting: {err}"); + } } /// Set the xcursor theme. @@ -376,11 +393,12 @@ impl Input { /// input.set_xcursor_theme("Adwaita"); /// ``` pub fn set_xcursor_theme(&self, theme: impl ToString) { - block_on_tokio(crate::input().set_xcursor(SetXcursorRequest { + if let Err(err) = block_on_tokio(crate::input().set_xcursor(SetXcursorRequest { theme: Some(theme.to_string()), size: None, - })) - .unwrap(); + })) { + error!("Failed to set xcursor theme: {err}"); + } } /// Set the xcursor size. @@ -394,11 +412,12 @@ impl Input { /// input.set_xcursor_size(64); /// ``` pub fn set_xcursor_size(&self, size: u32) { - block_on_tokio(crate::input().set_xcursor(SetXcursorRequest { + if let Err(err) = block_on_tokio(crate::input().set_xcursor(SetXcursorRequest { theme: None, size: Some(size), - })) - .unwrap(); + })) { + error!("Failed to set xcursor size: {err}"); + } } } diff --git a/api/rust/src/layout.rs b/api/rust/src/layout.rs index a5b2d8b..754d002 100644 --- a/api/rust/src/layout.rs +++ b/api/rust/src/layout.rs @@ -17,6 +17,7 @@ use pinnacle_api_defs::pinnacle::layout::v0alpha1::{ }; use tokio::sync::mpsc::{unbounded_channel, UnboundedSender}; use tokio_stream::StreamExt; +use tracing::debug; use crate::{ block_on_tokio, layout, @@ -73,7 +74,7 @@ impl Layout { output_height: response.output_height.unwrap_or_default(), }; let geos = manager.lock().unwrap().active_layout(&args).layout(&args); - from_client + if from_client .send(LayoutRequest { body: Some(Body::Geometries(Geometries { request_id: response.request_id, @@ -89,7 +90,10 @@ impl Layout { .collect(), })), }) - .unwrap(); + .is_err() + { + debug!("Failed to send layout geometries: channel closed"); + } } }; @@ -235,22 +239,30 @@ impl LayoutRequester { /// If you want to layout a specific output, see [`LayoutRequester::request_layout_on_output`]. pub fn request_layout(&self) { let output_name = Output.get_focused().map(|op| op.name); - self.sender + if self + .sender .send(LayoutRequest { body: Some(Body::Layout(ExplicitLayout { output_name })), }) - .unwrap(); + .is_err() + { + debug!("Failed to request layout: channel closed"); + } } /// Request a layout from the compositor for the given output. pub fn request_layout_on_output(&self, output: &OutputHandle) { - self.sender + if self + .sender .send(LayoutRequest { body: Some(Body::Layout(ExplicitLayout { output_name: Some(output.name.clone()), })), }) - .unwrap(); + .is_err() + { + debug!("Failed to request layout on output: channel closed"); + } } } diff --git a/api/rust/src/lib.rs b/api/rust/src/lib.rs index 544b40c..dd0cf6b 100644 --- a/api/rust/src/lib.rs +++ b/api/rust/src/lib.rs @@ -98,6 +98,7 @@ use tag::Tag; use tokio::sync::{MappedMutexGuard, Mutex, MutexGuard, RwLock}; use tonic::transport::{Channel, Endpoint, Uri}; use tower::service_fn; +use tracing::info; use window::Window; pub mod input; @@ -260,6 +261,9 @@ pub async fn connect() -> Result<(), Box> { .await .unwrap(); + let socket_path = std::env::var("PINNACLE_GRPC_SOCKET").unwrap(); + info!("Connected to {socket_path}"); + PINNACLE .write() .await @@ -321,6 +325,18 @@ pub async fn listen() { signal_module().shutdown(); } +/// Sets the default `tracing_subscriber` to output logs. +/// +/// This subscriber does not include the time or ansi escape codes. +/// If you would like to disable this in [`crate::config`], pass in +/// `internal_tracing = false`. +pub fn set_default_tracing_subscriber() { + tracing_subscriber::fmt() + .without_time() + .with_ansi(false) + .init(); +} + /// Block on a future using the current Tokio runtime. pub(crate) fn block_on_tokio(future: F) -> F::Output { tokio::task::block_in_place(|| { diff --git a/api/rust/src/output.rs b/api/rust/src/output.rs index d1e819f..0b54589 100644 --- a/api/rust/src/output.rs +++ b/api/rust/src/output.rs @@ -19,6 +19,7 @@ use pinnacle_api_defs::pinnacle::output::{ SetModelineRequest, SetPoweredRequest, SetScaleRequest, SetTransformRequest, }, }; +use tracing::{error, instrument}; use crate::{ block_on_tokio, @@ -56,9 +57,9 @@ impl Output { crate::output() .get(output::v0alpha1::GetRequest {}) .await - .unwrap() - .into_inner() - .output_names + .map(|resp| resp.into_inner().output_names) + .inspect_err(|err| error!("Failed to get outputs: {err}")) + .unwrap_or_default() .into_iter() .map(|name| self.new_handle(name)) .collect() @@ -151,7 +152,7 @@ impl Output { /// // Add tags 1-3 to all outputs and set tag "1" to active /// output.connect_for_all(|op| { /// let tags = tag.add(&op, ["1", "2", "3"]); - /// tags.next().unwrap().set_active(true); + /// tags.first()?.set_active(true); /// }); /// ``` pub fn connect_for_all(&self, mut for_all: impl FnMut(&OutputHandle) + Send + 'static) { @@ -200,7 +201,7 @@ impl Output { /// // Give all outputs tags 1 through 5 /// OutputSetup::new_with_matcher(|_| true).with_tags(["1", "2", "3", "4", "5"]), /// // Give outputs with a preferred mode of 4K a scale of 2.0 - /// OutputSetup::new_with_matcher(|op| op.preferred_mode().unwrap().pixel_width == 2160) + /// OutputSetup::new_with_matcher(|op| op.preferred_mode()?.pixel_width == 2160) /// .with_scale(2.0), /// // Additionally give eDP-1 tags 6 and 7 /// OutputSetup::new(OutputId::name("eDP-1")).with_tags(["6", "7"]), @@ -289,8 +290,11 @@ impl Output { placed_outputs.push(output.clone()); let props = output.props(); - let x = props.x.unwrap(); - let width = props.logical_width.unwrap() as i32; + let x = props.x.expect("output should have x-coord"); + let width = props + .logical_width + .expect("output should have logical width") + as i32; if rightmost_output_and_x.is_none() || rightmost_output_and_x .as_ref() @@ -328,8 +332,10 @@ impl Output { placed_outputs.push(output.clone()); let props = output.props(); - let x = props.x.unwrap(); - let width = props.logical_width.unwrap() as i32; + let x = props.x.expect("output should have x-coord"); + let width = props + .logical_width + .expect("output should have logical width") as i32; if rightmost_output_and_x.is_none() || rightmost_output_and_x .as_ref() @@ -353,8 +359,10 @@ impl Output { placed_outputs.push(output.clone()); let props = output.props(); - let x = props.x.unwrap(); - let width = props.logical_width.unwrap() as i32; + let x = props.x.expect("output should have x-coord"); + let width = props + .logical_width + .expect("output should have logical width") as i32; if rightmost_output_and_x.is_none() || rightmost_output_and_x .as_ref() @@ -590,25 +598,11 @@ bitflags::bitflags! { /// A handle to an output. /// /// This allows you to manipulate outputs and get their properties. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct OutputHandle { pub(crate) name: String, } -impl PartialEq for OutputHandle { - fn eq(&self, other: &Self) -> bool { - self.name == other.name - } -} - -impl Eq for OutputHandle {} - -impl std::hash::Hash for OutputHandle { - fn hash(&self, state: &mut H) { - self.name.hash(state); - } -} - /// The alignment to use for [`OutputHandle::set_loc_adj_to`]. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Alignment { @@ -696,13 +690,15 @@ impl OutputHandle { /// // └─────┴───────┘ /// // ^x=1920 /// ``` + #[instrument(skip(x, y))] pub fn set_location(&self, x: impl Into>, y: impl Into>) { - block_on_tokio(crate::output().set_location(SetLocationRequest { + if let Err(err) = block_on_tokio(crate::output().set_location(SetLocationRequest { output_name: Some(self.name.clone()), x: x.into(), y: y.into(), - })) - .unwrap(); + })) { + error!("{err}"); + } } /// Set this output adjacent to another one. @@ -822,19 +818,21 @@ impl OutputHandle { /// ``` /// output.get_focused()?.set_mode(2560, 1440, 144000); /// ``` + #[instrument(skip(refresh_rate_millihertz))] pub fn set_mode( &self, pixel_width: u32, pixel_height: u32, refresh_rate_millihertz: impl Into>, ) { - block_on_tokio(crate::output().set_mode(SetModeRequest { + if let Err(err) = block_on_tokio(crate::output().set_mode(SetModeRequest { output_name: Some(self.name.clone()), pixel_width: Some(pixel_width), pixel_height: Some(pixel_height), refresh_rate_millihz: refresh_rate_millihertz.into(), - })) - .unwrap(); + })) { + error!("{err}"); + } } /// Set a custom modeline for this output. @@ -849,8 +847,9 @@ impl OutputHandle { /// ``` /// output.set_modeline("173.00 1920 2048 2248 2576 1080 1083 1088 1120 -hsync +vsync".parse()?); /// ``` + #[instrument(skip(modeline))] pub fn set_modeline(&self, modeline: Modeline) { - block_on_tokio(crate::output().set_modeline(SetModelineRequest { + if let Err(err) = block_on_tokio(crate::output().set_modeline(SetModelineRequest { output_name: Some(self.name.clone()), clock: Some(modeline.clock), hdisplay: Some(modeline.hdisplay), @@ -863,8 +862,9 @@ impl OutputHandle { vtotal: Some(modeline.vtotal), hsync_pos: Some(modeline.hsync), vsync_pos: Some(modeline.vsync), - })) - .unwrap(); + })) { + error!("{err}"); + } } /// Set this output's scaling factor. @@ -874,12 +874,14 @@ impl OutputHandle { /// ``` /// output.get_focused()?.set_scale(1.5); /// ``` + #[instrument] pub fn set_scale(&self, scale: f32) { - block_on_tokio(crate::output().set_scale(SetScaleRequest { + if let Err(err) = block_on_tokio(crate::output().set_scale(SetScaleRequest { output_name: Some(self.name.clone()), absolute_or_relative: Some(AbsoluteOrRelative::Absolute(scale)), - })) - .unwrap(); + })) { + error!("{err}"); + } } /// Increase this output's scaling factor by `increase_by`. @@ -889,12 +891,14 @@ impl OutputHandle { /// ``` /// output.get_focused()?.increase_scale(0.25); /// ``` + #[instrument] pub fn increase_scale(&self, increase_by: f32) { - block_on_tokio(crate::output().set_scale(SetScaleRequest { + if let Err(err) = block_on_tokio(crate::output().set_scale(SetScaleRequest { output_name: Some(self.name.clone()), absolute_or_relative: Some(AbsoluteOrRelative::Relative(increase_by)), - })) - .unwrap(); + })) { + error!("{err}"); + } } /// Decrease this output's scaling factor by `decrease_by`. @@ -920,12 +924,14 @@ impl OutputHandle { /// // Rotate 90 degrees counter-clockwise /// output.set_transform(Transform::_90); /// ``` + #[instrument] pub fn set_transform(&self, transform: Transform) { - block_on_tokio(crate::output().set_transform(SetTransformRequest { + if let Err(err) = block_on_tokio(crate::output().set_transform(SetTransformRequest { output_name: Some(self.name.clone()), transform: Some(transform as i32), - })) - .unwrap(); + })) { + error!("{err}"); + } } /// Power on or off this output. @@ -939,12 +945,14 @@ impl OutputHandle { /// // Power off `output` /// output.set_powered(false); /// ``` + #[instrument] pub fn set_powered(&self, powered: bool) { - block_on_tokio(crate::output().set_powered(SetPoweredRequest { + if let Err(err) = block_on_tokio(crate::output().set_powered(SetPoweredRequest { output_name: Some(self.name.clone()), powered: Some(powered), - })) - .unwrap(); + })) { + error!("{err}"); + } } /// Get all properties of this output. @@ -963,14 +971,20 @@ impl OutputHandle { } /// The async version of [`OutputHandle::props`]. + #[instrument] pub async fn props_async(&self) -> OutputProperties { - let response = crate::output() + let response = match crate::output() .get_properties(output::v0alpha1::GetPropertiesRequest { output_name: Some(self.name.clone()), }) .await - .unwrap() - .into_inner(); + { + Ok(resp) => resp.into_inner(), + Err(err) => { + error!("{err}"); + return OutputProperties::default(); + } + }; OutputProperties { make: response.make, diff --git a/api/rust/src/pinnacle.rs b/api/rust/src/pinnacle.rs index 32e11d8..99789ec 100644 --- a/api/rust/src/pinnacle.rs +++ b/api/rust/src/pinnacle.rs @@ -8,11 +8,15 @@ use std::time::Duration; -use pinnacle_api_defs::pinnacle::v0alpha1::{ - PingRequest, QuitRequest, ReloadConfigRequest, ShutdownWatchRequest, ShutdownWatchResponse, +use pinnacle_api_defs::pinnacle::{ + v0alpha1::{ + PingRequest, QuitRequest, ReloadConfigRequest, ShutdownWatchRequest, ShutdownWatchResponse, + }, + window::v0alpha1::SetFocusedRequest, }; use rand::RngCore; use tonic::{Request, Streaming}; +use tracing::error; use crate::{block_on_tokio, pinnacle}; diff --git a/api/rust/src/process.rs b/api/rust/src/process.rs index 88da944..142b963 100644 --- a/api/rust/src/process.rs +++ b/api/rust/src/process.rs @@ -9,6 +9,7 @@ use pinnacle_api_defs::pinnacle::process::v0alpha1::{SetEnvRequest, SpawnRequest}; use tokio_stream::StreamExt; +use tracing::error; use crate::{block_on_tokio, process}; @@ -110,9 +111,13 @@ impl Process { has_callback: Some(callbacks.is_some()), }; - let mut stream = block_on_tokio(process().spawn(request)) - .unwrap() - .into_inner(); + let mut stream = match block_on_tokio(process().spawn(request)) { + Ok(stream) => stream.into_inner(), + Err(err) => { + error!("Failed to spawn process: {err}"); + return; + } + }; tokio::spawn(async move { let Some(mut callbacks) = callbacks else { return }; @@ -149,10 +154,11 @@ impl Process { let key = key.into(); let value = value.into(); - block_on_tokio(process().set_env(SetEnvRequest { + if let Err(err) = block_on_tokio(process().set_env(SetEnvRequest { key: Some(key), value: Some(value), - })) - .unwrap(); + })) { + error!("Failed to set env: {err}"); + } } } diff --git a/api/rust/src/render.rs b/api/rust/src/render.rs index aff278c..c6eea55 100644 --- a/api/rust/src/render.rs +++ b/api/rust/src/render.rs @@ -3,6 +3,7 @@ use pinnacle_api_defs::pinnacle::render::v0alpha1::{ SetDownscaleFilterRequest, SetUpscaleFilterRequest, }; +use tracing::error; use crate::{block_on_tokio, render}; @@ -33,10 +34,11 @@ impl Render { /// render.set_upscale_filter(ScalingFilter::NearestNeighbor); /// ``` pub fn set_upscale_filter(&self, filter: ScalingFilter) { - block_on_tokio(render().set_upscale_filter(SetUpscaleFilterRequest { + if let Err(err) = block_on_tokio(render().set_upscale_filter(SetUpscaleFilterRequest { filter: Some(filter as i32), - })) - .unwrap(); + })) { + error!("Failed to set upscale filter: {err}"); + } } /// Set the downscaling filter that will be used for rendering. @@ -49,9 +51,10 @@ impl Render { /// render.set_downscale_filter(ScalingFilter::NearestNeighbor); /// ``` pub fn set_downscale_filter(&self, filter: ScalingFilter) { - block_on_tokio(render().set_downscale_filter(SetDownscaleFilterRequest { + if let Err(err) = block_on_tokio(render().set_downscale_filter(SetDownscaleFilterRequest { filter: Some(filter as i32), - })) - .unwrap(); + })) { + error!("Failed to set downscale filter: {err}"); + } } } diff --git a/api/rust/src/snowcap/integration.rs b/api/rust/src/snowcap/integration.rs index e1a7937..a7b9304 100644 --- a/api/rust/src/snowcap/integration.rs +++ b/api/rust/src/snowcap/integration.rs @@ -106,6 +106,7 @@ impl QuitPrompt { ExclusiveZone::Respect, ZLayer::Overlay, ) + .unwrap() .on_key_press(|handle, key, _mods| { if key == Keysym::Return { Pinnacle.quit(); @@ -291,6 +292,7 @@ impl KeybindOverlay { ExclusiveZone::Respect, ZLayer::Top, ) + .unwrap() .on_key_press(|handle, _key, _mods| { handle.close(); }); diff --git a/api/rust/src/tag.rs b/api/rust/src/tag.rs index 61ac790..f0ad464 100644 --- a/api/rust/src/tag.rs +++ b/api/rust/src/tag.rs @@ -37,6 +37,7 @@ use pinnacle_api_defs::pinnacle::{ }, v0alpha1::SetOrToggle, }; +use tracing::{error, instrument}; use crate::{ block_on_tokio, @@ -85,17 +86,17 @@ impl Tag { ) -> Vec { let tag_names = tag_names.into_iter().map(Into::into).collect(); - let response = crate::tag() + let tag_ids = crate::tag() .add(AddRequest { output_name: Some(output.name.clone()), tag_names, }) .await - .unwrap() - .into_inner(); + .map(|resp| resp.into_inner().tag_ids) + .inspect_err(|err| error!("Failed to add tags: {err}")) + .unwrap_or_default(); - response - .tag_ids + tag_ids .into_iter() .map(move |id| self.new_handle(id)) .collect() @@ -114,14 +115,14 @@ impl Tag { /// The async version of [`Tag::get_all`]. pub async fn get_all_async(&self) -> Vec { - let response = crate::tag() + let tag_ids = crate::tag() .get(tag::v0alpha1::GetRequest {}) .await - .unwrap() - .into_inner(); + .map(|resp| resp.into_inner().tag_ids) + .inspect_err(|err| error!("Failed to get tags: {err}")) + .unwrap_or_default(); - response - .tag_ids + tag_ids .into_iter() .map(move |id| self.new_handle(id)) .collect() @@ -202,7 +203,9 @@ impl Tag { pub fn remove(&self, tags: impl IntoIterator) { let tag_ids = tags.into_iter().map(|handle| handle.id).collect::>(); - block_on_tokio(crate::tag().remove(RemoveRequest { tag_ids })).unwrap(); + if let Err(err) = block_on_tokio(crate::tag().remove(RemoveRequest { tag_ids })) { + error!("Failed to remove tags: {err}"); + } } /// Connect to a tag signal. @@ -256,11 +259,13 @@ impl TagHandle { /// tag.get("2")?.switch_to(); // Displays Firefox and Discord /// tag.get("3")?.switch_to(); // Displays Steam /// ``` + #[instrument] pub fn switch_to(&self) { - block_on_tokio(crate::tag().switch_to(SwitchToRequest { + if let Err(err) = block_on_tokio(crate::tag().switch_to(SwitchToRequest { tag_id: Some(self.id), - })) - .unwrap(); + })) { + error!("{err}"); + } } /// Set this tag to active or not. @@ -281,15 +286,17 @@ impl TagHandle { /// tag.get("3")?.set_active(true); // Displays Firefox, Discord, and Steam /// tag.get("2")?.set_active(false); // Displays Steam /// ``` + #[instrument] pub fn set_active(&self, set: bool) { - block_on_tokio(crate::tag().set_active(SetActiveRequest { + if let Err(err) = block_on_tokio(crate::tag().set_active(SetActiveRequest { tag_id: Some(self.id), set_or_toggle: Some(match set { true => SetOrToggle::Set, false => SetOrToggle::Unset, } as i32), - })) - .unwrap(); + })) { + error!("{err}"); + } } /// Toggle this tag between active and inactive. @@ -311,12 +318,14 @@ impl TagHandle { /// tag.get("3")?.toggle(); // Displays Firefox, Discord /// tag.get("2")?.toggle(); // Displays nothing /// ``` + #[instrument] pub fn toggle_active(&self) { - block_on_tokio(crate::tag().set_active(SetActiveRequest { + if let Err(err) = block_on_tokio(crate::tag().set_active(SetActiveRequest { tag_id: Some(self.id), set_or_toggle: Some(SetOrToggle::Toggle as i32), - })) - .unwrap(); + })) { + error!("{err}"); + } } /// Remove this tag from its output. @@ -332,11 +341,13 @@ impl TagHandle { /// tags[3].remove(); /// // "DP-1" now only has tags "1" and "Buckle" /// ``` + #[instrument] pub fn remove(&self) { - block_on_tokio(crate::tag().remove(RemoveRequest { + if let Err(err) = block_on_tokio(crate::tag().remove(RemoveRequest { tag_ids: vec![self.id], - })) - .unwrap(); + })) { + error!("{err}"); + } } /// Get all properties of this tag. @@ -357,14 +368,20 @@ impl TagHandle { } /// The async version of [`TagHandle::props`]. + #[instrument] pub async fn props_async(&self) -> TagProperties { - let response = crate::tag() + let response = match crate::tag() .get_properties(tag::v0alpha1::GetPropertiesRequest { tag_id: Some(self.id), }) .await - .unwrap() - .into_inner(); + { + Ok(resp) => resp.into_inner(), + Err(err) => { + error!("{err}"); + return TagProperties::default(); + } + }; TagProperties { active: response.active, diff --git a/api/rust/src/window.rs b/api/rust/src/window.rs index e09f102..3314666 100644 --- a/api/rust/src/window.rs +++ b/api/rust/src/window.rs @@ -25,6 +25,7 @@ use pinnacle_api_defs::pinnacle::{ }, }, }; +use tracing::{error, instrument}; use crate::{ block_on_tokio, @@ -68,10 +69,10 @@ impl Window { /// }); /// ``` pub fn begin_move(&self, button: MouseButton) { - if let Err(status) = block_on_tokio(crate::window().move_grab(MoveGrabRequest { + if let Err(err) = block_on_tokio(crate::window().move_grab(MoveGrabRequest { button: Some(button as u32), })) { - eprintln!("ERROR: {status}"); + error!("Failed to begin window move: {err}"); } } @@ -93,10 +94,11 @@ impl Window { /// }); /// ``` pub fn begin_resize(&self, button: MouseButton) { - block_on_tokio(crate::window().resize_grab(ResizeGrabRequest { + if let Err(err) = block_on_tokio(crate::window().resize_grab(ResizeGrabRequest { button: Some(button as u32), - })) - .unwrap(); + })) { + error!("Failed to begin window resize: {err}"); + } } /// Get all windows. @@ -115,9 +117,9 @@ impl Window { crate::window() .get(GetRequest {}) .await - .unwrap() - .into_inner() - .window_ids + .map(|resp| resp.into_inner().window_ids) + .inspect_err(|err| error!("Failed to get windows: {err}")) + .unwrap_or_default() .into_iter() .map(move |id| self.new_handle(id)) .collect::>() @@ -149,11 +151,12 @@ impl Window { /// /// See the [`rules`] module for more information. pub fn add_window_rule(&self, cond: WindowRuleCondition, rule: WindowRule) { - block_on_tokio(crate::window().add_window_rule(AddWindowRuleRequest { + if let Err(err) = block_on_tokio(crate::window().add_window_rule(AddWindowRuleRequest { cond: Some(cond.0), rule: Some(rule.0), - })) - .unwrap(); + })) { + error!("Failed to add window rule: {err}"); + } } /// Connect to a window signal. @@ -174,25 +177,11 @@ impl Window { /// A handle to a window. /// /// This allows you to manipulate the window and get its properties. -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct WindowHandle { id: u32, } -impl PartialEq for WindowHandle { - fn eq(&self, other: &Self) -> bool { - self.id == other.id - } -} - -impl Eq for WindowHandle {} - -impl std::hash::Hash for WindowHandle { - fn hash(&self, state: &mut H) { - self.id.hash(state); - } -} - /// Whether a window is fullscreen, maximized, or neither. #[repr(i32)] #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, TryFromPrimitive)] @@ -256,11 +245,13 @@ impl WindowHandle { /// // Close the focused window /// window.get_focused()?.close() /// ``` + #[instrument] pub fn close(&self) { - block_on_tokio(crate::window().close(CloseRequest { + if let Err(err) = block_on_tokio(crate::window().close(CloseRequest { window_id: Some(self.id), - })) - .unwrap(); + })) { + error!("{err}"); + } } /// Set this window to fullscreen or not. @@ -273,15 +264,17 @@ impl WindowHandle { /// // Set the focused window to fullscreen. /// window.get_focused()?.set_fullscreen(true); /// ``` + #[instrument] pub fn set_fullscreen(&self, set: bool) { - block_on_tokio(crate::window().set_fullscreen(SetFullscreenRequest { + if let Err(err) = block_on_tokio(crate::window().set_fullscreen(SetFullscreenRequest { window_id: Some(self.id), set_or_toggle: Some(match set { true => SetOrToggle::Set, false => SetOrToggle::Unset, } as i32), - })) - .unwrap(); + })) { + error!("{err}"); + } } /// Toggle this window between fullscreen and not. @@ -294,12 +287,14 @@ impl WindowHandle { /// // Toggle the focused window to and from fullscreen. /// window.get_focused()?.toggle_fullscreen(); /// ``` + #[instrument] pub fn toggle_fullscreen(&self) { - block_on_tokio(crate::window().set_fullscreen(SetFullscreenRequest { + if let Err(err) = block_on_tokio(crate::window().set_fullscreen(SetFullscreenRequest { window_id: Some(self.id), set_or_toggle: Some(SetOrToggle::Toggle as i32), - })) - .unwrap(); + })) { + error!("{err}"); + } } /// Set this window to maximized or not. @@ -312,15 +307,17 @@ impl WindowHandle { /// // Set the focused window to maximized. /// window.get_focused()?.set_maximized(true); /// ``` + #[instrument] pub fn set_maximized(&self, set: bool) { - block_on_tokio(crate::window().set_maximized(SetMaximizedRequest { + if let Err(err) = block_on_tokio(crate::window().set_maximized(SetMaximizedRequest { window_id: Some(self.id), set_or_toggle: Some(match set { true => SetOrToggle::Set, false => SetOrToggle::Unset, } as i32), - })) - .unwrap(); + })) { + error!("{err}"); + } } /// Toggle this window between maximized and not. @@ -333,12 +330,14 @@ impl WindowHandle { /// // Toggle the focused window to and from maximized. /// window.get_focused()?.toggle_maximized(); /// ``` + #[instrument] pub fn toggle_maximized(&self) { - block_on_tokio(crate::window().set_maximized(SetMaximizedRequest { + if let Err(err) = block_on_tokio(crate::window().set_maximized(SetMaximizedRequest { window_id: Some(self.id), set_or_toggle: Some(SetOrToggle::Toggle as i32), - })) - .unwrap(); + })) { + error!("{err}"); + } } /// Set this window to floating or not. @@ -354,15 +353,17 @@ impl WindowHandle { /// // Set the focused window to floating. /// window.get_focused()?.set_floating(true); /// ``` + #[instrument] pub fn set_floating(&self, set: bool) { - block_on_tokio(crate::window().set_floating(SetFloatingRequest { + if let Err(err) = block_on_tokio(crate::window().set_floating(SetFloatingRequest { window_id: Some(self.id), set_or_toggle: Some(match set { true => SetOrToggle::Set, false => SetOrToggle::Unset, } as i32), - })) - .unwrap(); + })) { + error!("{err}"); + } } /// Toggle this window to and from floating. @@ -378,12 +379,14 @@ impl WindowHandle { /// // Toggle the focused window to and from floating. /// window.get_focused()?.toggle_floating(); /// ``` + #[instrument] pub fn toggle_floating(&self) { - block_on_tokio(crate::window().set_floating(SetFloatingRequest { + if let Err(err) = block_on_tokio(crate::window().set_floating(SetFloatingRequest { window_id: Some(self.id), set_or_toggle: Some(SetOrToggle::Toggle as i32), - })) - .unwrap(); + })) { + error!("{err}"); + } } /// Focus or unfocus this window. @@ -394,15 +397,17 @@ impl WindowHandle { /// // Unfocus the focused window /// window.get_focused()?.set_focused(false); /// ``` + #[instrument] pub fn set_focused(&self, set: bool) { - block_on_tokio(crate::window().set_focused(SetFocusedRequest { + if let Err(err) = block_on_tokio(crate::window().set_focused(SetFocusedRequest { window_id: Some(self.id), set_or_toggle: Some(match set { true => SetOrToggle::Set, false => SetOrToggle::Unset, } as i32), - })) - .unwrap(); + })) { + error!("{err}"); + } } /// Toggle this window to and from focused. @@ -415,12 +420,14 @@ impl WindowHandle { /// // be a focused window. /// window.get_focused()?.toggle_focused(); /// ``` + #[instrument] pub fn toggle_focused(&self) { - block_on_tokio(crate::window().set_focused(SetFocusedRequest { + if let Err(err) = block_on_tokio(crate::window().set_focused(SetFocusedRequest { window_id: Some(self.id), set_or_toggle: Some(SetOrToggle::Toggle as i32), - })) - .unwrap(); + })) { + error!("{err}"); + } } /// Move this window to the given `tag`. @@ -434,12 +441,14 @@ impl WindowHandle { /// // Move the focused window to tag "Code" on the focused output /// window.get_focused()?.move_to_tag(&tag.get("Code", None)?); /// ``` + #[instrument] pub fn move_to_tag(&self, tag: &TagHandle) { - block_on_tokio(crate::window().move_to_tag(MoveToTagRequest { + if let Err(err) = block_on_tokio(crate::window().move_to_tag(MoveToTagRequest { window_id: Some(self.id), tag_id: Some(tag.id), - })) - .unwrap(); + })) { + error!("{err}"); + } } /// Set or unset a tag on this window. @@ -453,16 +462,18 @@ impl WindowHandle { /// focused.set_tag(&tg, true); // `focused` now has tag "Potato" /// focused.set_tag(&tg, false); // `focused` no longer has tag "Potato" /// ``` + #[instrument] pub fn set_tag(&self, tag: &TagHandle, set: bool) { - block_on_tokio(crate::window().set_tag(SetTagRequest { + if let Err(err) = block_on_tokio(crate::window().set_tag(SetTagRequest { window_id: Some(self.id), tag_id: Some(tag.id), set_or_toggle: Some(match set { true => SetOrToggle::Set, false => SetOrToggle::Unset, } as i32), - })) - .unwrap(); + })) { + error!("{err}"); + } } /// Toggle a tag on this window. @@ -478,13 +489,15 @@ impl WindowHandle { /// focused.toggle_tag(&tg); // `focused` now has tag "Potato" /// focused.toggle_tag(&tg); // `focused` no longer has tag "Potato" /// ``` + #[instrument] pub fn toggle_tag(&self, tag: &TagHandle) { - block_on_tokio(crate::window().set_tag(SetTagRequest { + if let Err(err) = block_on_tokio(crate::window().set_tag(SetTagRequest { window_id: Some(self.id), tag_id: Some(tag.id), set_or_toggle: Some(SetOrToggle::Toggle as i32), - })) - .unwrap(); + })) { + error!("{err}"); + } } /// Raise this window. @@ -496,11 +509,13 @@ impl WindowHandle { /// ``` /// window.get_focused()?.raise(); /// ``` + #[instrument] pub fn raise(&self) { - block_on_tokio(crate::window().raise(RaiseRequest { + if let Err(err) = block_on_tokio(crate::window().raise(RaiseRequest { window_id: Some(self.id), - })) - .unwrap(); + })) { + error!("{err}"); + } } /// Get all properties of this window. diff --git a/snowcap b/snowcap index 35747e3..7baca8e 160000 --- a/snowcap +++ b/snowcap @@ -1 +1 @@ -Subproject commit 35747e3f798c0f604ef401f2b8fdc7e74b1f3d20 +Subproject commit 7baca8e4299780723f21ea696b19f8998207dc1f