2025-02-04 00:19:30 +03:00
|
|
|
use super::{
|
|
|
|
KeyboardLayoutClient, KeyboardLayoutUpdate, Visibility, Workspace, WorkspaceClient,
|
|
|
|
WorkspaceUpdate,
|
|
|
|
};
|
2023-12-17 23:51:43 +00:00
|
|
|
use crate::{arc_mut, lock, send, spawn_blocking};
|
2023-01-30 21:56:41 +00:00
|
|
|
use color_eyre::Result;
|
2025-02-04 00:19:30 +03:00
|
|
|
use hyprland::ctl::switch_xkb_layout;
|
|
|
|
use hyprland::data::{Devices, Workspace as HWorkspace, Workspaces};
|
2023-01-27 20:08:14 +00:00
|
|
|
use hyprland::dispatch::{Dispatch, DispatchType, WorkspaceIdentifierWithSpecial};
|
2023-08-11 21:15:45 +01:00
|
|
|
use hyprland::event_listener::EventListener;
|
2023-01-27 20:08:14 +00:00
|
|
|
use hyprland::prelude::*;
|
2023-10-19 21:11:56 +01:00
|
|
|
use hyprland::shared::{HyprDataVec, WorkspaceType};
|
2023-01-27 20:08:14 +00:00
|
|
|
use tokio::sync::broadcast::{channel, Receiver, Sender};
|
2023-01-30 18:49:30 +00:00
|
|
|
use tracing::{debug, error, info};
|
2023-01-27 20:08:14 +00:00
|
|
|
|
2024-01-07 23:50:10 +00:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct Client {
|
2023-01-27 20:08:14 +00:00
|
|
|
workspace_tx: Sender<WorkspaceUpdate>,
|
|
|
|
_workspace_rx: Receiver<WorkspaceUpdate>,
|
2025-02-04 00:19:30 +03:00
|
|
|
|
|
|
|
keyboard_layout_tx: Sender<KeyboardLayoutUpdate>,
|
|
|
|
_keyboard_layout_rx: Receiver<KeyboardLayoutUpdate>,
|
2023-01-27 20:08:14 +00:00
|
|
|
}
|
|
|
|
|
2024-01-07 23:50:10 +00:00
|
|
|
impl Client {
|
|
|
|
pub(crate) fn new() -> Self {
|
2023-01-27 20:08:14 +00:00
|
|
|
let (workspace_tx, workspace_rx) = channel(16);
|
2025-02-16 18:01:21 -05:00
|
|
|
let (keyboard_layout_tx, keyboard_layout_rx) = channel(16);
|
2023-01-27 20:08:14 +00:00
|
|
|
|
2024-01-07 23:50:10 +00:00
|
|
|
let instance = Self {
|
2023-01-27 20:08:14 +00:00
|
|
|
workspace_tx,
|
|
|
|
_workspace_rx: workspace_rx,
|
2025-02-04 00:19:30 +03:00
|
|
|
keyboard_layout_tx,
|
|
|
|
_keyboard_layout_rx: keyboard_layout_rx,
|
2024-01-07 23:50:10 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
instance.listen_workspace_events();
|
|
|
|
instance
|
2023-01-27 20:08:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn listen_workspace_events(&self) {
|
|
|
|
info!("Starting Hyprland event listener");
|
|
|
|
|
|
|
|
let tx = self.workspace_tx.clone();
|
2025-02-04 00:19:30 +03:00
|
|
|
let keyboard_layout_tx = self.keyboard_layout_tx.clone();
|
2023-01-27 20:08:14 +00:00
|
|
|
|
|
|
|
spawn_blocking(move || {
|
|
|
|
let mut event_listener = EventListener::new();
|
|
|
|
|
2023-01-30 21:56:41 +00:00
|
|
|
// we need a lock to ensure events don't run at the same time
|
2023-06-29 23:16:31 +01:00
|
|
|
let lock = arc_mut!(());
|
2023-01-30 21:56:41 +00:00
|
|
|
|
|
|
|
// cache the active workspace since Hyprland doesn't give us the prev active
|
|
|
|
let active = Self::get_active_workspace().expect("Failed to get active workspace");
|
2023-06-29 23:16:31 +01:00
|
|
|
let active = arc_mut!(Some(active));
|
2023-01-30 21:56:41 +00:00
|
|
|
|
2023-01-27 20:08:14 +00:00
|
|
|
{
|
|
|
|
let tx = tx.clone();
|
2023-01-30 21:56:41 +00:00
|
|
|
let lock = lock.clone();
|
|
|
|
let active = active.clone();
|
2023-01-27 20:08:14 +00:00
|
|
|
|
2023-08-11 21:15:45 +01:00
|
|
|
event_listener.add_workspace_added_handler(move |workspace_type| {
|
2023-01-30 21:56:41 +00:00
|
|
|
let _lock = lock!(lock);
|
2023-01-30 18:49:30 +00:00
|
|
|
debug!("Added workspace: {workspace_type:?}");
|
|
|
|
|
2023-01-30 21:56:41 +00:00
|
|
|
let workspace_name = get_workspace_name(workspace_type);
|
|
|
|
let prev_workspace = lock!(active);
|
2023-01-27 20:08:14 +00:00
|
|
|
|
2023-08-24 21:18:07 -05:00
|
|
|
let workspace = Self::get_workspace(&workspace_name, prev_workspace.as_ref());
|
2023-01-30 21:56:41 +00:00
|
|
|
|
|
|
|
if let Some(workspace) = workspace {
|
|
|
|
send!(tx, WorkspaceUpdate::Add(workspace));
|
|
|
|
}
|
2023-01-27 20:08:14 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
let tx = tx.clone();
|
2023-01-30 21:56:41 +00:00
|
|
|
let lock = lock.clone();
|
|
|
|
let active = active.clone();
|
2023-01-27 20:08:14 +00:00
|
|
|
|
2023-08-11 21:15:45 +01:00
|
|
|
event_listener.add_workspace_change_handler(move |workspace_type| {
|
2023-01-30 21:56:41 +00:00
|
|
|
let _lock = lock!(lock);
|
2023-01-27 20:08:14 +00:00
|
|
|
|
2023-01-30 21:56:41 +00:00
|
|
|
let mut prev_workspace = lock!(active);
|
2023-01-27 20:08:14 +00:00
|
|
|
|
2023-01-30 21:56:41 +00:00
|
|
|
debug!(
|
|
|
|
"Received workspace change: {:?} -> {workspace_type:?}",
|
|
|
|
prev_workspace.as_ref().map(|w| &w.id)
|
|
|
|
);
|
2023-01-27 20:08:14 +00:00
|
|
|
|
2023-01-30 21:56:41 +00:00
|
|
|
let workspace_name = get_workspace_name(workspace_type);
|
2023-08-24 21:18:07 -05:00
|
|
|
let workspace = Self::get_workspace(&workspace_name, prev_workspace.as_ref());
|
2023-01-30 18:49:30 +00:00
|
|
|
|
2023-01-27 20:08:14 +00:00
|
|
|
workspace.map_or_else(
|
2023-01-30 21:56:41 +00:00
|
|
|
|| {
|
|
|
|
error!("Unable to locate workspace");
|
|
|
|
},
|
2023-01-27 20:08:14 +00:00
|
|
|
|workspace| {
|
2023-01-30 21:56:41 +00:00
|
|
|
// there may be another type of update so dispatch that regardless of focus change
|
2023-08-24 21:18:07 -05:00
|
|
|
if !workspace.visibility.is_focused() {
|
2023-01-30 21:56:41 +00:00
|
|
|
Self::send_focus_change(&mut prev_workspace, workspace, &tx);
|
|
|
|
}
|
2023-01-27 20:08:14 +00:00
|
|
|
},
|
|
|
|
);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
let tx = tx.clone();
|
2023-01-30 21:56:41 +00:00
|
|
|
let lock = lock.clone();
|
|
|
|
let active = active.clone();
|
2023-01-27 20:08:14 +00:00
|
|
|
|
2023-08-11 21:15:45 +01:00
|
|
|
event_listener.add_active_monitor_change_handler(move |event_data| {
|
2023-01-30 21:56:41 +00:00
|
|
|
let _lock = lock!(lock);
|
2023-08-11 21:15:45 +01:00
|
|
|
let workspace_type = event_data.workspace;
|
2023-01-27 20:08:14 +00:00
|
|
|
|
2023-01-30 21:56:41 +00:00
|
|
|
let mut prev_workspace = lock!(active);
|
2023-01-27 20:08:14 +00:00
|
|
|
|
2023-01-30 21:56:41 +00:00
|
|
|
debug!(
|
|
|
|
"Received active monitor change: {:?} -> {workspace_type:?}",
|
|
|
|
prev_workspace.as_ref().map(|w| &w.name)
|
2023-01-27 20:08:14 +00:00
|
|
|
);
|
2023-01-30 21:56:41 +00:00
|
|
|
|
|
|
|
let workspace_name = get_workspace_name(workspace_type);
|
2023-08-24 21:18:07 -05:00
|
|
|
let workspace = Self::get_workspace(&workspace_name, prev_workspace.as_ref());
|
2023-01-30 21:56:41 +00:00
|
|
|
|
2023-08-24 21:18:07 -05:00
|
|
|
if let Some((false, workspace)) =
|
|
|
|
workspace.map(|w| (w.visibility.is_focused(), w))
|
|
|
|
{
|
2023-01-30 21:56:41 +00:00
|
|
|
Self::send_focus_change(&mut prev_workspace, workspace, &tx);
|
|
|
|
} else {
|
2024-10-18 23:58:04 +01:00
|
|
|
error!("unable to locate workspace: {workspace_name}");
|
2023-01-30 21:56:41 +00:00
|
|
|
}
|
2023-01-27 20:08:14 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
2023-01-30 21:56:41 +00:00
|
|
|
let tx = tx.clone();
|
|
|
|
let lock = lock.clone();
|
2023-01-27 20:08:14 +00:00
|
|
|
|
2023-08-11 21:15:45 +01:00
|
|
|
event_listener.add_workspace_moved_handler(move |event_data| {
|
2023-01-30 21:56:41 +00:00
|
|
|
let _lock = lock!(lock);
|
2023-08-11 21:15:45 +01:00
|
|
|
let workspace_type = event_data.workspace;
|
2023-01-30 21:56:41 +00:00
|
|
|
debug!("Received workspace move: {workspace_type:?}");
|
2023-01-27 20:08:14 +00:00
|
|
|
|
2023-01-30 21:56:41 +00:00
|
|
|
let mut prev_workspace = lock!(active);
|
2023-01-27 20:08:14 +00:00
|
|
|
|
2023-01-30 21:56:41 +00:00
|
|
|
let workspace_name = get_workspace_name(workspace_type);
|
2023-08-24 21:18:07 -05:00
|
|
|
let workspace = Self::get_workspace(&workspace_name, prev_workspace.as_ref());
|
2023-01-27 20:08:14 +00:00
|
|
|
|
2023-01-30 21:56:41 +00:00
|
|
|
if let Some(workspace) = workspace {
|
|
|
|
send!(tx, WorkspaceUpdate::Move(workspace.clone()));
|
2023-01-27 20:08:14 +00:00
|
|
|
|
2023-08-24 21:18:07 -05:00
|
|
|
if !workspace.visibility.is_focused() {
|
2023-01-30 21:56:41 +00:00
|
|
|
Self::send_focus_change(&mut prev_workspace, workspace, &tx);
|
2023-01-27 20:08:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-01-30 21:56:41 +00:00
|
|
|
{
|
2024-05-06 16:46:26 +01:00
|
|
|
let tx = tx.clone();
|
|
|
|
let lock = lock.clone();
|
|
|
|
|
|
|
|
event_listener.add_workspace_rename_handler(move |data| {
|
2023-01-30 21:56:41 +00:00
|
|
|
let _lock = lock!(lock);
|
2024-10-18 23:58:04 +01:00
|
|
|
debug!("Received workspace rename: {data:?}");
|
2023-01-30 21:56:41 +00:00
|
|
|
|
2024-05-06 16:46:26 +01:00
|
|
|
send!(
|
|
|
|
tx,
|
|
|
|
WorkspaceUpdate::Rename {
|
2024-05-09 17:25:08 +01:00
|
|
|
id: data.workspace_id as i64,
|
2024-05-06 16:46:26 +01:00
|
|
|
name: data.workspace_name
|
|
|
|
}
|
|
|
|
);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
2024-09-25 22:38:14 -03:00
|
|
|
let tx = tx.clone();
|
|
|
|
let lock = lock.clone();
|
|
|
|
|
2024-05-06 16:46:26 +01:00
|
|
|
event_listener.add_workspace_destroy_handler(move |data| {
|
|
|
|
let _lock = lock!(lock);
|
|
|
|
debug!("Received workspace destroy: {data:?}");
|
2024-05-09 17:25:08 +01:00
|
|
|
send!(tx, WorkspaceUpdate::Remove(data.workspace_id as i64));
|
2023-01-30 21:56:41 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2024-09-25 22:38:14 -03:00
|
|
|
{
|
2025-02-04 00:19:30 +03:00
|
|
|
let tx = tx.clone();
|
|
|
|
let lock = lock.clone();
|
|
|
|
|
2024-09-25 22:38:14 -03:00
|
|
|
event_listener.add_urgent_state_handler(move |address| {
|
|
|
|
let _lock = lock!(lock);
|
|
|
|
debug!("Received urgent state: {address:?}");
|
|
|
|
|
|
|
|
let clients = match hyprland::data::Clients::get() {
|
|
|
|
Ok(clients) => clients,
|
|
|
|
Err(err) => {
|
|
|
|
error!("Failed to get clients: {err}");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
clients.iter().find(|c| c.address == address).map_or_else(
|
|
|
|
|| {
|
|
|
|
error!("Unable to locate client");
|
|
|
|
},
|
|
|
|
|c| {
|
|
|
|
send!(
|
|
|
|
tx,
|
|
|
|
WorkspaceUpdate::Urgent {
|
|
|
|
id: c.workspace.id as i64,
|
|
|
|
urgent: true,
|
|
|
|
}
|
|
|
|
);
|
|
|
|
},
|
|
|
|
);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2025-02-04 00:19:30 +03:00
|
|
|
{
|
|
|
|
let tx = keyboard_layout_tx.clone();
|
|
|
|
let lock = lock.clone();
|
|
|
|
|
|
|
|
event_listener.add_keyboard_layout_change_handler(move |layout_event| {
|
|
|
|
let _lock = lock!(lock);
|
|
|
|
|
|
|
|
let layout = if layout_event.layout_name.is_empty() {
|
|
|
|
// FIXME: This field is empty due to bug in `hyprland-rs_0.4.0-alpha.3`. Which is already fixed in last betas
|
|
|
|
|
|
|
|
// The layout may be empty due to a bug in `hyprland-rs`, because of which the `layout_event` is incorrect.
|
|
|
|
//
|
|
|
|
// Instead of:
|
|
|
|
// ```
|
|
|
|
// LayoutEvent {
|
|
|
|
// keyboard_name: "keychron-keychron-c2",
|
|
|
|
// layout_name: "English (US)",
|
|
|
|
// }
|
|
|
|
// ```
|
|
|
|
//
|
|
|
|
// We get:
|
|
|
|
// ```
|
|
|
|
// LayoutEvent {
|
|
|
|
// keyboard_name: "keychron-keychron-c2,English (US)",
|
|
|
|
// layout_name: "",
|
|
|
|
// }
|
|
|
|
// ```
|
|
|
|
//
|
|
|
|
// Here we are trying to recover `layout_name` from `keyboard_name`
|
|
|
|
|
|
|
|
let layout = layout_event.keyboard_name.as_str().split(",").nth(1);
|
|
|
|
let Some(layout) = layout else {
|
|
|
|
error!(
|
|
|
|
"Failed to get layout from string: {}. The failed logic is a workaround for a bug in `hyprland 0.4.0-alpha.3`", layout_event.keyboard_name);
|
|
|
|
return;
|
|
|
|
};
|
|
|
|
|
|
|
|
layout.into()
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
layout_event.layout_name
|
|
|
|
};
|
|
|
|
|
|
|
|
debug!("Received layout: {layout:?}");
|
|
|
|
|
|
|
|
send!(tx, KeyboardLayoutUpdate(layout));
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-01-27 20:08:14 +00:00
|
|
|
event_listener
|
|
|
|
.start_listener()
|
|
|
|
.expect("Failed to start listener");
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-01-30 21:56:41 +00:00
|
|
|
/// Sends a `WorkspaceUpdate::Focus` event
|
|
|
|
/// and updates the active workspace cache.
|
|
|
|
fn send_focus_change(
|
|
|
|
prev_workspace: &mut Option<Workspace>,
|
|
|
|
workspace: Workspace,
|
|
|
|
tx: &Sender<WorkspaceUpdate>,
|
|
|
|
) {
|
|
|
|
send!(
|
|
|
|
tx,
|
|
|
|
WorkspaceUpdate::Focus {
|
2023-08-24 21:18:07 -05:00
|
|
|
old: prev_workspace.take(),
|
|
|
|
new: workspace.clone(),
|
2023-01-30 21:56:41 +00:00
|
|
|
}
|
|
|
|
);
|
2023-01-27 20:08:14 +00:00
|
|
|
|
2024-09-25 22:38:14 -03:00
|
|
|
send!(
|
|
|
|
tx,
|
|
|
|
WorkspaceUpdate::Urgent {
|
|
|
|
id: workspace.id,
|
|
|
|
urgent: false,
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
2023-01-30 21:56:41 +00:00
|
|
|
prev_workspace.replace(workspace);
|
2023-01-27 20:08:14 +00:00
|
|
|
}
|
|
|
|
|
2023-08-24 21:18:07 -05:00
|
|
|
/// Gets a workspace by name from the server, given the active workspace if known.
|
|
|
|
fn get_workspace(name: &str, active: Option<&Workspace>) -> Option<Workspace> {
|
2023-01-30 21:56:41 +00:00
|
|
|
Workspaces::get()
|
|
|
|
.expect("Failed to get workspaces")
|
2024-05-03 15:39:09 -05:00
|
|
|
.into_iter()
|
2023-01-30 21:56:41 +00:00
|
|
|
.find_map(|w| {
|
|
|
|
if w.name == name {
|
2023-08-24 21:18:07 -05:00
|
|
|
let vis = Visibility::from((&w, active.map(|w| w.name.as_ref()), &|w| {
|
|
|
|
create_is_visible()(w)
|
|
|
|
}));
|
|
|
|
|
|
|
|
Some(Workspace::from((vis, w)))
|
2023-01-30 21:56:41 +00:00
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
})
|
2023-01-27 20:08:14 +00:00
|
|
|
}
|
|
|
|
|
2023-01-30 21:56:41 +00:00
|
|
|
/// Gets the active workspace from the server.
|
|
|
|
fn get_active_workspace() -> Result<Workspace> {
|
2023-08-24 21:18:07 -05:00
|
|
|
let w = HWorkspace::get_active().map(|w| Workspace::from((Visibility::focused(), w)))?;
|
2023-01-30 21:56:41 +00:00
|
|
|
Ok(w)
|
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) {
|
2024-02-01 22:39:43 +00:00
|
|
|
let identifier = id.parse::<i32>().map_or_else(
|
|
|
|
|_| WorkspaceIdentifierWithSpecial::Name(&id),
|
|
|
|
WorkspaceIdentifierWithSpecial::Id,
|
|
|
|
);
|
2023-08-11 21:15:45 +01:00
|
|
|
|
2025-02-04 00:19:30 +03:00
|
|
|
if let Err(e) = Dispatch::call(DispatchType::Workspace(identifier)) {
|
|
|
|
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> {
|
2023-01-27 20:08:14 +00:00
|
|
|
let rx = self.workspace_tx.subscribe();
|
|
|
|
|
2025-02-04 00:19:30 +03:00
|
|
|
let active_id = HWorkspace::get_active().ok().map(|active| active.name);
|
|
|
|
let is_visible = create_is_visible();
|
|
|
|
|
|
|
|
let workspaces = Workspaces::get()
|
|
|
|
.expect("Failed to get workspaces")
|
|
|
|
.into_iter()
|
|
|
|
.map(|w| {
|
|
|
|
let vis = Visibility::from((&w, active_id.as_deref(), &is_visible));
|
|
|
|
|
|
|
|
Workspace::from((vis, w))
|
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
send!(self.workspace_tx, WorkspaceUpdate::Init(workspaces));
|
2023-01-27 20:08:14 +00:00
|
|
|
|
2025-02-04 00:19:30 +03:00
|
|
|
rx
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl KeyboardLayoutClient for Client {
|
|
|
|
fn set_next_active(&self) {
|
|
|
|
let device = Devices::get()
|
|
|
|
.expect("Failed to get devices")
|
|
|
|
.keyboards
|
|
|
|
.iter()
|
|
|
|
.find(|k| k.main)
|
|
|
|
.map(|k| k.name.clone());
|
|
|
|
|
|
|
|
if let Some(device) = device {
|
|
|
|
if let Err(e) =
|
|
|
|
switch_xkb_layout::call(device, switch_xkb_layout::SwitchXKBLayoutCmdTypes::Next)
|
|
|
|
{
|
|
|
|
error!("Failed to switch keyboard layout due to Hyprland error: {e}");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
error!("Failed to get keyboard device from hyprland");
|
|
|
|
}
|
|
|
|
}
|
2023-01-27 20:08:14 +00:00
|
|
|
|
2025-02-04 00:19:30 +03:00
|
|
|
fn subscribe(&self) -> Receiver<KeyboardLayoutUpdate> {
|
|
|
|
let rx = self.keyboard_layout_tx.subscribe();
|
2023-08-24 21:18:07 -05:00
|
|
|
|
2025-02-04 00:19:30 +03:00
|
|
|
let layout = Devices::get()
|
|
|
|
.expect("Failed to get devices")
|
|
|
|
.keyboards
|
|
|
|
.iter()
|
|
|
|
.find(|k| k.main)
|
|
|
|
.map(|k| k.active_keymap.clone());
|
2023-01-27 20:08:14 +00:00
|
|
|
|
2025-02-04 00:19:30 +03:00
|
|
|
if let Some(layout) = layout {
|
|
|
|
send!(self.keyboard_layout_tx, KeyboardLayoutUpdate(layout));
|
|
|
|
} else {
|
|
|
|
error!("Failed to get current keyboard layout hyprland");
|
2023-01-27 20:08:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
rx
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-30 21:56:41 +00:00
|
|
|
fn get_workspace_name(name: WorkspaceType) -> String {
|
|
|
|
match name {
|
2023-01-28 15:41:52 +00:00
|
|
|
WorkspaceType::Regular(name) => name,
|
2023-01-27 20:08:14 +00:00
|
|
|
WorkspaceType::Special(name) => name.unwrap_or_default(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-19 21:11:56 +01:00
|
|
|
/// Creates a function which determines if a workspace is visible.
|
|
|
|
///
|
|
|
|
/// This function makes a Hyprland call that allocates so it should be cached when possible,
|
|
|
|
/// but it is only valid so long as workspaces do not change so it should not be stored long term
|
2023-08-24 21:18:07 -05:00
|
|
|
fn create_is_visible() -> impl Fn(&HWorkspace) -> bool {
|
2023-10-19 21:11:56 +01:00
|
|
|
let monitors = hyprland::data::Monitors::get().map_or(Vec::new(), HyprDataVec::to_vec);
|
2023-08-24 21:18:07 -05:00
|
|
|
|
|
|
|
move |w| monitors.iter().any(|m| m.active_workspace.id == w.id)
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<(Visibility, HWorkspace)> for Workspace {
|
|
|
|
fn from((visibility, workspace): (Visibility, HWorkspace)) -> Self {
|
2023-01-27 20:08:14 +00:00
|
|
|
Self {
|
2024-05-09 17:25:08 +01:00
|
|
|
id: workspace.id as i64,
|
2023-01-27 20:08:14 +00:00
|
|
|
name: workspace.name,
|
|
|
|
monitor: workspace.monitor,
|
2023-08-24 21:18:07 -05:00
|
|
|
visibility,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, 'f, F> From<(&'a HWorkspace, Option<&str>, F)> for Visibility
|
|
|
|
where
|
|
|
|
F: FnOnce(&'f HWorkspace) -> bool,
|
|
|
|
'a: 'f,
|
|
|
|
{
|
|
|
|
fn from((workspace, active_name, is_visible): (&'a HWorkspace, Option<&str>, F)) -> Self {
|
|
|
|
if Some(workspace.name.as_str()) == active_name {
|
|
|
|
Self::focused()
|
|
|
|
} else if is_visible(workspace) {
|
|
|
|
Self::visible()
|
|
|
|
} else {
|
|
|
|
Self::Hidden
|
2023-01-27 20:08:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|