mirror of
https://github.com/pinnacle-comp/pinnacle.git
synced 2024-12-26 21:58:10 +01:00
Add basic API batching for Rust
Function coloring is fun
This commit is contained in:
parent
20af3a116c
commit
b8c5ec751b
5 changed files with 496 additions and 140 deletions
|
@ -21,15 +21,16 @@ use pinnacle_api_defs::pinnacle::{
|
|||
};
|
||||
use tonic::transport::Channel;
|
||||
|
||||
use crate::{block_on_tokio, tag::TagHandle};
|
||||
use crate::{block_on_tokio, tag::TagHandle, util::Batch};
|
||||
|
||||
/// A struct that allows you to get handles to connected outputs and set them up.
|
||||
///
|
||||
/// See [`OutputHandle`] for more information.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Output {
|
||||
channel: Channel,
|
||||
fut_sender: UnboundedSender<BoxFuture<'static, ()>>,
|
||||
output_client: OutputServiceClient<Channel>,
|
||||
tag_client: TagServiceClient<Channel>,
|
||||
}
|
||||
|
||||
impl Output {
|
||||
|
@ -38,19 +39,12 @@ impl Output {
|
|||
fut_sender: UnboundedSender<BoxFuture<'static, ()>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
channel,
|
||||
output_client: OutputServiceClient::new(channel.clone()),
|
||||
tag_client: TagServiceClient::new(channel),
|
||||
fut_sender,
|
||||
}
|
||||
}
|
||||
|
||||
fn create_output_client(&self) -> OutputServiceClient<Channel> {
|
||||
OutputServiceClient::new(self.channel.clone())
|
||||
}
|
||||
|
||||
fn create_tag_client(&self) -> TagServiceClient<Channel> {
|
||||
TagServiceClient::new(self.channel.clone())
|
||||
}
|
||||
|
||||
/// Get a handle to all connected outputs.
|
||||
///
|
||||
/// # Examples
|
||||
|
@ -59,15 +53,22 @@ impl Output {
|
|||
/// let outputs = output.get_all();
|
||||
/// ```
|
||||
pub fn get_all(&self) -> impl Iterator<Item = OutputHandle> {
|
||||
let mut client = self.create_output_client();
|
||||
let tag_client = self.create_tag_client();
|
||||
block_on_tokio(client.get(output::v0alpha1::GetRequest {}))
|
||||
block_on_tokio(self.get_all_async())
|
||||
}
|
||||
|
||||
/// The async version of [`Output::get_all`].
|
||||
pub async fn get_all_async(&self) -> impl Iterator<Item = OutputHandle> {
|
||||
let mut client = self.output_client.clone();
|
||||
let tag_client = self.tag_client.clone();
|
||||
client
|
||||
.get(output::v0alpha1::GetRequest {})
|
||||
.await
|
||||
.unwrap()
|
||||
.into_inner()
|
||||
.output_names
|
||||
.into_iter()
|
||||
.map(move |name| OutputHandle {
|
||||
client: client.clone(),
|
||||
output_client: client.clone(),
|
||||
tag_client: tag_client.clone(),
|
||||
name,
|
||||
})
|
||||
|
@ -84,8 +85,15 @@ impl Output {
|
|||
/// let op2 = output.get_by_name("HDMI-2")?;
|
||||
/// ```
|
||||
pub fn get_by_name(&self, name: impl Into<String>) -> Option<OutputHandle> {
|
||||
block_on_tokio(self.get_by_name_async(name))
|
||||
}
|
||||
|
||||
/// The async version of [`Output::get_by_name`].
|
||||
pub async fn get_by_name_async(&self, name: impl Into<String>) -> Option<OutputHandle> {
|
||||
let name: String = name.into();
|
||||
self.get_all().find(|output| output.name == name)
|
||||
self.get_all_async()
|
||||
.await
|
||||
.find(|output| output.name == name)
|
||||
}
|
||||
|
||||
/// Get a handle to the focused output.
|
||||
|
@ -102,6 +110,14 @@ impl Output {
|
|||
.find(|output| matches!(output.props().focused, Some(true)))
|
||||
}
|
||||
|
||||
/// The async version of [`Output::get_focused`].
|
||||
pub async fn get_focused_async(&self) -> Option<OutputHandle> {
|
||||
self.get_all_async().await.batch_find(
|
||||
|output| output.props_async().boxed(),
|
||||
|props| props.focused.is_some_and(|focused| focused),
|
||||
)
|
||||
}
|
||||
|
||||
/// Connect a closure to be run on all current and future outputs.
|
||||
///
|
||||
/// When called, `connect_for_all` will do two things:
|
||||
|
@ -126,8 +142,8 @@ impl Output {
|
|||
for_all(output);
|
||||
}
|
||||
|
||||
let mut client = self.create_output_client();
|
||||
let tag_client = self.create_tag_client();
|
||||
let mut client = self.output_client.clone();
|
||||
let tag_client = self.tag_client.clone();
|
||||
|
||||
self.fut_sender
|
||||
.unbounded_send(
|
||||
|
@ -144,7 +160,7 @@ impl Output {
|
|||
};
|
||||
|
||||
let output = OutputHandle {
|
||||
client: client.clone(),
|
||||
output_client: client.clone(),
|
||||
tag_client: tag_client.clone(),
|
||||
name: output_name,
|
||||
};
|
||||
|
@ -163,7 +179,7 @@ impl Output {
|
|||
/// This allows you to manipulate outputs and get their properties.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct OutputHandle {
|
||||
pub(crate) client: OutputServiceClient<Channel>,
|
||||
pub(crate) output_client: OutputServiceClient<Channel>,
|
||||
pub(crate) tag_client: TagServiceClient<Channel>,
|
||||
pub(crate) name: String,
|
||||
}
|
||||
|
@ -245,7 +261,7 @@ impl OutputHandle {
|
|||
/// // ^x=1920
|
||||
/// ```
|
||||
pub fn set_location(&self, x: impl Into<Option<i32>>, y: impl Into<Option<i32>>) {
|
||||
let mut client = self.client.clone();
|
||||
let mut client = self.output_client.clone();
|
||||
block_on_tokio(client.set_location(SetLocationRequest {
|
||||
output_name: Some(self.name.clone()),
|
||||
x: x.into(),
|
||||
|
@ -377,14 +393,19 @@ impl OutputHandle {
|
|||
/// } = output.get_focused()?.props();
|
||||
/// ```
|
||||
pub fn props(&self) -> OutputProperties {
|
||||
let mut client = self.client.clone();
|
||||
let response = block_on_tokio(client.get_properties(
|
||||
output::v0alpha1::GetPropertiesRequest {
|
||||
block_on_tokio(self.props_async())
|
||||
}
|
||||
|
||||
/// The async version of [`OutputHandle::props`].
|
||||
pub async fn props_async(&self) -> OutputProperties {
|
||||
let mut client = self.output_client.clone();
|
||||
let response = client
|
||||
.get_properties(output::v0alpha1::GetPropertiesRequest {
|
||||
output_name: Some(self.name.clone()),
|
||||
},
|
||||
))
|
||||
.unwrap()
|
||||
.into_inner();
|
||||
})
|
||||
.await
|
||||
.unwrap()
|
||||
.into_inner();
|
||||
|
||||
OutputProperties {
|
||||
make: response.make,
|
||||
|
@ -401,8 +422,8 @@ impl OutputHandle {
|
|||
.tag_ids
|
||||
.into_iter()
|
||||
.map(|id| TagHandle {
|
||||
client: self.tag_client.clone(),
|
||||
output_client: self.client.clone(),
|
||||
tag_client: self.tag_client.clone(),
|
||||
output_client: self.output_client.clone(),
|
||||
id,
|
||||
})
|
||||
.collect(),
|
||||
|
@ -418,6 +439,11 @@ impl OutputHandle {
|
|||
self.props().make
|
||||
}
|
||||
|
||||
/// The async version of [`OutputHandle::make`].
|
||||
pub async fn make_async(&self) -> Option<String> {
|
||||
self.props_async().await.make
|
||||
}
|
||||
|
||||
/// Get this output's model.
|
||||
///
|
||||
/// Shorthand for `self.props().make`.
|
||||
|
@ -425,6 +451,11 @@ impl OutputHandle {
|
|||
self.props().model
|
||||
}
|
||||
|
||||
/// The async version of [`OutputHandle::model`].
|
||||
pub async fn model_async(&self) -> Option<String> {
|
||||
self.props_async().await.model
|
||||
}
|
||||
|
||||
/// Get this output's x position in the global space.
|
||||
///
|
||||
/// Shorthand for `self.props().x`.
|
||||
|
@ -432,6 +463,11 @@ impl OutputHandle {
|
|||
self.props().x
|
||||
}
|
||||
|
||||
/// The async version of [`OutputHandle::x`].
|
||||
pub async fn x_async(&self) -> Option<i32> {
|
||||
self.props_async().await.x
|
||||
}
|
||||
|
||||
/// Get this output's y position in the global space.
|
||||
///
|
||||
/// Shorthand for `self.props().y`.
|
||||
|
@ -439,6 +475,11 @@ impl OutputHandle {
|
|||
self.props().y
|
||||
}
|
||||
|
||||
/// The async version of [`OutputHandle::y`].
|
||||
pub async fn y_async(&self) -> Option<i32> {
|
||||
self.props_async().await.y
|
||||
}
|
||||
|
||||
/// Get this output's screen width in pixels.
|
||||
///
|
||||
/// Shorthand for `self.props().pixel_width`.
|
||||
|
@ -446,6 +487,11 @@ impl OutputHandle {
|
|||
self.props().pixel_width
|
||||
}
|
||||
|
||||
/// The async version of [`OutputHandle::pixel_width`].
|
||||
pub async fn pixel_width_async(&self) -> Option<u32> {
|
||||
self.props_async().await.pixel_width
|
||||
}
|
||||
|
||||
/// Get this output's screen height in pixels.
|
||||
///
|
||||
/// Shorthand for `self.props().pixel_height`.
|
||||
|
@ -453,6 +499,11 @@ impl OutputHandle {
|
|||
self.props().pixel_height
|
||||
}
|
||||
|
||||
/// The async version of [`OutputHandle::pixel_height`].
|
||||
pub async fn pixel_height_async(&self) -> Option<u32> {
|
||||
self.props_async().await.pixel_height
|
||||
}
|
||||
|
||||
/// Get this output's refresh rate in millihertz.
|
||||
///
|
||||
/// For example, 144Hz will be returned as 144000.
|
||||
|
@ -462,6 +513,11 @@ impl OutputHandle {
|
|||
self.props().refresh_rate
|
||||
}
|
||||
|
||||
/// The async version of [`OutputHandle::refresh_rate`].
|
||||
pub async fn refresh_rate_async(&self) -> Option<u32> {
|
||||
self.props_async().await.refresh_rate
|
||||
}
|
||||
|
||||
/// Get this output's physical width in millimeters.
|
||||
///
|
||||
/// Shorthand for `self.props().physical_width`.
|
||||
|
@ -469,6 +525,11 @@ impl OutputHandle {
|
|||
self.props().physical_width
|
||||
}
|
||||
|
||||
/// The async version of [`OutputHandle::physical_width`].
|
||||
pub async fn physical_width_async(&self) -> Option<u32> {
|
||||
self.props_async().await.physical_width
|
||||
}
|
||||
|
||||
/// Get this output's physical height in millimeters.
|
||||
///
|
||||
/// Shorthand for `self.props().physical_height`.
|
||||
|
@ -476,6 +537,11 @@ impl OutputHandle {
|
|||
self.props().physical_height
|
||||
}
|
||||
|
||||
/// The async version of [`OutputHandle::physical_height`].
|
||||
pub async fn physical_height_async(&self) -> Option<u32> {
|
||||
self.props_async().await.physical_height
|
||||
}
|
||||
|
||||
/// Get whether this output is focused or not.
|
||||
///
|
||||
/// This is currently implemented as the output with the most recent pointer motion.
|
||||
|
@ -485,6 +551,11 @@ impl OutputHandle {
|
|||
self.props().focused
|
||||
}
|
||||
|
||||
/// The async version of [`OutputHandle::focused`].
|
||||
pub async fn focused_async(&self) -> Option<bool> {
|
||||
self.props_async().await.focused
|
||||
}
|
||||
|
||||
/// Get the tags this output has.
|
||||
///
|
||||
/// Shorthand for `self.props().tags`
|
||||
|
@ -492,6 +563,11 @@ impl OutputHandle {
|
|||
self.props().tags
|
||||
}
|
||||
|
||||
/// The async version of [`OutputHandle::tags`].
|
||||
pub async fn tags_async(&self) -> Vec<TagHandle> {
|
||||
self.props_async().await.tags
|
||||
}
|
||||
|
||||
/// Get this output's unique name (the name of its connector).
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
|
@ -499,7 +575,7 @@ impl OutputHandle {
|
|||
}
|
||||
|
||||
/// The properties of an output.
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
|
||||
pub struct OutputProperties {
|
||||
/// The make of the output
|
||||
pub make: Option<String>,
|
||||
|
|
|
@ -24,6 +24,7 @@ pub struct Process {
|
|||
}
|
||||
|
||||
/// Optional callbacks to be run when a spawned process prints to stdout or stderr or exits.
|
||||
#[derive(Default)]
|
||||
pub struct SpawnCallbacks {
|
||||
/// A callback that will be run when a process prints to stdout with a line
|
||||
pub stdout: Option<Box<dyn FnMut(String) + Send>>,
|
||||
|
|
|
@ -34,7 +34,7 @@ use std::{
|
|||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use futures::{channel::mpsc::UnboundedSender, future::BoxFuture};
|
||||
use futures::{channel::mpsc::UnboundedSender, future::BoxFuture, FutureExt};
|
||||
use num_enum::TryFromPrimitive;
|
||||
use pinnacle_api_defs::pinnacle::{
|
||||
output::v0alpha1::output_service_client::OutputServiceClient,
|
||||
|
@ -51,6 +51,7 @@ use tonic::transport::Channel;
|
|||
use crate::{
|
||||
block_on_tokio,
|
||||
output::{Output, OutputHandle},
|
||||
util::Batch,
|
||||
};
|
||||
|
||||
/// A struct that allows you to add and remove tags and get [`TagHandle`]s.
|
||||
|
@ -58,6 +59,8 @@ use crate::{
|
|||
pub struct Tag {
|
||||
channel: Channel,
|
||||
fut_sender: UnboundedSender<BoxFuture<'static, ()>>,
|
||||
tag_client: TagServiceClient<Channel>,
|
||||
output_client: OutputServiceClient<Channel>,
|
||||
}
|
||||
|
||||
impl Tag {
|
||||
|
@ -66,19 +69,13 @@ impl Tag {
|
|||
fut_sender: UnboundedSender<BoxFuture<'static, ()>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
tag_client: TagServiceClient::new(channel.clone()),
|
||||
output_client: OutputServiceClient::new(channel.clone()),
|
||||
channel,
|
||||
fut_sender,
|
||||
}
|
||||
}
|
||||
|
||||
fn create_tag_client(&self) -> TagServiceClient<Channel> {
|
||||
TagServiceClient::new(self.channel.clone())
|
||||
}
|
||||
|
||||
fn create_output_client(&self) -> OutputServiceClient<Channel> {
|
||||
OutputServiceClient::new(self.channel.clone())
|
||||
}
|
||||
|
||||
/// Add tags to the specified output.
|
||||
///
|
||||
/// This will add tags with the given names to `output` and return [`TagHandle`]s to all of
|
||||
|
@ -97,20 +94,31 @@ impl Tag {
|
|||
output: &OutputHandle,
|
||||
tag_names: impl IntoIterator<Item = impl Into<String>>,
|
||||
) -> impl Iterator<Item = TagHandle> {
|
||||
let mut client = self.create_tag_client();
|
||||
let output_client = self.create_output_client();
|
||||
block_on_tokio(self.add_async(output, tag_names))
|
||||
}
|
||||
|
||||
/// The async version of [`Tag::add`].
|
||||
pub async fn add_async(
|
||||
&self,
|
||||
output: &OutputHandle,
|
||||
tag_names: impl IntoIterator<Item = impl Into<String>>,
|
||||
) -> impl Iterator<Item = TagHandle> {
|
||||
let mut client = self.tag_client.clone();
|
||||
let output_client = self.output_client.clone();
|
||||
|
||||
let tag_names = tag_names.into_iter().map(Into::into).collect();
|
||||
|
||||
let response = block_on_tokio(client.add(AddRequest {
|
||||
output_name: Some(output.name.clone()),
|
||||
tag_names,
|
||||
}))
|
||||
.unwrap()
|
||||
.into_inner();
|
||||
let response = client
|
||||
.add(AddRequest {
|
||||
output_name: Some(output.name.clone()),
|
||||
tag_names,
|
||||
})
|
||||
.await
|
||||
.unwrap()
|
||||
.into_inner();
|
||||
|
||||
response.tag_ids.into_iter().map(move |id| TagHandle {
|
||||
client: client.clone(),
|
||||
tag_client: client.clone(),
|
||||
output_client: output_client.clone(),
|
||||
id,
|
||||
})
|
||||
|
@ -124,15 +132,22 @@ impl Tag {
|
|||
/// let all_tags = tag.get_all();
|
||||
/// ```
|
||||
pub fn get_all(&self) -> impl Iterator<Item = TagHandle> {
|
||||
let mut client = self.create_tag_client();
|
||||
let output_client = self.create_output_client();
|
||||
block_on_tokio(self.get_all_async())
|
||||
}
|
||||
|
||||
let response = block_on_tokio(client.get(tag::v0alpha1::GetRequest {}))
|
||||
/// The async version of [`Tag::get_all`].
|
||||
pub async fn get_all_async(&self) -> impl Iterator<Item = TagHandle> {
|
||||
let mut client = self.tag_client.clone();
|
||||
let output_client = self.output_client.clone();
|
||||
|
||||
let response = client
|
||||
.get(tag::v0alpha1::GetRequest {})
|
||||
.await
|
||||
.unwrap()
|
||||
.into_inner();
|
||||
|
||||
response.tag_ids.into_iter().map(move |id| TagHandle {
|
||||
client: client.clone(),
|
||||
tag_client: client.clone(),
|
||||
output_client: output_client.clone(),
|
||||
id,
|
||||
})
|
||||
|
@ -149,18 +164,20 @@ impl Tag {
|
|||
/// let tg = tag.get("Thing");
|
||||
/// ```
|
||||
pub fn get(&self, name: impl Into<String>) -> Option<TagHandle> {
|
||||
block_on_tokio(self.get_async(name))
|
||||
}
|
||||
|
||||
/// The async version of [`Tag::get`].
|
||||
pub async fn get_async(&self, name: impl Into<String>) -> Option<TagHandle> {
|
||||
let name = name.into();
|
||||
let output_module = Output::new(self.channel.clone(), self.fut_sender.clone());
|
||||
let focused_output = output_module.get_focused();
|
||||
|
||||
self.get_all().find(|tag| {
|
||||
let props = tag.props();
|
||||
|
||||
let same_tag_name = props.name.as_ref() == Some(&name);
|
||||
let same_output = props.output.is_some_and(|op| Some(op) == focused_output);
|
||||
|
||||
same_tag_name && same_output
|
||||
})
|
||||
if let Some(output) = focused_output {
|
||||
self.get_on_output_async(name, &output).await
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a handle to the first tag with the given name on the specified output.
|
||||
|
@ -177,17 +194,27 @@ impl Tag {
|
|||
&self,
|
||||
name: impl Into<String>,
|
||||
output: &OutputHandle,
|
||||
) -> Option<TagHandle> {
|
||||
block_on_tokio(self.get_on_output_async(name, output))
|
||||
}
|
||||
|
||||
/// The async version of [`Tag::get_on_output`].
|
||||
pub async fn get_on_output_async(
|
||||
&self,
|
||||
name: impl Into<String>,
|
||||
output: &OutputHandle,
|
||||
) -> Option<TagHandle> {
|
||||
let name = name.into();
|
||||
|
||||
self.get_all().find(|tag| {
|
||||
let props = tag.props();
|
||||
self.get_all_async().await.batch_find(
|
||||
|tag| tag.props_async().boxed(),
|
||||
|props| {
|
||||
let same_tag_name = props.name.as_ref() == Some(&name);
|
||||
let same_output = props.output.as_ref().is_some_and(|op| op == output);
|
||||
|
||||
let same_tag_name = props.name.as_ref() == Some(&name);
|
||||
let same_output = props.output.is_some_and(|op| &op == output);
|
||||
|
||||
same_tag_name && same_output
|
||||
})
|
||||
same_tag_name && same_output
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Remove the given tags from their outputs.
|
||||
|
@ -202,7 +229,7 @@ impl Tag {
|
|||
pub fn remove(&self, tags: impl IntoIterator<Item = TagHandle>) {
|
||||
let tag_ids = tags.into_iter().map(|handle| handle.id).collect::<Vec<_>>();
|
||||
|
||||
let mut client = self.create_tag_client();
|
||||
let mut client = self.tag_client.clone();
|
||||
|
||||
block_on_tokio(client.remove(RemoveRequest { tag_ids })).unwrap();
|
||||
}
|
||||
|
@ -333,9 +360,9 @@ pub struct LayoutCycler {
|
|||
/// This handle allows you to do things like switch to tags and get their properties.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TagHandle {
|
||||
pub(crate) client: TagServiceClient<Channel>,
|
||||
pub(crate) output_client: OutputServiceClient<Channel>,
|
||||
pub(crate) id: u32,
|
||||
pub(crate) tag_client: TagServiceClient<Channel>,
|
||||
pub(crate) output_client: OutputServiceClient<Channel>,
|
||||
}
|
||||
|
||||
impl PartialEq for TagHandle {
|
||||
|
@ -388,7 +415,7 @@ impl TagHandle {
|
|||
/// tag.get("3")?.switch_to(); // Displays Steam
|
||||
/// ```
|
||||
pub fn switch_to(&self) {
|
||||
let mut client = self.client.clone();
|
||||
let mut client = self.tag_client.clone();
|
||||
block_on_tokio(client.switch_to(SwitchToRequest {
|
||||
tag_id: Some(self.id),
|
||||
}))
|
||||
|
@ -414,7 +441,7 @@ impl TagHandle {
|
|||
/// tag.get("2")?.set_active(false); // Displays Steam
|
||||
/// ```
|
||||
pub fn set_active(&self, set: bool) {
|
||||
let mut client = self.client.clone();
|
||||
let mut client = self.tag_client.clone();
|
||||
block_on_tokio(client.set_active(SetActiveRequest {
|
||||
tag_id: Some(self.id),
|
||||
set_or_toggle: Some(tag::v0alpha1::set_active_request::SetOrToggle::Set(set)),
|
||||
|
@ -442,7 +469,7 @@ impl TagHandle {
|
|||
/// tag.get("2")?.toggle(); // Displays nothing
|
||||
/// ```
|
||||
pub fn toggle_active(&self) {
|
||||
let mut client = self.client.clone();
|
||||
let mut client = self.tag_client.clone();
|
||||
block_on_tokio(client.set_active(SetActiveRequest {
|
||||
tag_id: Some(self.id),
|
||||
set_or_toggle: Some(tag::v0alpha1::set_active_request::SetOrToggle::Toggle(())),
|
||||
|
@ -463,8 +490,9 @@ impl TagHandle {
|
|||
/// tags[3].remove();
|
||||
/// // "DP-1" now only has tags "1" and "Buckle"
|
||||
/// ```
|
||||
pub fn remove(mut self) {
|
||||
block_on_tokio(self.client.remove(RemoveRequest {
|
||||
pub fn remove(&self) {
|
||||
let mut tag_client = self.tag_client.clone();
|
||||
block_on_tokio(tag_client.remove(RemoveRequest {
|
||||
tag_ids: vec![self.id],
|
||||
}))
|
||||
.unwrap();
|
||||
|
@ -487,7 +515,7 @@ impl TagHandle {
|
|||
/// tag.get("1", None)?.set_layout(Layout::CornerTopLeft);
|
||||
/// ```
|
||||
pub fn set_layout(&self, layout: Layout) {
|
||||
let mut client = self.client.clone();
|
||||
let mut client = self.tag_client.clone();
|
||||
block_on_tokio(client.set_layout(SetLayoutRequest {
|
||||
tag_id: Some(self.id),
|
||||
layout: Some(layout as i32),
|
||||
|
@ -509,20 +537,27 @@ impl TagHandle {
|
|||
/// } = tag.get("1", None)?.props();
|
||||
/// ```
|
||||
pub fn props(&self) -> TagProperties {
|
||||
let mut client = self.client.clone();
|
||||
block_on_tokio(self.props_async())
|
||||
}
|
||||
|
||||
/// The async version of [`TagHandle::props`].
|
||||
pub async fn props_async(&self) -> TagProperties {
|
||||
let mut client = self.tag_client.clone();
|
||||
let output_client = self.output_client.clone();
|
||||
|
||||
let response = block_on_tokio(client.get_properties(tag::v0alpha1::GetPropertiesRequest {
|
||||
tag_id: Some(self.id),
|
||||
}))
|
||||
.unwrap()
|
||||
.into_inner();
|
||||
let response = client
|
||||
.get_properties(tag::v0alpha1::GetPropertiesRequest {
|
||||
tag_id: Some(self.id),
|
||||
})
|
||||
.await
|
||||
.unwrap()
|
||||
.into_inner();
|
||||
|
||||
TagProperties {
|
||||
active: response.active,
|
||||
name: response.name,
|
||||
output: response.output_name.map(|name| OutputHandle {
|
||||
client: output_client,
|
||||
output_client,
|
||||
tag_client: client,
|
||||
name,
|
||||
}),
|
||||
|
@ -536,6 +571,11 @@ impl TagHandle {
|
|||
self.props().active
|
||||
}
|
||||
|
||||
/// The async version of [`TagHandle::active`].
|
||||
pub async fn active_async(&self) -> Option<bool> {
|
||||
self.props_async().await.active
|
||||
}
|
||||
|
||||
/// Get this tag's name.
|
||||
///
|
||||
/// Shorthand for `self.props().name`.
|
||||
|
@ -543,15 +583,26 @@ impl TagHandle {
|
|||
self.props().name
|
||||
}
|
||||
|
||||
/// The async version of [`TagHandle::name`].
|
||||
pub async fn name_async(&self) -> Option<String> {
|
||||
self.props_async().await.name
|
||||
}
|
||||
|
||||
/// Get a handle to the output this tag is on.
|
||||
///
|
||||
/// Shorthand for `self.props().output`.
|
||||
pub fn output(&self) -> Option<OutputHandle> {
|
||||
self.props().output
|
||||
}
|
||||
|
||||
/// The async version of [`TagHandle::output`].
|
||||
pub async fn output_async(&self) -> Option<OutputHandle> {
|
||||
self.props_async().await.output
|
||||
}
|
||||
}
|
||||
|
||||
/// Properties of a tag.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
|
||||
pub struct TagProperties {
|
||||
/// Whether the tag is active or not
|
||||
pub active: Option<bool>,
|
||||
|
|
|
@ -4,6 +4,15 @@
|
|||
|
||||
//! Utility types.
|
||||
|
||||
use std::pin::Pin;
|
||||
|
||||
use futures::{stream::FuturesOrdered, Future, StreamExt};
|
||||
|
||||
use crate::block_on_tokio;
|
||||
|
||||
pub use crate::batch_boxed;
|
||||
pub use crate::batch_boxed_async;
|
||||
|
||||
/// The size and location of something.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct Geometry {
|
||||
|
@ -16,3 +25,160 @@ pub struct Geometry {
|
|||
/// The height
|
||||
pub height: u32,
|
||||
}
|
||||
|
||||
/// Batch a set of requests that will be sent ot the compositor all at once.
|
||||
///
|
||||
/// # Rationale
|
||||
///
|
||||
/// Normally, all API calls are blocking. For example, calling [`Window::get_all`][crate::window::Window::get_all]
|
||||
/// then calling [`WindowHandle.props`][crate::window::WindowHandle::props]
|
||||
/// on each returned window handle will block after each `props` call waiting for the compositor to respond:
|
||||
///
|
||||
/// ```
|
||||
/// // This will block after each call to `window.props()`, meaning this all happens synchronously.
|
||||
/// // If the compositor is running slowly for whatever reason, this will take a long time to complete.
|
||||
/// let props = window.get_all()
|
||||
/// .map(|window| window.props())
|
||||
/// .collect::<Vec<_>>();
|
||||
/// ```
|
||||
///
|
||||
/// In order to mitigate this issue, you can batch up a set of API calls using this function.
|
||||
/// This will send all requests to the compositor at once without blocking, then wait for the compositor
|
||||
/// to respond.
|
||||
///
|
||||
/// You'll see that this function takes in an `IntoIterator` of `Future`s. As such,
|
||||
/// all API calls that return something have an async variant named `*_async` that returns a future.
|
||||
/// You must pass these futures into the batch function instead of their non-async counterparts.
|
||||
///
|
||||
/// # The `batch_boxed` macro
|
||||
/// The [`util`][crate::util] module also provides the [`batch_boxed`] macro.
|
||||
///
|
||||
/// The [`batch`] function only accepts one concrete type of future, meaning that you
|
||||
/// can only batch a collection of futures from one specific function or method.
|
||||
///
|
||||
/// As a convenience, `batch_boxed` accepts one or more different futures that return the same type.
|
||||
/// It will place provided futures in a `Pin<Box<_>>` to erase the types and pass them along to `batch`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use pinnacle_api::util::batch;
|
||||
/// use pinnacle_api::window::WindowProperties;
|
||||
///
|
||||
/// let props: Vec<WindowProperties> = batch(window.get_all().map(|window| window.props_async()));
|
||||
/// // Don't forget the `async` ^^^^^
|
||||
/// ```
|
||||
///
|
||||
pub fn batch<T>(requests: impl IntoIterator<Item = impl Future<Output = T>>) -> Vec<T> {
|
||||
let results = FuturesOrdered::from_iter(requests).collect::<Vec<_>>();
|
||||
block_on_tokio(results)
|
||||
}
|
||||
|
||||
/// The async version of [`batch`].
|
||||
///
|
||||
/// See [`batch`] for more information.
|
||||
pub async fn batch_async<T>(requests: impl IntoIterator<Item = impl Future<Output = T>>) -> Vec<T> {
|
||||
let results = FuturesOrdered::from_iter(requests).collect::<Vec<_>>();
|
||||
results.await
|
||||
}
|
||||
|
||||
/// A convenience macro to batch API calls in different concrete futures.
|
||||
///
|
||||
/// The [`batch`] function only accepts a collection of the same concrete future e.g.
|
||||
/// from a single async function or method.
|
||||
///
|
||||
/// To support different futures (that still return the same value), this macro will place provided
|
||||
/// futures in a `Pin<Box<_>>` to erase their type and pass them along to `batch`.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use pinnacle_api::util::batch_boxed;
|
||||
///
|
||||
/// let windows = window.get_all();
|
||||
/// let first = windows.next()?;
|
||||
/// let last = windows.last()?;
|
||||
///
|
||||
/// let classes: Vec<String> = batch_boxed![
|
||||
/// async {
|
||||
/// let mut class = first.class_async().await;
|
||||
/// class.unwrap_or("no class".to_string())
|
||||
/// },
|
||||
/// async {
|
||||
/// last.class_async().await
|
||||
/// },
|
||||
/// ];
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! batch_boxed {
|
||||
[ $first:expr, $($request:expr),* ] => {
|
||||
$crate::util::batch([
|
||||
::std::boxed::Box::pin($first) as ::std::pin::Pin<::std::boxed::Box<dyn std::future::Future<Output = _>>>,
|
||||
$(
|
||||
::std::boxed::Box::pin($request),
|
||||
)*
|
||||
])
|
||||
};
|
||||
}
|
||||
|
||||
/// The async version of [`batch_boxed`].
|
||||
///
|
||||
/// See [`batch_boxed`] for more information.
|
||||
#[macro_export]
|
||||
macro_rules! batch_boxed_async {
|
||||
[ $first:expr, $($request:expr),* ] => {
|
||||
$crate::util::batch_async([
|
||||
::std::boxed::Box::pin($first) as ::std::pin::Pin<::std::boxed::Box<dyn std::future::Future<Output = _>>>,
|
||||
$(
|
||||
::std::boxed::Box::pin($request),
|
||||
)*
|
||||
])
|
||||
};
|
||||
}
|
||||
|
||||
/// Methods for batch sending API requests to the compositor.
|
||||
pub trait Batch<I> {
|
||||
/// [`batch_map`][Batch::batch_map]s then finds the object for which `f` with the results
|
||||
/// returns `true`.
|
||||
fn batch_find<M, F, FutOp>(self, map_to_future: M, find: F) -> Option<I>
|
||||
where
|
||||
Self: Sized,
|
||||
M: for<'a> FnMut(&'a I) -> Pin<Box<dyn Future<Output = FutOp> + 'a>>,
|
||||
F: FnMut(&FutOp) -> bool;
|
||||
|
||||
/// Maps the collection to compositor requests, batching all calls.
|
||||
fn batch_map<F, FutOp>(self, map: F) -> impl Iterator<Item = FutOp>
|
||||
where
|
||||
Self: Sized,
|
||||
F: for<'a> FnMut(&'a I) -> Pin<Box<dyn Future<Output = FutOp> + 'a>>;
|
||||
}
|
||||
|
||||
impl<T: IntoIterator<Item = I>, I> Batch<I> for T {
|
||||
fn batch_find<M, F, FutOp>(self, map_to_future: M, mut find: F) -> Option<I>
|
||||
where
|
||||
Self: Sized,
|
||||
M: for<'a> FnMut(&'a I) -> Pin<Box<dyn Future<Output = FutOp> + 'a>>,
|
||||
F: FnMut(&FutOp) -> bool,
|
||||
{
|
||||
let items = self.into_iter().collect::<Vec<_>>();
|
||||
let futures = items.iter().map(map_to_future);
|
||||
let results = crate::util::batch(futures);
|
||||
|
||||
assert_eq!(items.len(), results.len());
|
||||
|
||||
items
|
||||
.into_iter()
|
||||
.zip(results)
|
||||
.find(|(_, fut_op)| find(fut_op))
|
||||
.map(|(item, _)| item)
|
||||
}
|
||||
|
||||
fn batch_map<F, FutOp>(self, map: F) -> impl Iterator<Item = FutOp>
|
||||
where
|
||||
Self: Sized,
|
||||
F: for<'a> FnMut(&'a I) -> Pin<Box<dyn Future<Output = FutOp> + 'a>>,
|
||||
{
|
||||
let items = self.into_iter().collect::<Vec<_>>();
|
||||
let futures = items.iter().map(map);
|
||||
crate::util::batch(futures).into_iter()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
//!
|
||||
//! This module also allows you to set window rules; see the [rules] module for more information.
|
||||
|
||||
use futures::FutureExt;
|
||||
use num_enum::TryFromPrimitive;
|
||||
use pinnacle_api_defs::pinnacle::{
|
||||
output::v0alpha1::output_service_client::OutputServiceClient,
|
||||
|
@ -30,7 +31,12 @@ use pinnacle_api_defs::pinnacle::{
|
|||
};
|
||||
use tonic::transport::Channel;
|
||||
|
||||
use crate::{block_on_tokio, input::MouseButton, tag::TagHandle, util::Geometry};
|
||||
use crate::{
|
||||
block_on_tokio,
|
||||
input::MouseButton,
|
||||
tag::TagHandle,
|
||||
util::{Batch, Geometry},
|
||||
};
|
||||
|
||||
use self::rules::{WindowRule, WindowRuleCondition};
|
||||
|
||||
|
@ -41,24 +47,18 @@ pub mod rules;
|
|||
/// See [`WindowHandle`] for more information.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Window {
|
||||
channel: Channel,
|
||||
window_client: WindowServiceClient<Channel>,
|
||||
tag_client: TagServiceClient<Channel>,
|
||||
output_client: OutputServiceClient<Channel>,
|
||||
}
|
||||
|
||||
impl Window {
|
||||
pub(crate) fn new(channel: Channel) -> Self {
|
||||
Self { channel }
|
||||
}
|
||||
|
||||
fn create_window_client(&self) -> WindowServiceClient<Channel> {
|
||||
WindowServiceClient::new(self.channel.clone())
|
||||
}
|
||||
|
||||
fn create_tag_client(&self) -> TagServiceClient<Channel> {
|
||||
TagServiceClient::new(self.channel.clone())
|
||||
}
|
||||
|
||||
fn create_output_client(&self) -> OutputServiceClient<Channel> {
|
||||
OutputServiceClient::new(self.channel.clone())
|
||||
Self {
|
||||
window_client: WindowServiceClient::new(channel.clone()),
|
||||
tag_client: TagServiceClient::new(channel.clone()),
|
||||
output_client: OutputServiceClient::new(channel),
|
||||
}
|
||||
}
|
||||
|
||||
/// Start moving the window with the mouse.
|
||||
|
@ -66,7 +66,7 @@ impl Window {
|
|||
/// This will begin moving the window under the pointer using the specified [`MouseButton`].
|
||||
/// The button must be held down at the time this method is called for the move to start.
|
||||
///
|
||||
/// This is intended to be used with [`Input::keybind`][crate::input::Input::keybind].
|
||||
/// This is intended to be used with [`Input::mousebind`][crate::input::Input::mousebind].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
@ -79,11 +79,12 @@ impl Window {
|
|||
/// });
|
||||
/// ```
|
||||
pub fn begin_move(&self, button: MouseButton) {
|
||||
let mut client = self.create_window_client();
|
||||
block_on_tokio(client.move_grab(MoveGrabRequest {
|
||||
let mut client = self.window_client.clone();
|
||||
if let Err(status) = block_on_tokio(client.move_grab(MoveGrabRequest {
|
||||
button: Some(button as u32),
|
||||
}))
|
||||
.unwrap();
|
||||
})) {
|
||||
eprintln!("ERROR: {status}");
|
||||
}
|
||||
}
|
||||
|
||||
/// Start resizing the window with the mouse.
|
||||
|
@ -91,7 +92,7 @@ impl Window {
|
|||
/// This will begin resizing the window under the pointer using the specified [`MouseButton`].
|
||||
/// The button must be held down at the time this method is called for the resize to start.
|
||||
///
|
||||
/// This is intended to be used with [`Input::keybind`][crate::input::Input::keybind].
|
||||
/// This is intended to be used with [`Input::mousebind`][crate::input::Input::mousebind].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
@ -104,7 +105,7 @@ impl Window {
|
|||
/// });
|
||||
/// ```
|
||||
pub fn begin_resize(&self, button: MouseButton) {
|
||||
let mut client = self.create_window_client();
|
||||
let mut client = self.window_client.clone();
|
||||
block_on_tokio(client.resize_grab(ResizeGrabRequest {
|
||||
button: Some(button as u32),
|
||||
}))
|
||||
|
@ -119,16 +120,23 @@ impl Window {
|
|||
/// let windows = window.get_all();
|
||||
/// ```
|
||||
pub fn get_all(&self) -> impl Iterator<Item = WindowHandle> {
|
||||
let mut client = self.create_window_client();
|
||||
let tag_client = self.create_tag_client();
|
||||
let output_client = self.create_output_client();
|
||||
block_on_tokio(client.get(GetRequest {}))
|
||||
block_on_tokio(self.get_all_async())
|
||||
}
|
||||
|
||||
/// The async version of [`Window::get_all`].
|
||||
pub async fn get_all_async(&self) -> impl Iterator<Item = WindowHandle> {
|
||||
let mut client = self.window_client.clone();
|
||||
let tag_client = self.tag_client.clone();
|
||||
let output_client = self.output_client.clone();
|
||||
client
|
||||
.get(GetRequest {})
|
||||
.await
|
||||
.unwrap()
|
||||
.into_inner()
|
||||
.window_ids
|
||||
.into_iter()
|
||||
.map(move |id| WindowHandle {
|
||||
client: client.clone(),
|
||||
window_client: client.clone(),
|
||||
id,
|
||||
tag_client: tag_client.clone(),
|
||||
output_client: output_client.clone(),
|
||||
|
@ -143,8 +151,15 @@ impl Window {
|
|||
/// let focused_window = window.get_focused()?;
|
||||
/// ```
|
||||
pub fn get_focused(&self) -> Option<WindowHandle> {
|
||||
self.get_all()
|
||||
.find(|window| matches!(window.props().focused, Some(true)))
|
||||
block_on_tokio(self.get_focused_async())
|
||||
}
|
||||
|
||||
/// The async version of [`Window::get_focused`].
|
||||
pub async fn get_focused_async(&self) -> Option<WindowHandle> {
|
||||
self.get_all_async().await.batch_find(
|
||||
|win| win.focused_async().boxed(),
|
||||
|focused| focused.is_some_and(|focused| focused),
|
||||
)
|
||||
}
|
||||
|
||||
/// Add a window rule.
|
||||
|
@ -154,7 +169,7 @@ impl Window {
|
|||
///
|
||||
/// TODO:
|
||||
pub fn add_window_rule(&self, cond: WindowRuleCondition, rule: WindowRule) {
|
||||
let mut client = self.create_window_client();
|
||||
let mut client = self.window_client.clone();
|
||||
|
||||
block_on_tokio(client.add_window_rule(AddWindowRuleRequest {
|
||||
cond: Some(cond.0),
|
||||
|
@ -169,10 +184,10 @@ impl Window {
|
|||
/// This allows you to manipulate the window and get its properties.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct WindowHandle {
|
||||
pub(crate) client: WindowServiceClient<Channel>,
|
||||
pub(crate) id: u32,
|
||||
pub(crate) tag_client: TagServiceClient<Channel>,
|
||||
pub(crate) output_client: OutputServiceClient<Channel>,
|
||||
id: u32,
|
||||
window_client: WindowServiceClient<Channel>,
|
||||
tag_client: TagServiceClient<Channel>,
|
||||
output_client: OutputServiceClient<Channel>,
|
||||
}
|
||||
|
||||
impl PartialEq for WindowHandle {
|
||||
|
@ -202,7 +217,7 @@ pub enum FullscreenOrMaximized {
|
|||
}
|
||||
|
||||
/// Properties of a window.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
|
||||
pub struct WindowProperties {
|
||||
/// The location and size of the window
|
||||
pub geometry: Option<Geometry>,
|
||||
|
@ -215,7 +230,7 @@ pub struct WindowProperties {
|
|||
/// Whether the window is floating or not
|
||||
///
|
||||
/// Note that a window can still be floating even if it's fullscreen or maximized; those two
|
||||
/// state will just override the floating state.
|
||||
/// states will just override the floating state.
|
||||
pub floating: Option<bool>,
|
||||
/// Whether the window is fullscreen, maximized, or neither
|
||||
pub fullscreen_or_maximized: Option<FullscreenOrMaximized>,
|
||||
|
@ -234,8 +249,9 @@ impl WindowHandle {
|
|||
/// // Close the focused window
|
||||
/// window.get_focused()?.close()
|
||||
/// ```
|
||||
pub fn close(mut self) {
|
||||
block_on_tokio(self.client.close(CloseRequest {
|
||||
pub fn close(&self) {
|
||||
let mut window_client = self.window_client.clone();
|
||||
block_on_tokio(window_client.close(CloseRequest {
|
||||
window_id: Some(self.id),
|
||||
}))
|
||||
.unwrap();
|
||||
|
@ -252,7 +268,7 @@ impl WindowHandle {
|
|||
/// window.get_focused()?.set_fullscreen(true);
|
||||
/// ```
|
||||
pub fn set_fullscreen(&self, set: bool) {
|
||||
let mut client = self.client.clone();
|
||||
let mut client = self.window_client.clone();
|
||||
block_on_tokio(client.set_fullscreen(SetFullscreenRequest {
|
||||
window_id: Some(self.id),
|
||||
set_or_toggle: Some(window::v0alpha1::set_fullscreen_request::SetOrToggle::Set(
|
||||
|
@ -273,7 +289,7 @@ impl WindowHandle {
|
|||
/// window.get_focused()?.toggle_fullscreen();
|
||||
/// ```
|
||||
pub fn toggle_fullscreen(&self) {
|
||||
let mut client = self.client.clone();
|
||||
let mut client = self.window_client.clone();
|
||||
block_on_tokio(client.set_fullscreen(SetFullscreenRequest {
|
||||
window_id: Some(self.id),
|
||||
set_or_toggle: Some(window::v0alpha1::set_fullscreen_request::SetOrToggle::Toggle(())),
|
||||
|
@ -292,7 +308,7 @@ impl WindowHandle {
|
|||
/// window.get_focused()?.set_maximized(true);
|
||||
/// ```
|
||||
pub fn set_maximized(&self, set: bool) {
|
||||
let mut client = self.client.clone();
|
||||
let mut client = self.window_client.clone();
|
||||
block_on_tokio(client.set_maximized(SetMaximizedRequest {
|
||||
window_id: Some(self.id),
|
||||
set_or_toggle: Some(window::v0alpha1::set_maximized_request::SetOrToggle::Set(
|
||||
|
@ -304,7 +320,7 @@ impl WindowHandle {
|
|||
|
||||
/// Toggle this window between maximized and not.
|
||||
///
|
||||
/// If it is fullscreen, setting it to maximized will remove the fullscreen state.
|
||||
/// If it is fullscreen, toggling it to maximized will remove the fullscreen state.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
@ -313,7 +329,7 @@ impl WindowHandle {
|
|||
/// window.get_focused()?.toggle_maximized();
|
||||
/// ```
|
||||
pub fn toggle_maximized(&self) {
|
||||
let mut client = self.client.clone();
|
||||
let mut client = self.window_client.clone();
|
||||
block_on_tokio(client.set_maximized(SetMaximizedRequest {
|
||||
window_id: Some(self.id),
|
||||
set_or_toggle: Some(window::v0alpha1::set_maximized_request::SetOrToggle::Toggle(())),
|
||||
|
@ -335,7 +351,7 @@ impl WindowHandle {
|
|||
/// window.get_focused()?.set_floating(true);
|
||||
/// ```
|
||||
pub fn set_floating(&self, set: bool) {
|
||||
let mut client = self.client.clone();
|
||||
let mut client = self.window_client.clone();
|
||||
block_on_tokio(client.set_floating(SetFloatingRequest {
|
||||
window_id: Some(self.id),
|
||||
set_or_toggle: Some(window::v0alpha1::set_floating_request::SetOrToggle::Set(
|
||||
|
@ -359,7 +375,7 @@ impl WindowHandle {
|
|||
/// window.get_focused()?.toggle_floating();
|
||||
/// ```
|
||||
pub fn toggle_floating(&self) {
|
||||
let mut client = self.client.clone();
|
||||
let mut client = self.window_client.clone();
|
||||
block_on_tokio(client.set_floating(SetFloatingRequest {
|
||||
window_id: Some(self.id),
|
||||
set_or_toggle: Some(window::v0alpha1::set_floating_request::SetOrToggle::Toggle(
|
||||
|
@ -381,7 +397,7 @@ impl WindowHandle {
|
|||
/// window.get_focused()?.move_to_tag(&tag.get("Code", None)?);
|
||||
/// ```
|
||||
pub fn move_to_tag(&self, tag: &TagHandle) {
|
||||
let mut client = self.client.clone();
|
||||
let mut client = self.window_client.clone();
|
||||
|
||||
block_on_tokio(client.move_to_tag(MoveToTagRequest {
|
||||
window_id: Some(self.id),
|
||||
|
@ -402,7 +418,7 @@ impl WindowHandle {
|
|||
/// focused.set_tag(&tg, false); // `focused` no longer has tag "Potato"
|
||||
/// ```
|
||||
pub fn set_tag(&self, tag: &TagHandle, set: bool) {
|
||||
let mut client = self.client.clone();
|
||||
let mut client = self.window_client.clone();
|
||||
|
||||
block_on_tokio(client.set_tag(SetTagRequest {
|
||||
window_id: Some(self.id),
|
||||
|
@ -426,7 +442,7 @@ impl WindowHandle {
|
|||
/// focused.toggle_tag(&tg); // `focused` no longer has tag "Potato"
|
||||
/// ```
|
||||
pub fn toggle_tag(&self, tag: &TagHandle) {
|
||||
let mut client = self.client.clone();
|
||||
let mut client = self.window_client.clone();
|
||||
|
||||
block_on_tokio(client.set_tag(SetTagRequest {
|
||||
window_id: Some(self.id),
|
||||
|
@ -454,15 +470,26 @@ impl WindowHandle {
|
|||
/// } = window.get_focused()?.props();
|
||||
/// ```
|
||||
pub fn props(&self) -> WindowProperties {
|
||||
let mut client = self.client.clone();
|
||||
block_on_tokio(self.props_async())
|
||||
}
|
||||
|
||||
/// The async version of [`props`][Self::props].
|
||||
pub async fn props_async(&self) -> WindowProperties {
|
||||
let mut client = self.window_client.clone();
|
||||
let tag_client = self.tag_client.clone();
|
||||
let response = block_on_tokio(client.get_properties(
|
||||
window::v0alpha1::GetPropertiesRequest {
|
||||
|
||||
let response = match client
|
||||
.get_properties(window::v0alpha1::GetPropertiesRequest {
|
||||
window_id: Some(self.id),
|
||||
},
|
||||
))
|
||||
.unwrap()
|
||||
.into_inner();
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(response) => response.into_inner(),
|
||||
Err(status) => {
|
||||
eprintln!("ERROR: {status}");
|
||||
return WindowProperties::default();
|
||||
}
|
||||
};
|
||||
|
||||
let fullscreen_or_maximized = response
|
||||
.fullscreen_or_maximized
|
||||
|
@ -488,7 +515,7 @@ impl WindowHandle {
|
|||
.tag_ids
|
||||
.into_iter()
|
||||
.map(|id| TagHandle {
|
||||
client: tag_client.clone(),
|
||||
tag_client: tag_client.clone(),
|
||||
output_client: self.output_client.clone(),
|
||||
id,
|
||||
})
|
||||
|
@ -503,6 +530,11 @@ impl WindowHandle {
|
|||
self.props().geometry
|
||||
}
|
||||
|
||||
/// The async version of [`geometry`][Self::geometry].
|
||||
pub async fn geometry_async(&self) -> Option<Geometry> {
|
||||
self.props_async().await.geometry
|
||||
}
|
||||
|
||||
/// Get this window's class.
|
||||
///
|
||||
/// Shorthand for `self.props().class`.
|
||||
|
@ -510,6 +542,11 @@ impl WindowHandle {
|
|||
self.props().class
|
||||
}
|
||||
|
||||
/// The async version of [`class`][Self::class].
|
||||
pub async fn class_async(&self) -> Option<String> {
|
||||
self.props_async().await.class
|
||||
}
|
||||
|
||||
/// Get this window's title.
|
||||
///
|
||||
/// Shorthand for `self.props().title`.
|
||||
|
@ -517,6 +554,11 @@ impl WindowHandle {
|
|||
self.props().title
|
||||
}
|
||||
|
||||
/// The async version of [`title`][Self::title].
|
||||
pub async fn title_async(&self) -> Option<String> {
|
||||
self.props_async().await.title
|
||||
}
|
||||
|
||||
/// Get whether or not this window is focused.
|
||||
///
|
||||
/// Shorthand for `self.props().focused`.
|
||||
|
@ -524,6 +566,11 @@ impl WindowHandle {
|
|||
self.props().focused
|
||||
}
|
||||
|
||||
/// The async version of [`focused`][Self::focused].
|
||||
pub async fn focused_async(&self) -> Option<bool> {
|
||||
self.props_async().await.focused
|
||||
}
|
||||
|
||||
/// Get whether or not this window is floating.
|
||||
///
|
||||
/// Shorthand for `self.props().floating`.
|
||||
|
@ -531,6 +578,11 @@ impl WindowHandle {
|
|||
self.props().floating
|
||||
}
|
||||
|
||||
/// The async version of [`floating`][Self::floating]
|
||||
pub async fn floating_async(&self) -> Option<bool> {
|
||||
self.props_async().await.floating
|
||||
}
|
||||
|
||||
/// Get whether this window is fullscreen, maximized, or neither.
|
||||
///
|
||||
/// Shorthand for `self.props().fullscreen_or_maximized`.
|
||||
|
@ -538,10 +590,20 @@ impl WindowHandle {
|
|||
self.props().fullscreen_or_maximized
|
||||
}
|
||||
|
||||
/// The async version of [`fullscreen_or_maximized`][Self::fullscreen_or_maximized].
|
||||
pub async fn fullscreen_or_maximized_async(&self) -> Option<FullscreenOrMaximized> {
|
||||
self.props_async().await.fullscreen_or_maximized
|
||||
}
|
||||
|
||||
/// Get all the tags on this window.
|
||||
///
|
||||
/// Shorthand for `self.props().tags`.
|
||||
pub fn tags(&self) -> Vec<TagHandle> {
|
||||
self.props().tags
|
||||
}
|
||||
|
||||
/// The async version of [`tags`][Self::tags].
|
||||
pub async fn tags_async(&self) -> Vec<TagHandle> {
|
||||
self.props_async().await.tags
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue