Merge pull request #132 from pinnacle-comp/rust_api_tokio_and_futures

Get the futures to work
This commit is contained in:
Ottatop 2024-01-20 15:34:08 -06:00 committed by GitHub
commit 5b9450fa8d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 517 additions and 268 deletions

View file

@ -7,10 +7,13 @@ edition = "2021"
pinnacle-api-defs = { path = "../../pinnacle-api-defs" }
pinnacle-api-macros = { path = "./pinnacle-api-macros" }
tokio = { version = "1.35.1", features = ["macros", "rt-multi-thread", "net"] }
tokio-stream = { version = "0.1.14", features = ["net"] }
# tokio-stream = { version = "0.1.14", features = ["net"] }
async-net = "2.0.0"
async-compat = "0.2.3"
tonic = "0.10.2"
tower = { version = "0.4.13", features = ["util"] }
futures = "0.3.30"
# futures-lite = "2.2.0"
num_enum = "0.7.2"
xkbcommon = "0.7.0"

View file

@ -1,18 +0,0 @@
use pinnacle_api::{input::Mod, ApiModules};
#[pinnacle_api::config(modules)]
#[tokio::main]
async fn main() {
let ApiModules {
pinnacle,
process,
window,
input,
output,
tag,
} = modules;
input.keybind([Mod::Shift], 'a', || {
process.spawn(["alacritty"]);
});
}

View file

@ -0,0 +1,103 @@
use pinnacle_api::{
input::{Mod, MouseButton, MouseEdge},
ApiModules,
};
use xkbcommon::xkb::keysyms;
#[pinnacle_api::config(modules)]
#[tokio::main]
async fn main() {
let ApiModules {
pinnacle,
process,
window,
input,
output,
tag,
} = modules;
let mod_key = Mod::Ctrl;
input.mousebind([mod_key], MouseButton::Left, MouseEdge::Press, || {
window.begin_move(MouseButton::Left);
});
input.mousebind([mod_key], MouseButton::Right, MouseEdge::Press, || {
window.begin_resize(MouseButton::Right);
});
// Keybinds
input.keybind([mod_key, Mod::Alt], 'q', || {
pinnacle.quit();
});
input.keybind([mod_key, Mod::Alt], 'c', || {
if let Some(window) = window.get_focused() {
window.close();
}
});
input.keybind([mod_key], keysyms::KEY_Return, || {
process.spawn(["alacritty"]);
});
input.keybind([mod_key, Mod::Alt], keysyms::KEY_space, || {
if let Some(window) = window.get_focused() {
window.toggle_floating();
}
});
input.keybind([mod_key], 'f', || {
if let Some(window) = window.get_focused() {
window.toggle_fullscreen();
}
});
input.keybind([mod_key], 'm', || {
if let Some(window) = window.get_focused() {
window.toggle_maximized();
}
});
// Tags
let tag_names = ["1", "2", "3", "4", "5"];
output.connect_for_all(move |op| {
let mut tags = tag.add(&op, tag_names);
tags.next().unwrap().set_active(true);
});
process.spawn_once(["alacritty"]);
for tag_name in tag_names {
input.keybind([mod_key], tag_name, move || {
if let Some(tg) = tag.get(tag_name, None) {
tg.switch_to();
}
});
input.keybind([mod_key, Mod::Shift], tag_name, move || {
if let Some(tg) = tag.get(tag_name, None) {
tg.toggle_active();
}
});
input.keybind([mod_key, Mod::Alt], tag_name, move || {
if let Some(tg) = tag.get(tag_name, None) {
if let Some(win) = window.get_focused() {
win.move_to_tag(&tg);
}
}
});
input.keybind([mod_key, Mod::Shift, Mod::Alt], tag_name, move || {
if let Some(tg) = tag.get(tag_name, None) {
if let Some(win) = window.get_focused() {
win.toggle_tag(&tg);
}
}
});
}
}

View file

@ -0,0 +1,46 @@
# This metaconfig.toml file dictates what config Pinnacle will run.
#
# When running Pinnacle, the compositor will look in the following directories for a metaconfig.toml file,
# in order from top to bottom:
# $PINNACLE_CONFIG_DIR
# $XDG_CONFIG_HOME/pinnacle/
# ~/.config/pinnacle/
#
# When Pinnacle finds a metaconfig.toml file, it will execute the command provided to `command`.
# To use a Rust config, this should be changed to something like ["cargo", "run"].
#
# Because configuration is done using an external process, if it ever crashes, you lose all of your keybinds.
# The compositor will load the default config if that happens, but in the event that you don't have
# the necessary dependencies for it to run, you may get softlocked.
# In order prevent you from getting stuck in the compositor, you must define keybinds to reload your config
# and kill Pinnacle.
#
# More details on each setting can be found below.
# The command Pinnacle will run on startup and when you reload your config.
# Paths are relative to the directory the metaconfig.toml file is in.
# This must be an array.
command = ["cargo", "run", "--example", "default_config"]
### Keybinds ###
# Each keybind takes in a table with two fields: `modifiers` and `key`.
# - `modifiers` can be one of "Ctrl", "Alt", "Shift", or "Super".
# - `key` can be a string of any lowercase letter, number,
# "numN" where N is a number for numpad keys, or "esc"/"escape".
# Support for any xkbcommon key is planned for a future update.
# The keybind that will reload your config.
reload_keybind = { modifiers = ["Ctrl", "Alt"], key = "r" }
# The keybind that will kill Pinnacle.
kill_keybind = { modifiers = ["Ctrl", "Alt", "Shift"], key = "escape" }
### Socket directory ###
# Pinnacle will open a Unix socket at `$XDG_RUNTIME_DIR` by default, falling back to `/tmp` if it doesn't exist.
# If you want/need to change this, use the `socket_dir` setting set to the directory of your choosing.
#
# socket_dir = "/your/dir/here/"
### Environment Variables ###
# If you need to spawn your config with any environment variables, list them here.
[envs]
# key = "value"

View file

@ -20,11 +20,11 @@ pub fn config(
quote! {
#(#attrs)*
#vis #sig {
let (#module_ident, __fut_receiver) = ::pinnacle_api::create_modules().unwrap();
let (#module_ident, __fut_receiver) = ::pinnacle_api::connect().await.unwrap();
#(#stmts)*
::pinnacle_api::listen(__fut_receiver);
::pinnacle_api::listen(__fut_receiver).await;
}
}
.into()

View file

@ -1,4 +1,6 @@
use futures::executor::block_on;
use futures::{
channel::mpsc::UnboundedSender, executor::block_on, future::BoxFuture, FutureExt, StreamExt,
};
use num_enum::TryFromPrimitive;
use pinnacle_api_defs::pinnacle::input::{
self,
@ -8,14 +10,11 @@ use pinnacle_api_defs::pinnacle::input::{
SetKeybindRequest, SetLibinputSettingRequest, SetMousebindRequest, SetRepeatRateRequest,
},
};
use tokio_stream::StreamExt;
use tonic::transport::Channel;
use xkbcommon::xkb::Keysym;
pub use pinnacle_api_defs::pinnacle::input::v0alpha1::SetXkbConfigRequest as XkbConfig;
use crate::FutSender;
use self::libinput::LibinputSetting;
pub mod libinput;
@ -48,13 +47,21 @@ pub enum MouseEdge {
#[derive(Debug, Clone)]
pub struct Input {
client: InputServiceClient<Channel>,
fut_sender: FutSender,
// client: InputServiceClient<Channel>,
channel: Channel,
fut_sender: UnboundedSender<BoxFuture<'static, ()>>,
}
impl Input {
pub fn new(client: InputServiceClient<Channel>, fut_sender: FutSender) -> Self {
Self { client, fut_sender }
pub fn new(channel: Channel, fut_sender: UnboundedSender<BoxFuture<'static, ()>>) -> Self {
Self {
channel,
fut_sender,
}
}
fn create_input_client(&self) -> InputServiceClient<Channel> {
InputServiceClient::new(self.channel.clone())
}
pub fn keybind(
@ -63,12 +70,13 @@ impl Input {
key: impl Key + Send + 'static,
mut action: impl FnMut() + Send + 'static,
) {
let mut client = self.client.clone();
let mut client = self.create_input_client();
let modifiers = mods.into_iter().map(|modif| modif as i32).collect();
self.fut_sender
.send(Box::pin(async move {
.unbounded_send(
async move {
let mut stream = client
.set_keybind(SetKeybindRequest {
modifiers,
@ -83,7 +91,9 @@ impl Input {
while let Some(Ok(_response)) = stream.next().await {
action();
}
}))
}
.boxed(),
)
.unwrap();
}
@ -94,11 +104,13 @@ impl Input {
edge: MouseEdge,
mut action: impl FnMut() + 'static + Send,
) {
let mut client = self.client.clone();
let mut client = self.create_input_client();
let modifiers = mods.into_iter().map(|modif| modif as i32).collect();
tokio::spawn(async move {
self.fut_sender
.unbounded_send(
async move {
let mut stream = client
.set_mousebind(SetMousebindRequest {
modifiers,
@ -112,17 +124,20 @@ impl Input {
while let Some(Ok(_response)) = stream.next().await {
action();
}
});
}
.boxed(),
)
.unwrap();
}
pub fn set_xkb_config(&self, xkb_config: XkbConfig) {
let mut client = self.client.clone();
let mut client = self.create_input_client();
block_on(client.set_xkb_config(xkb_config)).unwrap();
}
pub fn set_repeat_rate(&self, rate: i32, delay: i32) {
let mut client = self.client.clone();
let mut client = self.create_input_client();
block_on(client.set_repeat_rate(SetRepeatRateRequest {
rate: Some(rate),
@ -132,7 +147,7 @@ impl Input {
}
pub fn set_libinput_setting(&self, setting: LibinputSetting) {
let mut client = self.client.clone();
let mut client = self.create_input_client();
let setting = match setting {
LibinputSetting::AccelProfile(profile) => Setting::AccelProfile(profile as i32),

View file

@ -2,23 +2,15 @@
use std::sync::OnceLock;
use futures::{executor::block_on, future::BoxFuture, stream::FuturesUnordered, StreamExt};
use futures::{
channel::mpsc::UnboundedReceiver, future::BoxFuture, stream::FuturesUnordered, StreamExt,
};
use input::Input;
use output::Output;
use pinnacle::Pinnacle;
use pinnacle_api_defs::pinnacle::{
input::v0alpha1::input_service_client::InputServiceClient,
output::v0alpha1::output_service_client::OutputServiceClient,
process::v0alpha1::process_service_client::ProcessServiceClient,
tag::v0alpha1::tag_service_client::TagServiceClient,
v0alpha1::pinnacle_service_client::PinnacleServiceClient,
window::v0alpha1::window_service_client::WindowServiceClient,
};
use process::Process;
use tag::Tag;
use tokio::{net::UnixStream, sync::mpsc::UnboundedSender};
use tokio_stream::wrappers::UnboundedReceiverStream;
use tonic::transport::{Channel, Endpoint, Uri};
use tonic::transport::{Endpoint, Uri};
use tower::service_fn;
use window::Window;
@ -31,6 +23,7 @@ pub mod util;
pub mod window;
pub use pinnacle_api_macros::config;
pub use xkbcommon;
static PINNACLE: OnceLock<Pinnacle> = OnceLock::new();
static PROCESS: OnceLock<Process> = OnceLock::new();
@ -39,30 +32,37 @@ static INPUT: OnceLock<Input> = OnceLock::new();
static OUTPUT: OnceLock<Output> = OnceLock::new();
static TAG: OnceLock<Tag> = OnceLock::new();
pub(crate) type FutSender = UnboundedSender<BoxFuture<'static, ()>>;
#[derive(Debug, Clone)]
pub struct ApiModules {
pub pinnacle: &'static Pinnacle,
pub process: &'static Process,
pub window: &'static Window,
pub input: &'static Input,
pub output: &'static Output,
pub tag: &'static Tag,
}
pub fn create_modules(
) -> Result<(ApiModules, UnboundedReceiverStream<BoxFuture<'static, ()>>), Box<dyn std::error::Error>>
{
let channel = connect()?;
pub async fn connect(
) -> Result<(ApiModules, UnboundedReceiver<BoxFuture<'static, ()>>), Box<dyn std::error::Error>> {
let channel = Endpoint::try_from("http://[::]:50051")? // port doesn't matter, we use a unix socket
.connect_with_connector(service_fn(|_: Uri| {
tokio::net::UnixStream::connect(
std::env::var("PINNACLE_GRPC_SOCKET")
.expect("PINNACLE_GRPC_SOCKET was not set; is Pinnacle running?"),
)
}))
.await?;
let pinnacle_client = PinnacleServiceClient::new(channel.clone());
let window_client = WindowServiceClient::new(channel.clone());
let input_client = InputServiceClient::new(channel.clone());
let output_client = OutputServiceClient::new(channel.clone());
let tag_client = TagServiceClient::new(channel.clone());
let process_client = ProcessServiceClient::new(channel.clone());
let (fut_sender, fut_recv) = futures::channel::mpsc::unbounded::<BoxFuture<()>>();
let (fut_sender, fut_receiver) = tokio::sync::mpsc::unbounded_channel::<BoxFuture<()>>();
let output = Output::new(channel.clone(), fut_sender.clone());
let fut_receiver = UnboundedReceiverStream::new(fut_receiver);
let pinnacle = PINNACLE.get_or_init(|| Pinnacle::new(pinnacle_client));
let process = PROCESS.get_or_init(|| Process::new(process_client, fut_sender.clone()));
let window = WINDOW.get_or_init(|| Window::new(window_client, tag_client.clone()));
let input = INPUT.get_or_init(|| Input::new(input_client, fut_sender.clone()));
let output = OUTPUT.get_or_init(|| Output::new(output_client, tag_client.clone()));
let tag = TAG.get_or_init(|| Tag::new(tag_client));
let pinnacle = PINNACLE.get_or_init(|| Pinnacle::new(channel.clone()));
let process = PROCESS.get_or_init(|| Process::new(channel.clone(), fut_sender.clone()));
let window = WINDOW.get_or_init(|| Window::new(channel.clone()));
let input = INPUT.get_or_init(|| Input::new(channel.clone(), fut_sender.clone()));
let tag = TAG.get_or_init(|| Tag::new(channel.clone(), fut_sender.clone()));
let output = OUTPUT.get_or_init(|| output);
let modules = ApiModules {
pinnacle,
@ -73,26 +73,24 @@ pub fn create_modules(
tag,
};
Ok((modules, fut_receiver))
Ok((modules, fut_recv))
}
pub fn listen(
fut_receiver: UnboundedReceiverStream<BoxFuture<()>>,
// api_modules: ApiModules<'a>,
pub async fn listen(
fut_recv: UnboundedReceiver<BoxFuture<'static, ()>>, // api_modules: ApiModules<'a>,
) {
let mut future_set = FuturesUnordered::<
BoxFuture<(
Option<BoxFuture<()>>,
Option<UnboundedReceiverStream<BoxFuture<()>>>,
Option<UnboundedReceiver<BoxFuture<()>>>,
)>,
>::new();
future_set.push(Box::pin(async move {
let (fut, stream) = fut_receiver.into_future().await;
let (fut, stream) = fut_recv.into_future().await;
(fut, Some(stream))
}));
block_on(async move {
while let Some((fut, stream)) = future_set.next().await {
if let Some(fut) = fut {
future_set.push(Box::pin(async move {
@ -107,29 +105,4 @@ pub fn listen(
}))
}
}
});
}
// #[derive(Debug, Clone)]
pub struct ApiModules {
pub pinnacle: &'static Pinnacle,
pub process: &'static Process,
pub window: &'static Window,
pub input: &'static Input,
pub output: &'static Output,
pub tag: &'static Tag,
}
pub fn connect() -> Result<Channel, Box<dyn std::error::Error>> {
block_on(async {
Endpoint::try_from("http://[::]:50051")? // port doesn't matter, we use a unix socket
.connect_with_connector(service_fn(|_: Uri| {
UnixStream::connect(
std::env::var("PINNACLE_GRPC_SOCKET")
.expect("PINNACLE_GRPC_SOCKET was not set; is Pinnacle running?"),
)
}))
.await
})
.map_err(|err| err.into())
}

View file

@ -1,4 +1,6 @@
use futures::executor::block_on;
use futures::{
channel::mpsc::UnboundedSender, executor::block_on, future::BoxFuture, FutureExt, StreamExt,
};
use pinnacle_api_defs::pinnacle::{
output::{
self,
@ -8,28 +10,37 @@ use pinnacle_api_defs::pinnacle::{
},
tag::v0alpha1::tag_service_client::TagServiceClient,
};
use tokio_stream::StreamExt;
use tonic::transport::Channel;
use crate::tag::TagHandle;
#[derive(Debug, Clone)]
pub struct Output {
client: OutputServiceClient<Channel>,
tag_client: TagServiceClient<Channel>,
// client: OutputServiceClient<Channel>,
// tag_client: TagServiceClient<Channel>,
channel: Channel,
fut_sender: UnboundedSender<BoxFuture<'static, ()>>,
}
impl Output {
pub fn new(
client: OutputServiceClient<Channel>,
tag_client: TagServiceClient<Channel>,
) -> Self {
Self { client, tag_client }
pub fn new(channel: Channel, fut_sender: UnboundedSender<BoxFuture<'static, ()>>) -> Self {
Self {
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())
}
pub fn get_all(&self) -> impl Iterator<Item = OutputHandle> {
let mut client = self.client.clone();
let tag_client = self.tag_client.clone();
let mut client = self.create_output_client();
let tag_client = self.create_tag_client();
block_on(client.get(output::v0alpha1::GetRequest {}))
.unwrap()
.into_inner()
@ -52,10 +63,12 @@ impl Output {
for_all(output);
}
let mut client = self.client.clone();
let tag_client = self.tag_client.clone();
let mut client = self.create_output_client();
let tag_client = self.create_tag_client();
tokio::spawn(async move {
self.fut_sender
.unbounded_send(
async move {
let mut stream = client
.connect_for_all(ConnectForAllRequest {})
.await
@ -75,14 +88,18 @@ impl Output {
for_all(output);
}
});
}
.boxed(),
)
.unwrap();
}
}
#[derive(Clone, Debug)]
pub struct OutputHandle {
client: OutputServiceClient<Channel>,
tag_client: TagServiceClient<Channel>,
name: String,
pub(crate) client: OutputServiceClient<Channel>,
pub(crate) tag_client: TagServiceClient<Channel>,
pub(crate) name: String,
}
impl OutputHandle {
@ -126,6 +143,7 @@ impl OutputHandle {
.into_iter()
.map(|id| TagHandle {
client: self.tag_client.clone(),
output_client: self.client.clone(),
id,
})
.collect(),

View file

@ -6,16 +6,20 @@ use tonic::transport::Channel;
#[derive(Debug, Clone)]
pub struct Pinnacle {
client: PinnacleServiceClient<Channel>,
channel: Channel,
}
impl Pinnacle {
pub fn new(client: PinnacleServiceClient<Channel>) -> Self {
Self { client }
pub fn new(channel: Channel) -> Self {
Self { channel }
}
fn create_pinnacle_client(&self) -> PinnacleServiceClient<Channel> {
PinnacleServiceClient::new(self.channel.clone())
}
pub fn quit(&self) {
let mut client = self.client.clone();
let mut client = self.create_pinnacle_client();
block_on(client.quit(QuitRequest {})).unwrap();
}
}

View file

@ -1,15 +1,13 @@
use futures::{channel::mpsc::UnboundedSender, future::BoxFuture, FutureExt, StreamExt};
use pinnacle_api_defs::pinnacle::process::v0alpha1::{
process_service_client::ProcessServiceClient, SpawnRequest,
};
use tokio_stream::StreamExt;
use tonic::transport::Channel;
use crate::FutSender;
#[derive(Debug, Clone)]
pub struct Process {
client: ProcessServiceClient<Channel>,
fut_sender: FutSender,
channel: Channel,
fut_sender: UnboundedSender<BoxFuture<'static, ()>>,
}
pub struct SpawnCallbacks {
@ -19,8 +17,15 @@ pub struct SpawnCallbacks {
}
impl Process {
pub fn new(client: ProcessServiceClient<Channel>, fut_sender: FutSender) -> Process {
Self { client, fut_sender }
pub fn new(channel: Channel, fut_sender: UnboundedSender<BoxFuture<'static, ()>>) -> Process {
Self {
channel,
fut_sender,
}
}
fn create_process_client(&self) -> ProcessServiceClient<Channel> {
ProcessServiceClient::new(self.channel.clone())
}
pub fn spawn(&self, args: impl IntoIterator<Item = impl Into<String>>) {
@ -53,12 +58,9 @@ impl Process {
once: bool,
callbacks: Option<SpawnCallbacks>,
) {
let mut client = self.client.clone();
let mut client = self.create_process_client();
let args = args
.into_iter()
.map(|arg| Into::<String>::into(arg))
.collect::<Vec<_>>();
let args = args.into_iter().map(Into::into).collect::<Vec<_>>();
let request = SpawnRequest {
args,
@ -67,7 +69,8 @@ impl Process {
};
self.fut_sender
.send(Box::pin(async move {
.unbounded_send(
async move {
let mut stream = client.spawn(request).await.unwrap().into_inner();
let Some(mut callbacks) = callbacks else { return };
while let Some(Ok(response)) = stream.next().await {
@ -87,29 +90,9 @@ impl Process {
}
}
}
}))
}
.boxed(),
)
.unwrap();
// tokio::spawn(async move {
// let mut stream = client.spawn(request).await.unwrap().into_inner();
// let Some(mut callbacks) = callbacks else { return };
// while let Some(Ok(response)) = stream.next().await {
// if let Some(line) = response.stdout {
// if let Some(stdout) = callbacks.stdout.as_mut() {
// stdout(line);
// }
// }
// if let Some(line) = response.stderr {
// if let Some(stderr) = callbacks.stderr.as_mut() {
// stderr(line);
// }
// }
// if let Some(exit_msg) = response.exit_message {
// if let Some(exit) = callbacks.exit.as_mut() {
// exit(response.exit_code, exit_msg);
// }
// }
// }
// });
}
}

View file

@ -1,27 +1,104 @@
use futures::executor::block_on;
use futures::{channel::mpsc::UnboundedSender, executor::block_on, future::BoxFuture};
use num_enum::TryFromPrimitive;
use pinnacle_api_defs::pinnacle::tag::{
use pinnacle_api_defs::pinnacle::{
output::v0alpha1::output_service_client::OutputServiceClient,
tag::{
self,
v0alpha1::{
tag_service_client::TagServiceClient, RemoveRequest, SetActiveRequest, SetLayoutRequest,
tag_service_client::TagServiceClient, AddRequest, RemoveRequest, SetActiveRequest,
SetLayoutRequest, SwitchToRequest,
},
},
};
use tonic::transport::Channel;
use crate::output::{Output, OutputHandle};
#[derive(Clone, Debug)]
pub struct Tag {
client: TagServiceClient<Channel>,
channel: Channel,
fut_sender: UnboundedSender<BoxFuture<'static, ()>>,
}
impl Tag {
pub fn new(client: TagServiceClient<Channel>) -> Self {
Self { client }
pub fn new(channel: Channel, fut_sender: UnboundedSender<BoxFuture<'static, ()>>) -> Self {
Self {
channel,
fut_sender,
}
}
pub fn create_tag_client(&self) -> TagServiceClient<Channel> {
TagServiceClient::new(self.channel.clone())
}
pub fn create_output_client(&self) -> OutputServiceClient<Channel> {
OutputServiceClient::new(self.channel.clone())
}
pub fn add(
&self,
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();
let tag_names = tag_names.into_iter().map(Into::into).collect();
let response = block_on(client.add(AddRequest {
output_name: Some(output.name.clone()),
tag_names,
}))
.unwrap()
.into_inner();
response.tag_ids.into_iter().map(move |id| TagHandle {
client: client.clone(),
output_client: output_client.clone(),
id,
})
}
pub fn get_all(&self) -> impl Iterator<Item = TagHandle> {
let mut client = self.create_tag_client();
let output_client = self.create_output_client();
let response = block_on(client.get(tag::v0alpha1::GetRequest {}))
.unwrap()
.into_inner();
response.tag_ids.into_iter().map(move |id| TagHandle {
client: client.clone(),
output_client: output_client.clone(),
id,
})
}
pub fn get(&self, name: impl Into<String>, output: Option<&OutputHandle>) -> Option<TagHandle> {
let name = name.into();
let output_module = Output::new(self.channel.clone(), self.fut_sender.clone());
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.name)
== output
.map(|o| o.name.clone())
.or_else(|| output_module.get_focused().map(|o| o.name))
});
same_tag_name && same_output
})
}
}
#[derive(Debug, Clone)]
pub struct TagHandle {
pub(crate) client: TagServiceClient<Channel>,
pub(crate) output_client: OutputServiceClient<Channel>,
pub(crate) id: u32,
}
@ -72,13 +149,38 @@ impl TagHandle {
.unwrap();
}
pub fn switch_to(&self) {
let mut client = self.client.clone();
block_on(client.switch_to(SwitchToRequest {
tag_id: Some(self.id),
}))
.unwrap();
}
pub fn props(&self) -> TagProperties {
todo!()
let mut client = self.client.clone();
let output_client = self.output_client.clone();
let response = block_on(client.get_properties(tag::v0alpha1::GetPropertiesRequest {
tag_id: Some(self.id),
}))
.unwrap()
.into_inner();
TagProperties {
active: response.active,
name: response.name,
output: response.output_name.map(|name| OutputHandle {
client: output_client,
tag_client: client,
name,
}),
}
}
}
pub struct TagProperties {
pub active: Option<bool>,
pub name: Option<String>,
pub output: (),
pub output: Option<OutputHandle>,
}

View file

@ -1,9 +1,10 @@
use futures::executor::block_on;
use num_enum::TryFromPrimitive;
use pinnacle_api_defs::pinnacle::{
output::v0alpha1::output_service_client::OutputServiceClient,
tag::v0alpha1::tag_service_client::TagServiceClient,
window::v0alpha1::{
window_service_client::WindowServiceClient, MoveToTagRequest, SetTagRequest,
window_service_client::WindowServiceClient, CloseRequest, MoveToTagRequest, SetTagRequest,
},
window::{
self,
@ -19,22 +20,47 @@ use crate::{input::MouseButton, tag::TagHandle, util::Geometry};
#[derive(Debug, Clone)]
pub struct Window {
client: WindowServiceClient<Channel>,
tag_client: TagServiceClient<Channel>,
channel: Channel,
}
impl Window {
pub fn new(
client: WindowServiceClient<Channel>,
tag_client: TagServiceClient<Channel>,
) -> Self {
Self { client, tag_client }
pub fn new(channel: Channel) -> Self {
Self { channel }
}
pub fn create_window_client(&self) -> WindowServiceClient<Channel> {
WindowServiceClient::new(self.channel.clone())
}
pub fn create_tag_client(&self) -> TagServiceClient<Channel> {
TagServiceClient::new(self.channel.clone())
}
pub fn create_output_client(&self) -> OutputServiceClient<Channel> {
OutputServiceClient::new(self.channel.clone())
}
pub fn begin_move(&self, button: MouseButton) {
let mut client = self.create_window_client();
block_on(client.move_grab(MoveGrabRequest {
button: Some(button as u32),
}))
.unwrap();
}
pub fn begin_resize(&self, button: MouseButton) {
let mut client = self.create_window_client();
block_on(client.resize_grab(ResizeGrabRequest {
button: Some(button as u32),
}))
.unwrap();
}
/// Get all windows.
pub fn get_all(&self) -> impl Iterator<Item = WindowHandle> {
let mut client = self.client.clone();
let tag_client = self.tag_client.clone();
let mut client = self.create_window_client();
let tag_client = self.create_tag_client();
let output_client = self.create_output_client();
block_on(client.get(GetRequest {}))
.unwrap()
.into_inner()
@ -44,6 +70,7 @@ impl Window {
client: client.clone(),
id,
tag_client: tag_client.clone(),
output_client: output_client.clone(),
})
}
@ -59,6 +86,7 @@ pub struct WindowHandle {
pub(crate) client: WindowServiceClient<Channel>,
pub(crate) id: u32,
pub(crate) tag_client: TagServiceClient<Channel>,
pub(crate) output_client: OutputServiceClient<Channel>,
}
#[repr(i32)]
@ -81,7 +109,14 @@ pub struct WindowProperties {
}
impl WindowHandle {
pub fn set_fullscreen(&mut self, set: bool) {
pub fn close(mut self) {
block_on(self.client.close(CloseRequest {
window_id: Some(self.id),
}))
.unwrap();
}
pub fn set_fullscreen(&self, set: bool) {
let mut client = self.client.clone();
block_on(client.set_fullscreen(SetFullscreenRequest {
window_id: Some(self.id),
@ -92,7 +127,7 @@ impl WindowHandle {
.unwrap();
}
pub fn toggle_fullscreen(&mut self) {
pub fn toggle_fullscreen(&self) {
let mut client = self.client.clone();
block_on(client.set_fullscreen(SetFullscreenRequest {
window_id: Some(self.id),
@ -101,7 +136,7 @@ impl WindowHandle {
.unwrap();
}
pub fn set_maximized(&mut self, set: bool) {
pub fn set_maximized(&self, set: bool) {
let mut client = self.client.clone();
block_on(client.set_maximized(SetMaximizedRequest {
window_id: Some(self.id),
@ -112,7 +147,7 @@ impl WindowHandle {
.unwrap();
}
pub fn toggle_maximized(&mut self) {
pub fn toggle_maximized(&self) {
let mut client = self.client.clone();
block_on(client.set_maximized(SetMaximizedRequest {
window_id: Some(self.id),
@ -121,7 +156,7 @@ impl WindowHandle {
.unwrap();
}
pub fn set_floating(&mut self, set: bool) {
pub fn set_floating(&self, set: bool) {
let mut client = self.client.clone();
block_on(client.set_floating(SetFloatingRequest {
window_id: Some(self.id),
@ -132,7 +167,7 @@ impl WindowHandle {
.unwrap();
}
pub fn toggle_floating(&mut self) {
pub fn toggle_floating(&self) {
let mut client = self.client.clone();
block_on(client.set_floating(SetFloatingRequest {
window_id: Some(self.id),
@ -175,22 +210,6 @@ impl WindowHandle {
.unwrap();
}
pub fn begin_move(&self, button: MouseButton) {
let mut client = self.client.clone();
block_on(client.move_grab(MoveGrabRequest {
button: Some(button as u32),
}))
.unwrap();
}
pub fn begin_resize(&self, button: MouseButton) {
let mut client = self.client.clone();
block_on(client.resize_grab(ResizeGrabRequest {
button: Some(button as u32),
}))
.unwrap();
}
pub fn props(&self) -> WindowProperties {
let mut client = self.client.clone();
let tag_client = self.tag_client.clone();
@ -227,6 +246,7 @@ impl WindowHandle {
.into_iter()
.map(|id| TagHandle {
client: tag_client.clone(),
output_client: self.output_client.clone(),
id,
})
.collect(),