mirror of
https://github.com/pinnacle-comp/pinnacle.git
synced 2024-12-27 21:58:18 +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 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.
|
/// A struct that allows you to get handles to connected outputs and set them up.
|
||||||
///
|
///
|
||||||
/// See [`OutputHandle`] for more information.
|
/// See [`OutputHandle`] for more information.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Output {
|
pub struct Output {
|
||||||
channel: Channel,
|
|
||||||
fut_sender: UnboundedSender<BoxFuture<'static, ()>>,
|
fut_sender: UnboundedSender<BoxFuture<'static, ()>>,
|
||||||
|
output_client: OutputServiceClient<Channel>,
|
||||||
|
tag_client: TagServiceClient<Channel>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Output {
|
impl Output {
|
||||||
|
@ -38,19 +39,12 @@ impl Output {
|
||||||
fut_sender: UnboundedSender<BoxFuture<'static, ()>>,
|
fut_sender: UnboundedSender<BoxFuture<'static, ()>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
channel,
|
output_client: OutputServiceClient::new(channel.clone()),
|
||||||
|
tag_client: TagServiceClient::new(channel),
|
||||||
fut_sender,
|
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.
|
/// Get a handle to all connected outputs.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
|
@ -59,15 +53,22 @@ impl Output {
|
||||||
/// let outputs = output.get_all();
|
/// let outputs = output.get_all();
|
||||||
/// ```
|
/// ```
|
||||||
pub fn get_all(&self) -> impl Iterator<Item = OutputHandle> {
|
pub fn get_all(&self) -> impl Iterator<Item = OutputHandle> {
|
||||||
let mut client = self.create_output_client();
|
block_on_tokio(self.get_all_async())
|
||||||
let tag_client = self.create_tag_client();
|
}
|
||||||
block_on_tokio(client.get(output::v0alpha1::GetRequest {}))
|
|
||||||
|
/// 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()
|
.unwrap()
|
||||||
.into_inner()
|
.into_inner()
|
||||||
.output_names
|
.output_names
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(move |name| OutputHandle {
|
.map(move |name| OutputHandle {
|
||||||
client: client.clone(),
|
output_client: client.clone(),
|
||||||
tag_client: tag_client.clone(),
|
tag_client: tag_client.clone(),
|
||||||
name,
|
name,
|
||||||
})
|
})
|
||||||
|
@ -84,8 +85,15 @@ impl Output {
|
||||||
/// let op2 = output.get_by_name("HDMI-2")?;
|
/// let op2 = output.get_by_name("HDMI-2")?;
|
||||||
/// ```
|
/// ```
|
||||||
pub fn get_by_name(&self, name: impl Into<String>) -> Option<OutputHandle> {
|
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();
|
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.
|
/// Get a handle to the focused output.
|
||||||
|
@ -102,6 +110,14 @@ impl Output {
|
||||||
.find(|output| matches!(output.props().focused, Some(true)))
|
.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.
|
/// Connect a closure to be run on all current and future outputs.
|
||||||
///
|
///
|
||||||
/// When called, `connect_for_all` will do two things:
|
/// When called, `connect_for_all` will do two things:
|
||||||
|
@ -126,8 +142,8 @@ impl Output {
|
||||||
for_all(output);
|
for_all(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut client = self.create_output_client();
|
let mut client = self.output_client.clone();
|
||||||
let tag_client = self.create_tag_client();
|
let tag_client = self.tag_client.clone();
|
||||||
|
|
||||||
self.fut_sender
|
self.fut_sender
|
||||||
.unbounded_send(
|
.unbounded_send(
|
||||||
|
@ -144,7 +160,7 @@ impl Output {
|
||||||
};
|
};
|
||||||
|
|
||||||
let output = OutputHandle {
|
let output = OutputHandle {
|
||||||
client: client.clone(),
|
output_client: client.clone(),
|
||||||
tag_client: tag_client.clone(),
|
tag_client: tag_client.clone(),
|
||||||
name: output_name,
|
name: output_name,
|
||||||
};
|
};
|
||||||
|
@ -163,7 +179,7 @@ impl Output {
|
||||||
/// This allows you to manipulate outputs and get their properties.
|
/// This allows you to manipulate outputs and get their properties.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct OutputHandle {
|
pub struct OutputHandle {
|
||||||
pub(crate) client: OutputServiceClient<Channel>,
|
pub(crate) output_client: OutputServiceClient<Channel>,
|
||||||
pub(crate) tag_client: TagServiceClient<Channel>,
|
pub(crate) tag_client: TagServiceClient<Channel>,
|
||||||
pub(crate) name: String,
|
pub(crate) name: String,
|
||||||
}
|
}
|
||||||
|
@ -245,7 +261,7 @@ impl OutputHandle {
|
||||||
/// // ^x=1920
|
/// // ^x=1920
|
||||||
/// ```
|
/// ```
|
||||||
pub fn set_location(&self, x: impl Into<Option<i32>>, y: impl Into<Option<i32>>) {
|
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 {
|
block_on_tokio(client.set_location(SetLocationRequest {
|
||||||
output_name: Some(self.name.clone()),
|
output_name: Some(self.name.clone()),
|
||||||
x: x.into(),
|
x: x.into(),
|
||||||
|
@ -377,14 +393,19 @@ impl OutputHandle {
|
||||||
/// } = output.get_focused()?.props();
|
/// } = output.get_focused()?.props();
|
||||||
/// ```
|
/// ```
|
||||||
pub fn props(&self) -> OutputProperties {
|
pub fn props(&self) -> OutputProperties {
|
||||||
let mut client = self.client.clone();
|
block_on_tokio(self.props_async())
|
||||||
let response = block_on_tokio(client.get_properties(
|
}
|
||||||
output::v0alpha1::GetPropertiesRequest {
|
|
||||||
|
/// 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()),
|
output_name: Some(self.name.clone()),
|
||||||
},
|
})
|
||||||
))
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.into_inner();
|
.into_inner();
|
||||||
|
|
||||||
OutputProperties {
|
OutputProperties {
|
||||||
make: response.make,
|
make: response.make,
|
||||||
|
@ -401,8 +422,8 @@ impl OutputHandle {
|
||||||
.tag_ids
|
.tag_ids
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|id| TagHandle {
|
.map(|id| TagHandle {
|
||||||
client: self.tag_client.clone(),
|
tag_client: self.tag_client.clone(),
|
||||||
output_client: self.client.clone(),
|
output_client: self.output_client.clone(),
|
||||||
id,
|
id,
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
|
@ -418,6 +439,11 @@ impl OutputHandle {
|
||||||
self.props().make
|
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.
|
/// Get this output's model.
|
||||||
///
|
///
|
||||||
/// Shorthand for `self.props().make`.
|
/// Shorthand for `self.props().make`.
|
||||||
|
@ -425,6 +451,11 @@ impl OutputHandle {
|
||||||
self.props().model
|
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.
|
/// Get this output's x position in the global space.
|
||||||
///
|
///
|
||||||
/// Shorthand for `self.props().x`.
|
/// Shorthand for `self.props().x`.
|
||||||
|
@ -432,6 +463,11 @@ impl OutputHandle {
|
||||||
self.props().x
|
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.
|
/// Get this output's y position in the global space.
|
||||||
///
|
///
|
||||||
/// Shorthand for `self.props().y`.
|
/// Shorthand for `self.props().y`.
|
||||||
|
@ -439,6 +475,11 @@ impl OutputHandle {
|
||||||
self.props().y
|
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.
|
/// Get this output's screen width in pixels.
|
||||||
///
|
///
|
||||||
/// Shorthand for `self.props().pixel_width`.
|
/// Shorthand for `self.props().pixel_width`.
|
||||||
|
@ -446,6 +487,11 @@ impl OutputHandle {
|
||||||
self.props().pixel_width
|
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.
|
/// Get this output's screen height in pixels.
|
||||||
///
|
///
|
||||||
/// Shorthand for `self.props().pixel_height`.
|
/// Shorthand for `self.props().pixel_height`.
|
||||||
|
@ -453,6 +499,11 @@ impl OutputHandle {
|
||||||
self.props().pixel_height
|
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.
|
/// Get this output's refresh rate in millihertz.
|
||||||
///
|
///
|
||||||
/// For example, 144Hz will be returned as 144000.
|
/// For example, 144Hz will be returned as 144000.
|
||||||
|
@ -462,6 +513,11 @@ impl OutputHandle {
|
||||||
self.props().refresh_rate
|
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.
|
/// Get this output's physical width in millimeters.
|
||||||
///
|
///
|
||||||
/// Shorthand for `self.props().physical_width`.
|
/// Shorthand for `self.props().physical_width`.
|
||||||
|
@ -469,6 +525,11 @@ impl OutputHandle {
|
||||||
self.props().physical_width
|
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.
|
/// Get this output's physical height in millimeters.
|
||||||
///
|
///
|
||||||
/// Shorthand for `self.props().physical_height`.
|
/// Shorthand for `self.props().physical_height`.
|
||||||
|
@ -476,6 +537,11 @@ impl OutputHandle {
|
||||||
self.props().physical_height
|
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.
|
/// Get whether this output is focused or not.
|
||||||
///
|
///
|
||||||
/// This is currently implemented as the output with the most recent pointer motion.
|
/// This is currently implemented as the output with the most recent pointer motion.
|
||||||
|
@ -485,6 +551,11 @@ impl OutputHandle {
|
||||||
self.props().focused
|
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.
|
/// Get the tags this output has.
|
||||||
///
|
///
|
||||||
/// Shorthand for `self.props().tags`
|
/// Shorthand for `self.props().tags`
|
||||||
|
@ -492,6 +563,11 @@ impl OutputHandle {
|
||||||
self.props().tags
|
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).
|
/// Get this output's unique name (the name of its connector).
|
||||||
pub fn name(&self) -> &str {
|
pub fn name(&self) -> &str {
|
||||||
&self.name
|
&self.name
|
||||||
|
@ -499,7 +575,7 @@ impl OutputHandle {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The properties of an output.
|
/// The properties of an output.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
|
||||||
pub struct OutputProperties {
|
pub struct OutputProperties {
|
||||||
/// The make of the output
|
/// The make of the output
|
||||||
pub make: Option<String>,
|
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.
|
/// Optional callbacks to be run when a spawned process prints to stdout or stderr or exits.
|
||||||
|
#[derive(Default)]
|
||||||
pub struct SpawnCallbacks {
|
pub struct SpawnCallbacks {
|
||||||
/// A callback that will be run when a process prints to stdout with a line
|
/// A callback that will be run when a process prints to stdout with a line
|
||||||
pub stdout: Option<Box<dyn FnMut(String) + Send>>,
|
pub stdout: Option<Box<dyn FnMut(String) + Send>>,
|
||||||
|
|
|
@ -34,7 +34,7 @@ use std::{
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
};
|
};
|
||||||
|
|
||||||
use futures::{channel::mpsc::UnboundedSender, future::BoxFuture};
|
use futures::{channel::mpsc::UnboundedSender, future::BoxFuture, FutureExt};
|
||||||
use num_enum::TryFromPrimitive;
|
use num_enum::TryFromPrimitive;
|
||||||
use pinnacle_api_defs::pinnacle::{
|
use pinnacle_api_defs::pinnacle::{
|
||||||
output::v0alpha1::output_service_client::OutputServiceClient,
|
output::v0alpha1::output_service_client::OutputServiceClient,
|
||||||
|
@ -51,6 +51,7 @@ use tonic::transport::Channel;
|
||||||
use crate::{
|
use crate::{
|
||||||
block_on_tokio,
|
block_on_tokio,
|
||||||
output::{Output, OutputHandle},
|
output::{Output, OutputHandle},
|
||||||
|
util::Batch,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A struct that allows you to add and remove tags and get [`TagHandle`]s.
|
/// A struct that allows you to add and remove tags and get [`TagHandle`]s.
|
||||||
|
@ -58,6 +59,8 @@ use crate::{
|
||||||
pub struct Tag {
|
pub struct Tag {
|
||||||
channel: Channel,
|
channel: Channel,
|
||||||
fut_sender: UnboundedSender<BoxFuture<'static, ()>>,
|
fut_sender: UnboundedSender<BoxFuture<'static, ()>>,
|
||||||
|
tag_client: TagServiceClient<Channel>,
|
||||||
|
output_client: OutputServiceClient<Channel>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tag {
|
impl Tag {
|
||||||
|
@ -66,19 +69,13 @@ impl Tag {
|
||||||
fut_sender: UnboundedSender<BoxFuture<'static, ()>>,
|
fut_sender: UnboundedSender<BoxFuture<'static, ()>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
tag_client: TagServiceClient::new(channel.clone()),
|
||||||
|
output_client: OutputServiceClient::new(channel.clone()),
|
||||||
channel,
|
channel,
|
||||||
fut_sender,
|
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.
|
/// Add tags to the specified output.
|
||||||
///
|
///
|
||||||
/// This will add tags with the given names to `output` and return [`TagHandle`]s to all of
|
/// 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,
|
output: &OutputHandle,
|
||||||
tag_names: impl IntoIterator<Item = impl Into<String>>,
|
tag_names: impl IntoIterator<Item = impl Into<String>>,
|
||||||
) -> impl Iterator<Item = TagHandle> {
|
) -> impl Iterator<Item = TagHandle> {
|
||||||
let mut client = self.create_tag_client();
|
block_on_tokio(self.add_async(output, tag_names))
|
||||||
let output_client = self.create_output_client();
|
}
|
||||||
|
|
||||||
|
/// 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 tag_names = tag_names.into_iter().map(Into::into).collect();
|
||||||
|
|
||||||
let response = block_on_tokio(client.add(AddRequest {
|
let response = client
|
||||||
output_name: Some(output.name.clone()),
|
.add(AddRequest {
|
||||||
tag_names,
|
output_name: Some(output.name.clone()),
|
||||||
}))
|
tag_names,
|
||||||
.unwrap()
|
})
|
||||||
.into_inner();
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.into_inner();
|
||||||
|
|
||||||
response.tag_ids.into_iter().map(move |id| TagHandle {
|
response.tag_ids.into_iter().map(move |id| TagHandle {
|
||||||
client: client.clone(),
|
tag_client: client.clone(),
|
||||||
output_client: output_client.clone(),
|
output_client: output_client.clone(),
|
||||||
id,
|
id,
|
||||||
})
|
})
|
||||||
|
@ -124,15 +132,22 @@ impl Tag {
|
||||||
/// let all_tags = tag.get_all();
|
/// let all_tags = tag.get_all();
|
||||||
/// ```
|
/// ```
|
||||||
pub fn get_all(&self) -> impl Iterator<Item = TagHandle> {
|
pub fn get_all(&self) -> impl Iterator<Item = TagHandle> {
|
||||||
let mut client = self.create_tag_client();
|
block_on_tokio(self.get_all_async())
|
||||||
let output_client = self.create_output_client();
|
}
|
||||||
|
|
||||||
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()
|
.unwrap()
|
||||||
.into_inner();
|
.into_inner();
|
||||||
|
|
||||||
response.tag_ids.into_iter().map(move |id| TagHandle {
|
response.tag_ids.into_iter().map(move |id| TagHandle {
|
||||||
client: client.clone(),
|
tag_client: client.clone(),
|
||||||
output_client: output_client.clone(),
|
output_client: output_client.clone(),
|
||||||
id,
|
id,
|
||||||
})
|
})
|
||||||
|
@ -149,18 +164,20 @@ impl Tag {
|
||||||
/// let tg = tag.get("Thing");
|
/// let tg = tag.get("Thing");
|
||||||
/// ```
|
/// ```
|
||||||
pub fn get(&self, name: impl Into<String>) -> Option<TagHandle> {
|
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 name = name.into();
|
||||||
let output_module = Output::new(self.channel.clone(), self.fut_sender.clone());
|
let output_module = Output::new(self.channel.clone(), self.fut_sender.clone());
|
||||||
let focused_output = output_module.get_focused();
|
let focused_output = output_module.get_focused();
|
||||||
|
|
||||||
self.get_all().find(|tag| {
|
if let Some(output) = focused_output {
|
||||||
let props = tag.props();
|
self.get_on_output_async(name, &output).await
|
||||||
|
} else {
|
||||||
let same_tag_name = props.name.as_ref() == Some(&name);
|
None
|
||||||
let same_output = props.output.is_some_and(|op| Some(op) == focused_output);
|
}
|
||||||
|
|
||||||
same_tag_name && same_output
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a handle to the first tag with the given name on the specified output.
|
/// Get a handle to the first tag with the given name on the specified output.
|
||||||
|
@ -177,17 +194,27 @@ impl Tag {
|
||||||
&self,
|
&self,
|
||||||
name: impl Into<String>,
|
name: impl Into<String>,
|
||||||
output: &OutputHandle,
|
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> {
|
) -> Option<TagHandle> {
|
||||||
let name = name.into();
|
let name = name.into();
|
||||||
|
|
||||||
self.get_all().find(|tag| {
|
self.get_all_async().await.batch_find(
|
||||||
let props = tag.props();
|
|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);
|
same_tag_name && same_output
|
||||||
let same_output = props.output.is_some_and(|op| &op == output);
|
},
|
||||||
|
)
|
||||||
same_tag_name && same_output
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove the given tags from their outputs.
|
/// Remove the given tags from their outputs.
|
||||||
|
@ -202,7 +229,7 @@ impl Tag {
|
||||||
pub fn remove(&self, tags: impl IntoIterator<Item = TagHandle>) {
|
pub fn remove(&self, tags: impl IntoIterator<Item = TagHandle>) {
|
||||||
let tag_ids = tags.into_iter().map(|handle| handle.id).collect::<Vec<_>>();
|
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();
|
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.
|
/// This handle allows you to do things like switch to tags and get their properties.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct TagHandle {
|
pub struct TagHandle {
|
||||||
pub(crate) client: TagServiceClient<Channel>,
|
|
||||||
pub(crate) output_client: OutputServiceClient<Channel>,
|
|
||||||
pub(crate) id: u32,
|
pub(crate) id: u32,
|
||||||
|
pub(crate) tag_client: TagServiceClient<Channel>,
|
||||||
|
pub(crate) output_client: OutputServiceClient<Channel>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for TagHandle {
|
impl PartialEq for TagHandle {
|
||||||
|
@ -388,7 +415,7 @@ impl TagHandle {
|
||||||
/// tag.get("3")?.switch_to(); // Displays Steam
|
/// tag.get("3")?.switch_to(); // Displays Steam
|
||||||
/// ```
|
/// ```
|
||||||
pub fn switch_to(&self) {
|
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 {
|
block_on_tokio(client.switch_to(SwitchToRequest {
|
||||||
tag_id: Some(self.id),
|
tag_id: Some(self.id),
|
||||||
}))
|
}))
|
||||||
|
@ -414,7 +441,7 @@ impl TagHandle {
|
||||||
/// tag.get("2")?.set_active(false); // Displays Steam
|
/// tag.get("2")?.set_active(false); // Displays Steam
|
||||||
/// ```
|
/// ```
|
||||||
pub fn set_active(&self, set: bool) {
|
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 {
|
block_on_tokio(client.set_active(SetActiveRequest {
|
||||||
tag_id: Some(self.id),
|
tag_id: Some(self.id),
|
||||||
set_or_toggle: Some(tag::v0alpha1::set_active_request::SetOrToggle::Set(set)),
|
set_or_toggle: Some(tag::v0alpha1::set_active_request::SetOrToggle::Set(set)),
|
||||||
|
@ -442,7 +469,7 @@ impl TagHandle {
|
||||||
/// tag.get("2")?.toggle(); // Displays nothing
|
/// tag.get("2")?.toggle(); // Displays nothing
|
||||||
/// ```
|
/// ```
|
||||||
pub fn toggle_active(&self) {
|
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 {
|
block_on_tokio(client.set_active(SetActiveRequest {
|
||||||
tag_id: Some(self.id),
|
tag_id: Some(self.id),
|
||||||
set_or_toggle: Some(tag::v0alpha1::set_active_request::SetOrToggle::Toggle(())),
|
set_or_toggle: Some(tag::v0alpha1::set_active_request::SetOrToggle::Toggle(())),
|
||||||
|
@ -463,8 +490,9 @@ impl TagHandle {
|
||||||
/// tags[3].remove();
|
/// tags[3].remove();
|
||||||
/// // "DP-1" now only has tags "1" and "Buckle"
|
/// // "DP-1" now only has tags "1" and "Buckle"
|
||||||
/// ```
|
/// ```
|
||||||
pub fn remove(mut self) {
|
pub fn remove(&self) {
|
||||||
block_on_tokio(self.client.remove(RemoveRequest {
|
let mut tag_client = self.tag_client.clone();
|
||||||
|
block_on_tokio(tag_client.remove(RemoveRequest {
|
||||||
tag_ids: vec![self.id],
|
tag_ids: vec![self.id],
|
||||||
}))
|
}))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -487,7 +515,7 @@ impl TagHandle {
|
||||||
/// tag.get("1", None)?.set_layout(Layout::CornerTopLeft);
|
/// tag.get("1", None)?.set_layout(Layout::CornerTopLeft);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn set_layout(&self, layout: Layout) {
|
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 {
|
block_on_tokio(client.set_layout(SetLayoutRequest {
|
||||||
tag_id: Some(self.id),
|
tag_id: Some(self.id),
|
||||||
layout: Some(layout as i32),
|
layout: Some(layout as i32),
|
||||||
|
@ -509,20 +537,27 @@ impl TagHandle {
|
||||||
/// } = tag.get("1", None)?.props();
|
/// } = tag.get("1", None)?.props();
|
||||||
/// ```
|
/// ```
|
||||||
pub fn props(&self) -> TagProperties {
|
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 output_client = self.output_client.clone();
|
||||||
|
|
||||||
let response = block_on_tokio(client.get_properties(tag::v0alpha1::GetPropertiesRequest {
|
let response = client
|
||||||
tag_id: Some(self.id),
|
.get_properties(tag::v0alpha1::GetPropertiesRequest {
|
||||||
}))
|
tag_id: Some(self.id),
|
||||||
.unwrap()
|
})
|
||||||
.into_inner();
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.into_inner();
|
||||||
|
|
||||||
TagProperties {
|
TagProperties {
|
||||||
active: response.active,
|
active: response.active,
|
||||||
name: response.name,
|
name: response.name,
|
||||||
output: response.output_name.map(|name| OutputHandle {
|
output: response.output_name.map(|name| OutputHandle {
|
||||||
client: output_client,
|
output_client,
|
||||||
tag_client: client,
|
tag_client: client,
|
||||||
name,
|
name,
|
||||||
}),
|
}),
|
||||||
|
@ -536,6 +571,11 @@ impl TagHandle {
|
||||||
self.props().active
|
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.
|
/// Get this tag's name.
|
||||||
///
|
///
|
||||||
/// Shorthand for `self.props().name`.
|
/// Shorthand for `self.props().name`.
|
||||||
|
@ -543,15 +583,26 @@ impl TagHandle {
|
||||||
self.props().name
|
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.
|
/// Get a handle to the output this tag is on.
|
||||||
///
|
///
|
||||||
/// Shorthand for `self.props().output`.
|
/// Shorthand for `self.props().output`.
|
||||||
pub fn output(&self) -> Option<OutputHandle> {
|
pub fn output(&self) -> Option<OutputHandle> {
|
||||||
self.props().output
|
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.
|
/// Properties of a tag.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
|
||||||
pub struct TagProperties {
|
pub struct TagProperties {
|
||||||
/// Whether the tag is active or not
|
/// Whether the tag is active or not
|
||||||
pub active: Option<bool>,
|
pub active: Option<bool>,
|
||||||
|
|
|
@ -4,6 +4,15 @@
|
||||||
|
|
||||||
//! Utility types.
|
//! 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.
|
/// The size and location of something.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub struct Geometry {
|
pub struct Geometry {
|
||||||
|
@ -16,3 +25,160 @@ pub struct Geometry {
|
||||||
/// The height
|
/// The height
|
||||||
pub height: u32,
|
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.
|
//! This module also allows you to set window rules; see the [rules] module for more information.
|
||||||
|
|
||||||
|
use futures::FutureExt;
|
||||||
use num_enum::TryFromPrimitive;
|
use num_enum::TryFromPrimitive;
|
||||||
use pinnacle_api_defs::pinnacle::{
|
use pinnacle_api_defs::pinnacle::{
|
||||||
output::v0alpha1::output_service_client::OutputServiceClient,
|
output::v0alpha1::output_service_client::OutputServiceClient,
|
||||||
|
@ -30,7 +31,12 @@ use pinnacle_api_defs::pinnacle::{
|
||||||
};
|
};
|
||||||
use tonic::transport::Channel;
|
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};
|
use self::rules::{WindowRule, WindowRuleCondition};
|
||||||
|
|
||||||
|
@ -41,24 +47,18 @@ pub mod rules;
|
||||||
/// See [`WindowHandle`] for more information.
|
/// See [`WindowHandle`] for more information.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Window {
|
pub struct Window {
|
||||||
channel: Channel,
|
window_client: WindowServiceClient<Channel>,
|
||||||
|
tag_client: TagServiceClient<Channel>,
|
||||||
|
output_client: OutputServiceClient<Channel>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Window {
|
impl Window {
|
||||||
pub(crate) fn new(channel: Channel) -> Self {
|
pub(crate) fn new(channel: Channel) -> Self {
|
||||||
Self { channel }
|
Self {
|
||||||
}
|
window_client: WindowServiceClient::new(channel.clone()),
|
||||||
|
tag_client: TagServiceClient::new(channel.clone()),
|
||||||
fn create_window_client(&self) -> WindowServiceClient<Channel> {
|
output_client: OutputServiceClient::new(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())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Start moving the window with the mouse.
|
/// 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`].
|
/// 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.
|
/// 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
|
/// # Examples
|
||||||
///
|
///
|
||||||
|
@ -79,11 +79,12 @@ impl Window {
|
||||||
/// });
|
/// });
|
||||||
/// ```
|
/// ```
|
||||||
pub fn begin_move(&self, button: MouseButton) {
|
pub fn begin_move(&self, button: MouseButton) {
|
||||||
let mut client = self.create_window_client();
|
let mut client = self.window_client.clone();
|
||||||
block_on_tokio(client.move_grab(MoveGrabRequest {
|
if let Err(status) = block_on_tokio(client.move_grab(MoveGrabRequest {
|
||||||
button: Some(button as u32),
|
button: Some(button as u32),
|
||||||
}))
|
})) {
|
||||||
.unwrap();
|
eprintln!("ERROR: {status}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Start resizing the window with the mouse.
|
/// 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`].
|
/// 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.
|
/// 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
|
/// # Examples
|
||||||
///
|
///
|
||||||
|
@ -104,7 +105,7 @@ impl Window {
|
||||||
/// });
|
/// });
|
||||||
/// ```
|
/// ```
|
||||||
pub fn begin_resize(&self, button: MouseButton) {
|
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 {
|
block_on_tokio(client.resize_grab(ResizeGrabRequest {
|
||||||
button: Some(button as u32),
|
button: Some(button as u32),
|
||||||
}))
|
}))
|
||||||
|
@ -119,16 +120,23 @@ impl Window {
|
||||||
/// let windows = window.get_all();
|
/// let windows = window.get_all();
|
||||||
/// ```
|
/// ```
|
||||||
pub fn get_all(&self) -> impl Iterator<Item = WindowHandle> {
|
pub fn get_all(&self) -> impl Iterator<Item = WindowHandle> {
|
||||||
let mut client = self.create_window_client();
|
block_on_tokio(self.get_all_async())
|
||||||
let tag_client = self.create_tag_client();
|
}
|
||||||
let output_client = self.create_output_client();
|
|
||||||
block_on_tokio(client.get(GetRequest {}))
|
/// 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()
|
.unwrap()
|
||||||
.into_inner()
|
.into_inner()
|
||||||
.window_ids
|
.window_ids
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(move |id| WindowHandle {
|
.map(move |id| WindowHandle {
|
||||||
client: client.clone(),
|
window_client: client.clone(),
|
||||||
id,
|
id,
|
||||||
tag_client: tag_client.clone(),
|
tag_client: tag_client.clone(),
|
||||||
output_client: output_client.clone(),
|
output_client: output_client.clone(),
|
||||||
|
@ -143,8 +151,15 @@ impl Window {
|
||||||
/// let focused_window = window.get_focused()?;
|
/// let focused_window = window.get_focused()?;
|
||||||
/// ```
|
/// ```
|
||||||
pub fn get_focused(&self) -> Option<WindowHandle> {
|
pub fn get_focused(&self) -> Option<WindowHandle> {
|
||||||
self.get_all()
|
block_on_tokio(self.get_focused_async())
|
||||||
.find(|window| matches!(window.props().focused, Some(true)))
|
}
|
||||||
|
|
||||||
|
/// 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.
|
/// Add a window rule.
|
||||||
|
@ -154,7 +169,7 @@ impl Window {
|
||||||
///
|
///
|
||||||
/// TODO:
|
/// TODO:
|
||||||
pub fn add_window_rule(&self, cond: WindowRuleCondition, rule: WindowRule) {
|
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 {
|
block_on_tokio(client.add_window_rule(AddWindowRuleRequest {
|
||||||
cond: Some(cond.0),
|
cond: Some(cond.0),
|
||||||
|
@ -169,10 +184,10 @@ impl Window {
|
||||||
/// This allows you to manipulate the window and get its properties.
|
/// This allows you to manipulate the window and get its properties.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct WindowHandle {
|
pub struct WindowHandle {
|
||||||
pub(crate) client: WindowServiceClient<Channel>,
|
id: u32,
|
||||||
pub(crate) id: u32,
|
window_client: WindowServiceClient<Channel>,
|
||||||
pub(crate) tag_client: TagServiceClient<Channel>,
|
tag_client: TagServiceClient<Channel>,
|
||||||
pub(crate) output_client: OutputServiceClient<Channel>,
|
output_client: OutputServiceClient<Channel>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for WindowHandle {
|
impl PartialEq for WindowHandle {
|
||||||
|
@ -202,7 +217,7 @@ pub enum FullscreenOrMaximized {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Properties of a window.
|
/// Properties of a window.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
|
||||||
pub struct WindowProperties {
|
pub struct WindowProperties {
|
||||||
/// The location and size of the window
|
/// The location and size of the window
|
||||||
pub geometry: Option<Geometry>,
|
pub geometry: Option<Geometry>,
|
||||||
|
@ -215,7 +230,7 @@ pub struct WindowProperties {
|
||||||
/// Whether the window is floating or not
|
/// Whether the window is floating or not
|
||||||
///
|
///
|
||||||
/// Note that a window can still be floating even if it's fullscreen or maximized; those two
|
/// 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>,
|
pub floating: Option<bool>,
|
||||||
/// Whether the window is fullscreen, maximized, or neither
|
/// Whether the window is fullscreen, maximized, or neither
|
||||||
pub fullscreen_or_maximized: Option<FullscreenOrMaximized>,
|
pub fullscreen_or_maximized: Option<FullscreenOrMaximized>,
|
||||||
|
@ -234,8 +249,9 @@ impl WindowHandle {
|
||||||
/// // Close the focused window
|
/// // Close the focused window
|
||||||
/// window.get_focused()?.close()
|
/// window.get_focused()?.close()
|
||||||
/// ```
|
/// ```
|
||||||
pub fn close(mut self) {
|
pub fn close(&self) {
|
||||||
block_on_tokio(self.client.close(CloseRequest {
|
let mut window_client = self.window_client.clone();
|
||||||
|
block_on_tokio(window_client.close(CloseRequest {
|
||||||
window_id: Some(self.id),
|
window_id: Some(self.id),
|
||||||
}))
|
}))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -252,7 +268,7 @@ impl WindowHandle {
|
||||||
/// window.get_focused()?.set_fullscreen(true);
|
/// window.get_focused()?.set_fullscreen(true);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn set_fullscreen(&self, set: bool) {
|
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 {
|
block_on_tokio(client.set_fullscreen(SetFullscreenRequest {
|
||||||
window_id: Some(self.id),
|
window_id: Some(self.id),
|
||||||
set_or_toggle: Some(window::v0alpha1::set_fullscreen_request::SetOrToggle::Set(
|
set_or_toggle: Some(window::v0alpha1::set_fullscreen_request::SetOrToggle::Set(
|
||||||
|
@ -273,7 +289,7 @@ impl WindowHandle {
|
||||||
/// window.get_focused()?.toggle_fullscreen();
|
/// window.get_focused()?.toggle_fullscreen();
|
||||||
/// ```
|
/// ```
|
||||||
pub fn toggle_fullscreen(&self) {
|
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 {
|
block_on_tokio(client.set_fullscreen(SetFullscreenRequest {
|
||||||
window_id: Some(self.id),
|
window_id: Some(self.id),
|
||||||
set_or_toggle: Some(window::v0alpha1::set_fullscreen_request::SetOrToggle::Toggle(())),
|
set_or_toggle: Some(window::v0alpha1::set_fullscreen_request::SetOrToggle::Toggle(())),
|
||||||
|
@ -292,7 +308,7 @@ impl WindowHandle {
|
||||||
/// window.get_focused()?.set_maximized(true);
|
/// window.get_focused()?.set_maximized(true);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn set_maximized(&self, set: bool) {
|
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 {
|
block_on_tokio(client.set_maximized(SetMaximizedRequest {
|
||||||
window_id: Some(self.id),
|
window_id: Some(self.id),
|
||||||
set_or_toggle: Some(window::v0alpha1::set_maximized_request::SetOrToggle::Set(
|
set_or_toggle: Some(window::v0alpha1::set_maximized_request::SetOrToggle::Set(
|
||||||
|
@ -304,7 +320,7 @@ impl WindowHandle {
|
||||||
|
|
||||||
/// Toggle this window between maximized and not.
|
/// 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
|
/// # Examples
|
||||||
///
|
///
|
||||||
|
@ -313,7 +329,7 @@ impl WindowHandle {
|
||||||
/// window.get_focused()?.toggle_maximized();
|
/// window.get_focused()?.toggle_maximized();
|
||||||
/// ```
|
/// ```
|
||||||
pub fn toggle_maximized(&self) {
|
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 {
|
block_on_tokio(client.set_maximized(SetMaximizedRequest {
|
||||||
window_id: Some(self.id),
|
window_id: Some(self.id),
|
||||||
set_or_toggle: Some(window::v0alpha1::set_maximized_request::SetOrToggle::Toggle(())),
|
set_or_toggle: Some(window::v0alpha1::set_maximized_request::SetOrToggle::Toggle(())),
|
||||||
|
@ -335,7 +351,7 @@ impl WindowHandle {
|
||||||
/// window.get_focused()?.set_floating(true);
|
/// window.get_focused()?.set_floating(true);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn set_floating(&self, set: bool) {
|
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 {
|
block_on_tokio(client.set_floating(SetFloatingRequest {
|
||||||
window_id: Some(self.id),
|
window_id: Some(self.id),
|
||||||
set_or_toggle: Some(window::v0alpha1::set_floating_request::SetOrToggle::Set(
|
set_or_toggle: Some(window::v0alpha1::set_floating_request::SetOrToggle::Set(
|
||||||
|
@ -359,7 +375,7 @@ impl WindowHandle {
|
||||||
/// window.get_focused()?.toggle_floating();
|
/// window.get_focused()?.toggle_floating();
|
||||||
/// ```
|
/// ```
|
||||||
pub fn toggle_floating(&self) {
|
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 {
|
block_on_tokio(client.set_floating(SetFloatingRequest {
|
||||||
window_id: Some(self.id),
|
window_id: Some(self.id),
|
||||||
set_or_toggle: Some(window::v0alpha1::set_floating_request::SetOrToggle::Toggle(
|
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)?);
|
/// window.get_focused()?.move_to_tag(&tag.get("Code", None)?);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn move_to_tag(&self, tag: &TagHandle) {
|
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 {
|
block_on_tokio(client.move_to_tag(MoveToTagRequest {
|
||||||
window_id: Some(self.id),
|
window_id: Some(self.id),
|
||||||
|
@ -402,7 +418,7 @@ impl WindowHandle {
|
||||||
/// focused.set_tag(&tg, false); // `focused` no longer has tag "Potato"
|
/// focused.set_tag(&tg, false); // `focused` no longer has tag "Potato"
|
||||||
/// ```
|
/// ```
|
||||||
pub fn set_tag(&self, tag: &TagHandle, set: bool) {
|
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 {
|
block_on_tokio(client.set_tag(SetTagRequest {
|
||||||
window_id: Some(self.id),
|
window_id: Some(self.id),
|
||||||
|
@ -426,7 +442,7 @@ impl WindowHandle {
|
||||||
/// focused.toggle_tag(&tg); // `focused` no longer has tag "Potato"
|
/// focused.toggle_tag(&tg); // `focused` no longer has tag "Potato"
|
||||||
/// ```
|
/// ```
|
||||||
pub fn toggle_tag(&self, tag: &TagHandle) {
|
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 {
|
block_on_tokio(client.set_tag(SetTagRequest {
|
||||||
window_id: Some(self.id),
|
window_id: Some(self.id),
|
||||||
|
@ -454,15 +470,26 @@ impl WindowHandle {
|
||||||
/// } = window.get_focused()?.props();
|
/// } = window.get_focused()?.props();
|
||||||
/// ```
|
/// ```
|
||||||
pub fn props(&self) -> WindowProperties {
|
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 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),
|
window_id: Some(self.id),
|
||||||
},
|
})
|
||||||
))
|
.await
|
||||||
.unwrap()
|
{
|
||||||
.into_inner();
|
Ok(response) => response.into_inner(),
|
||||||
|
Err(status) => {
|
||||||
|
eprintln!("ERROR: {status}");
|
||||||
|
return WindowProperties::default();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let fullscreen_or_maximized = response
|
let fullscreen_or_maximized = response
|
||||||
.fullscreen_or_maximized
|
.fullscreen_or_maximized
|
||||||
|
@ -488,7 +515,7 @@ impl WindowHandle {
|
||||||
.tag_ids
|
.tag_ids
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|id| TagHandle {
|
.map(|id| TagHandle {
|
||||||
client: tag_client.clone(),
|
tag_client: tag_client.clone(),
|
||||||
output_client: self.output_client.clone(),
|
output_client: self.output_client.clone(),
|
||||||
id,
|
id,
|
||||||
})
|
})
|
||||||
|
@ -503,6 +530,11 @@ impl WindowHandle {
|
||||||
self.props().geometry
|
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.
|
/// Get this window's class.
|
||||||
///
|
///
|
||||||
/// Shorthand for `self.props().class`.
|
/// Shorthand for `self.props().class`.
|
||||||
|
@ -510,6 +542,11 @@ impl WindowHandle {
|
||||||
self.props().class
|
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.
|
/// Get this window's title.
|
||||||
///
|
///
|
||||||
/// Shorthand for `self.props().title`.
|
/// Shorthand for `self.props().title`.
|
||||||
|
@ -517,6 +554,11 @@ impl WindowHandle {
|
||||||
self.props().title
|
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.
|
/// Get whether or not this window is focused.
|
||||||
///
|
///
|
||||||
/// Shorthand for `self.props().focused`.
|
/// Shorthand for `self.props().focused`.
|
||||||
|
@ -524,6 +566,11 @@ impl WindowHandle {
|
||||||
self.props().focused
|
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.
|
/// Get whether or not this window is floating.
|
||||||
///
|
///
|
||||||
/// Shorthand for `self.props().floating`.
|
/// Shorthand for `self.props().floating`.
|
||||||
|
@ -531,6 +578,11 @@ impl WindowHandle {
|
||||||
self.props().floating
|
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.
|
/// Get whether this window is fullscreen, maximized, or neither.
|
||||||
///
|
///
|
||||||
/// Shorthand for `self.props().fullscreen_or_maximized`.
|
/// Shorthand for `self.props().fullscreen_or_maximized`.
|
||||||
|
@ -538,10 +590,20 @@ impl WindowHandle {
|
||||||
self.props().fullscreen_or_maximized
|
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.
|
/// Get all the tags on this window.
|
||||||
///
|
///
|
||||||
/// Shorthand for `self.props().tags`.
|
/// Shorthand for `self.props().tags`.
|
||||||
pub fn tags(&self) -> Vec<TagHandle> {
|
pub fn tags(&self) -> Vec<TagHandle> {
|
||||||
self.props().tags
|
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