2025-02-04 00:19:30 +03:00
|
|
|
use super::{
|
|
|
|
KeyboardLayoutClient, KeyboardLayoutUpdate, Visibility, Workspace, WorkspaceClient,
|
|
|
|
WorkspaceUpdate,
|
|
|
|
};
|
|
|
|
use crate::{await_sync, error, send, spawn};
|
|
|
|
use swayipc_async::{InputChange, InputEvent, Node, WorkspaceChange, WorkspaceEvent};
|
2024-08-05 09:22:01 -03:00
|
|
|
use tokio::sync::broadcast::{channel, Receiver};
|
2023-01-27 20:08:14 +00:00
|
|
|
|
2024-08-05 09:22:01 -03:00
|
|
|
use crate::clients::sway::Client;
|
2023-01-27 20:08:14 +00:00
|
|
|
|
2024-01-07 23:50:10 +00:00
|
|
|
impl WorkspaceClient for Client {
|
2025-02-04 00:19:30 +03:00
|
|
|
fn focus(&self, id: String) {
|
|
|
|
let client = self.connection().clone();
|
|
|
|
spawn(async move {
|
|
|
|
let mut client = client.lock().await;
|
|
|
|
if let Err(e) = client.run_command(format!("workspace {id}")).await {
|
|
|
|
error!("Couldn't focus workspace '{id}': {e:#}");
|
|
|
|
}
|
|
|
|
});
|
2023-01-27 20:08:14 +00:00
|
|
|
}
|
|
|
|
|
2025-02-04 00:19:30 +03:00
|
|
|
fn subscribe(&self) -> Receiver<WorkspaceUpdate> {
|
2024-08-05 09:22:01 -03:00
|
|
|
let (tx, rx) = channel(16);
|
2023-01-27 20:08:14 +00:00
|
|
|
|
2024-08-05 09:22:01 -03:00
|
|
|
let client = self.connection().clone();
|
2024-01-07 23:50:10 +00:00
|
|
|
|
2024-08-05 09:22:01 -03:00
|
|
|
await_sync(async {
|
|
|
|
let mut client = client.lock().await;
|
|
|
|
let workspaces = client.get_workspaces().await.expect("to get workspaces");
|
2023-01-27 20:08:14 +00:00
|
|
|
|
2024-08-05 09:22:01 -03:00
|
|
|
let event =
|
|
|
|
WorkspaceUpdate::Init(workspaces.into_iter().map(Workspace::from).collect());
|
2023-01-27 20:08:14 +00:00
|
|
|
|
2024-08-05 09:22:01 -03:00
|
|
|
send!(tx, event);
|
|
|
|
|
|
|
|
drop(client);
|
|
|
|
|
|
|
|
self.add_listener::<swayipc_async::WorkspaceEvent>(move |event| {
|
|
|
|
let update = WorkspaceUpdate::from(event.clone());
|
|
|
|
send!(tx, update);
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
.expect("to add listener");
|
|
|
|
});
|
2023-01-27 20:08:14 +00:00
|
|
|
|
|
|
|
rx
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<Node> for Workspace {
|
|
|
|
fn from(node: Node) -> Self {
|
2023-08-24 21:18:07 -05:00
|
|
|
let visibility = Visibility::from(&node);
|
|
|
|
|
2023-01-27 20:08:14 +00:00
|
|
|
Self {
|
2024-05-09 17:25:08 +01:00
|
|
|
id: node.id,
|
2023-01-27 20:08:14 +00:00
|
|
|
name: node.name.unwrap_or_default(),
|
|
|
|
monitor: node.output.unwrap_or_default(),
|
2023-08-24 21:18:07 -05:00
|
|
|
visibility,
|
2023-01-27 20:08:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<swayipc_async::Workspace> for Workspace {
|
|
|
|
fn from(workspace: swayipc_async::Workspace) -> Self {
|
2023-08-24 21:18:07 -05:00
|
|
|
let visibility = Visibility::from(&workspace);
|
|
|
|
|
2023-01-27 20:08:14 +00:00
|
|
|
Self {
|
2024-05-09 17:25:08 +01:00
|
|
|
id: workspace.id,
|
2023-01-27 20:08:14 +00:00
|
|
|
name: workspace.name,
|
|
|
|
monitor: workspace.output,
|
2023-08-24 21:18:07 -05:00
|
|
|
visibility,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<&Node> for Visibility {
|
|
|
|
fn from(node: &Node) -> Self {
|
|
|
|
if node.focused {
|
|
|
|
Self::focused()
|
|
|
|
} else if node.visible.unwrap_or(false) {
|
|
|
|
Self::visible()
|
|
|
|
} else {
|
|
|
|
Self::Hidden
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<&swayipc_async::Workspace> for Visibility {
|
|
|
|
fn from(workspace: &swayipc_async::Workspace) -> Self {
|
|
|
|
if workspace.focused {
|
|
|
|
Self::focused()
|
|
|
|
} else if workspace.visible {
|
|
|
|
Self::visible()
|
|
|
|
} else {
|
|
|
|
Self::Hidden
|
2023-01-27 20:08:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<WorkspaceEvent> for WorkspaceUpdate {
|
|
|
|
fn from(event: WorkspaceEvent) -> Self {
|
|
|
|
match event.change {
|
|
|
|
WorkspaceChange::Init => {
|
|
|
|
Self::Add(event.current.expect("Missing current workspace").into())
|
|
|
|
}
|
2024-05-09 17:25:08 +01:00
|
|
|
WorkspaceChange::Empty => {
|
|
|
|
Self::Remove(event.current.expect("Missing current workspace").id)
|
|
|
|
}
|
2023-01-27 20:08:14 +00:00
|
|
|
WorkspaceChange::Focus => Self::Focus {
|
2023-08-24 21:18:07 -05:00
|
|
|
old: event.old.map(Workspace::from),
|
|
|
|
new: Workspace::from(event.current.expect("Missing current workspace")),
|
2023-01-27 20:08:14 +00:00
|
|
|
},
|
|
|
|
WorkspaceChange::Move => {
|
|
|
|
Self::Move(event.current.expect("Missing current workspace").into())
|
|
|
|
}
|
2024-12-05 07:04:22 -05:00
|
|
|
WorkspaceChange::Rename => {
|
|
|
|
if let Some(node) = event.current {
|
|
|
|
Self::Rename {
|
|
|
|
id: node.id,
|
|
|
|
name: node.name.unwrap_or_default(),
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Self::Unknown
|
|
|
|
}
|
|
|
|
}
|
2024-09-25 22:38:14 -03:00
|
|
|
WorkspaceChange::Urgent => {
|
|
|
|
if let Some(node) = event.current {
|
|
|
|
Self::Urgent {
|
|
|
|
id: node.id,
|
|
|
|
urgent: node.urgent,
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Self::Unknown
|
|
|
|
}
|
|
|
|
}
|
2023-12-31 00:43:36 +00:00
|
|
|
_ => Self::Unknown,
|
2023-01-27 20:08:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2025-02-04 00:19:30 +03:00
|
|
|
|
|
|
|
impl KeyboardLayoutClient for Client {
|
|
|
|
fn set_next_active(&self) {
|
|
|
|
let client = self.connection().clone();
|
|
|
|
spawn(async move {
|
|
|
|
let mut client = client.lock().await;
|
|
|
|
|
|
|
|
let inputs = client.get_inputs().await.expect("to get inputs");
|
|
|
|
|
|
|
|
if let Some(keyboard) = inputs
|
|
|
|
.into_iter()
|
|
|
|
.find(|i| i.xkb_active_layout_name.is_some())
|
|
|
|
{
|
|
|
|
if let Err(e) = client
|
|
|
|
.run_command(format!(
|
|
|
|
"input {} xkb_switch_layout next",
|
|
|
|
keyboard.identifier
|
|
|
|
))
|
|
|
|
.await
|
|
|
|
{
|
|
|
|
error!("Failed to switch keyboard layout due to Sway error: {e}");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
error!("Failed to get keyboard identifier from Sway");
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
fn subscribe(&self) -> Receiver<KeyboardLayoutUpdate> {
|
|
|
|
let (tx, rx) = channel(4);
|
|
|
|
|
|
|
|
let client = self.connection().clone();
|
|
|
|
|
|
|
|
await_sync(async {
|
|
|
|
let mut client = client.lock().await;
|
|
|
|
let inputs = client.get_inputs().await.expect("to get inputs");
|
|
|
|
|
|
|
|
if let Some(layout) = inputs.into_iter().find_map(|i| i.xkb_active_layout_name) {
|
|
|
|
send!(tx, KeyboardLayoutUpdate(layout));
|
|
|
|
} else {
|
|
|
|
error!("Failed to get keyboard layout from Sway!");
|
|
|
|
}
|
|
|
|
|
|
|
|
drop(client);
|
|
|
|
|
|
|
|
self.add_listener::<InputEvent>(move |event| {
|
|
|
|
if let Ok(layout) = KeyboardLayoutUpdate::try_from(event.clone()) {
|
|
|
|
send!(tx, layout);
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
.expect("to add listener");
|
|
|
|
});
|
|
|
|
|
|
|
|
rx
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TryFrom<InputEvent> for KeyboardLayoutUpdate {
|
|
|
|
type Error = ();
|
|
|
|
|
|
|
|
fn try_from(value: InputEvent) -> std::result::Result<Self, Self::Error> {
|
|
|
|
match value.change {
|
|
|
|
InputChange::XkbLayout => {
|
|
|
|
if let Some(layout) = value.input.xkb_active_layout_name {
|
|
|
|
Ok(KeyboardLayoutUpdate(layout))
|
|
|
|
} else {
|
|
|
|
Err(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => Err(()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|