Skip to content

Commit

Permalink
refactor: move sway::Client out of compositor
Browse files Browse the repository at this point in the history
The module `compositor` is gated behind the `workspaces` feature, but
`sway::Client` is no longer used only by the `workspaces` module.
  • Loading branch information
Rodrigodd committed Aug 4, 2024
1 parent a376d6a commit 911545d
Show file tree
Hide file tree
Showing 3 changed files with 192 additions and 181 deletions.
184 changes: 6 additions & 178 deletions src/clients/compositor/sway.rs
Original file line number Diff line number Diff line change
@@ -1,121 +1,15 @@
use super::{Visibility, Workspace, WorkspaceClient, WorkspaceUpdate};
use crate::{await_sync, send, spawn};
use color_eyre::{Report, Result};
use futures_lite::StreamExt;
use std::sync::Arc;
use swayipc_async::{Connection, Event, EventType, Node, WorkspaceChange, WorkspaceEvent};
use crate::{await_sync, send};
use color_eyre::Result;
use swayipc_async::{Node, WorkspaceChange, WorkspaceEvent};
use tokio::sync::broadcast::{channel, Receiver};
use tokio::sync::Mutex;
use tracing::{info, trace};

type SyncFn<T> = dyn Fn(&T) + Sync + Send;

struct TaskState {
join_handle: Option<tokio::task::JoinHandle<Result<()>>>,
// could have been a `HashMap<EventType, Vec<Box<dyn Fn(&Event) + Sync + Send>>>`, but we don't
// expect enough listeners to justify the constant overhead of a hashmap.
listeners: Arc<Vec<(EventType, Box<SyncFn<Event>>)>>,
}

pub struct Client {
client: Arc<Mutex<Connection>>,
task_state: Mutex<TaskState>,
}

impl std::fmt::Debug for Client {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Client")
.field("client", &"Connection")
.field("task_state", &format_args!("<...>"))
.finish()
}
}

impl Client {
pub(crate) async fn new() -> Result<Self> {
// Avoid using `arc_mut!` here because we need tokio Mutex.
let client = Arc::new(Mutex::new(Connection::new().await?));
info!("Sway IPC subscription client connected");

Ok(Self {
client,
task_state: Mutex::new(TaskState {
listeners: Arc::new(Vec::new()),
join_handle: None,
}),
})
}

pub async fn add_listener<T: SwayIpcEvent>(
&self,
f: impl Fn(&T) + Sync + Send + 'static,
) -> Result<()> {
self.add_listener_type(
T::EVENT_TYPE,
Box::new(move |event| {
let event = T::from_event(event).unwrap();
f(event)
}),
)
.await
}

pub async fn add_listener_type(
&self,
event_type: EventType,
f: Box<SyncFn<Event>>,
) -> Result<()> {
// abort current running task
let TaskState {
join_handle,
listeners,
} = &mut *self.task_state.lock().await;

if let Some(handle) = join_handle.take() {
handle.abort();
let _ = handle.await;
}

// Only the task and self have a reference to listeners, and we just abort the task. This
// is the only reference to listeners, so we can safely get a mutable reference.
let listeners_mut = Arc::get_mut(listeners)
.ok_or_else(|| Report::msg("Failed to get mutable reference to listeners"))?;

listeners_mut.push((event_type, f));

// create new client as subscription takes ownership
let client = Connection::new().await?;

let event_types = listeners.iter().map(|(t, _)| *t).collect::<Vec<_>>();
let listeners = listeners.clone();

let handle = spawn(async move {
let mut events = client.subscribe(&event_types).await?;

while let Some(event) = events.next().await {
trace!("event: {:?}", event);
let event = event?;
let ty = sway_event_to_event_type(&event);
for (t, f) in listeners.iter() {
if *t == ty {
f(&event);
}
}
}

Ok::<(), Report>(())
});

*join_handle = Some(handle);

Ok(())
}
}
use crate::clients::sway::Client;

impl WorkspaceClient for Client {
fn focus(&self, id: String) -> Result<()> {
await_sync(async move {
let mut client = self.client.lock().await;
let mut client = self.connection().lock().await;
client.run_command(format!("workspace {id}")).await
})?;
Ok(())
Expand All @@ -124,7 +18,7 @@ impl WorkspaceClient for Client {
fn subscribe_workspace_change(&self) -> Receiver<WorkspaceUpdate> {
let (tx, rx) = channel(16);

let client = self.client.clone();
let client = self.connection().clone();

await_sync(async {
let mut client = client.lock().await;
Expand Down Expand Up @@ -219,69 +113,3 @@ impl From<WorkspaceEvent> for WorkspaceUpdate {
}
}
}

fn sway_event_to_event_type(event: &Event) -> EventType {
match event {
Event::Workspace(_) => EventType::Workspace,
Event::Mode(_) => EventType::Mode,
Event::Window(_) => EventType::Window,
Event::BarConfigUpdate(_) => EventType::BarConfigUpdate,
Event::Binding(_) => EventType::Binding,
Event::Shutdown(_) => EventType::Shutdown,
Event::Tick(_) => EventType::Tick,
Event::BarStateUpdate(_) => EventType::BarStateUpdate,
Event::Input(_) => EventType::Input,
_ => todo!(),
}
}

pub trait SwayIpcEvent {
const EVENT_TYPE: EventType;
fn from_event(e: &Event) -> Option<&Self>;
}
macro_rules! sway_ipc_event_impl {
(@ $($t:tt)*) => { $($t)* };
($t:ty, $v:expr, $($m:tt)*) => {
sway_ipc_event_impl! {@
impl SwayIpcEvent for $t {
const EVENT_TYPE: EventType = $v;
fn from_event(e: &Event) -> Option<&Self> {
match e {
$($m)* (x) => Some(x),
_ => None,
}
}
}
}
};
}

sway_ipc_event_impl!(
swayipc_async::WorkspaceEvent,
EventType::Workspace,
Event::Workspace
);
sway_ipc_event_impl!(swayipc_async::ModeEvent, EventType::Mode, Event::Mode);
sway_ipc_event_impl!(swayipc_async::WindowEvent, EventType::Window, Event::Window);
sway_ipc_event_impl!(
swayipc_async::BarConfig,
EventType::BarConfigUpdate,
Event::BarConfigUpdate
);
sway_ipc_event_impl!(
swayipc_async::BindingEvent,
EventType::Binding,
Event::Binding
);
sway_ipc_event_impl!(
swayipc_async::ShutdownEvent,
EventType::Shutdown,
Event::Shutdown
);
sway_ipc_event_impl!(swayipc_async::TickEvent, EventType::Tick, Event::Tick);
sway_ipc_event_impl!(
swayipc_async::BarStateUpdateEvent,
EventType::BarStateUpdate,
Event::BarStateUpdate
);
sway_ipc_event_impl!(swayipc_async::InputEvent, EventType::Input, Event::Input);
8 changes: 5 additions & 3 deletions src/clients/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ pub mod compositor;
pub mod lua;
#[cfg(feature = "music")]
pub mod music;
#[cfg(feature = "sway")]
pub mod sway;
#[cfg(feature = "notifications")]
pub mod swaync;
#[cfg(feature = "tray")]
Expand All @@ -30,7 +32,7 @@ pub struct Clients {
#[cfg(feature = "workspaces")]
workspaces: Option<Arc<dyn compositor::WorkspaceClient>>,
#[cfg(feature = "sway")]
sway: Option<Arc<compositor::sway::Client>>,
sway: Option<Arc<sway::Client>>,
#[cfg(feature = "clipboard")]
clipboard: Option<Arc<clipboard::Client>>,
#[cfg(feature = "cairo")]
Expand Down Expand Up @@ -84,11 +86,11 @@ impl Clients {
}

#[cfg(feature = "sway")]
pub fn sway(&mut self) -> ClientResult<compositor::sway::Client> {
pub fn sway(&mut self) -> ClientResult<sway::Client> {
let client = match &self.sway {
Some(client) => client.clone(),
None => {
let client = await_sync(async { compositor::sway::Client::new().await })?;
let client = await_sync(async { sway::Client::new().await })?;
let client = Arc::new(client);
self.sway.replace(client.clone());
client
Expand Down
Loading

0 comments on commit 911545d

Please sign in to comment.