api/rust: Add error handling

This commit is contained in:
Ottatop 2024-07-14 15:45:02 -05:00
parent 57dab51536
commit db3bfd9e5f
15 changed files with 339 additions and 198 deletions

12
Cargo.lock generated
View file

@ -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",

View file

@ -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`

View file

@ -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"]

View file

@ -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)*

View file

@ -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<KeybindInfo> = 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<Item = KeybindDescription> {
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}");
}
}
}

View file

@ -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<T> LayoutRequester<T> {
/// 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");
}
}
}

View file

@ -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<dyn std::error::Error>> {
.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<F: Future>(future: F) -> F::Output {
tokio::task::block_in_place(|| {

View file

@ -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<H: std::hash::Hasher>(&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<Option<i32>>, y: impl Into<Option<i32>>) {
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<Option<u32>>,
) {
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,

View file

@ -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};

View file

@ -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}");
}
}
}

View file

@ -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}");
}
}
}

View file

@ -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();
});

View file

@ -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<TagHandle> {
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<TagHandle> {
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<Item = TagHandle>) {
let tag_ids = tags.into_iter().map(|handle| handle.id).collect::<Vec<_>>();
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,

View file

@ -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::<Vec<_>>()
@ -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<H: std::hash::Hasher>(&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.

@ -1 +1 @@
Subproject commit 35747e3f798c0f604ef401f2b8fdc7e74b1f3d20
Subproject commit 7baca8e4299780723f21ea696b19f8998207dc1f