hyprshell_exec_lib/
switch.rs1use anyhow::Context;
2use core_lib::Warn;
3use hyprland::data::{Client, Monitors, Workspace, Workspaces};
4use hyprland::dispatch::{
5 Dispatch, DispatchType, WindowIdentifier, WorkspaceIdentifierWithSpecial,
6};
7use hyprland::prelude::*;
8use hyprland::shared::{Address, WorkspaceId};
9use tracing::{debug, trace};
10
11pub fn switch_client(address: Address) -> anyhow::Result<()> {
12 debug!("execute switch to client: {address}");
13 deactivate_special_workspace_if_needed().warn();
14 Dispatch::call(DispatchType::FocusWindow(WindowIdentifier::Address(
15 address,
16 )))?;
17 Dispatch::call(DispatchType::BringActiveToTop)?;
18 Ok(())
19}
20
21pub fn switch_client_by_initial_class(class: &str) -> anyhow::Result<()> {
22 debug!("execute switch to client: {class} by initial_class");
23 deactivate_special_workspace_if_needed().warn();
24 Dispatch::call(DispatchType::FocusWindow(
25 WindowIdentifier::ClassRegularExpression(&format!(
26 "initialclass:{}",
27 class.to_ascii_lowercase()
28 )),
29 ))?;
30 Dispatch::call(DispatchType::BringActiveToTop)?;
31 Ok(())
32}
33
34pub fn switch_workspace(workspace_id: WorkspaceId) -> anyhow::Result<()> {
35 deactivate_special_workspace_if_needed().warn();
36
37 let current_workspace = Workspace::get_active();
39 if let Ok(workspace) = current_workspace {
40 if workspace_id == workspace.id {
41 trace!("Already on workspace {}", workspace_id);
42 return Ok(());
43 }
44 }
45
46 if workspace_id < 0 {
47 switch_special_workspace(workspace_id).with_context(|| {
48 format!("Failed to execute switch special workspace with id {workspace_id}")
49 })?;
50 } else {
51 switch_normal_workspace(workspace_id).with_context(|| {
52 format!("Failed to execute switch workspace with id {workspace_id}")
53 })?;
54 }
55 Ok(())
56}
57
58fn switch_special_workspace(workspace_id: WorkspaceId) -> anyhow::Result<()> {
59 let special = Monitors::get()?
60 .into_iter()
61 .find(|m| m.special_workspace.id == workspace_id);
62 if let Some(special) = special {
63 trace!("Special workspace already toggled: {special:?}");
64 return Ok(());
65 }
66 let ws = Workspaces::get()?
67 .into_iter()
68 .find(|w| w.id == workspace_id)
69 .context("workspace not found")?;
70 Dispatch::call(DispatchType::ToggleSpecialWorkspace(Some(
71 ws.name.trim_start_matches("special:").to_string(),
72 )))
73 .context("failed to execute toggle special workspace")
74}
75
76fn switch_normal_workspace(workspace_id: WorkspaceId) -> anyhow::Result<()> {
77 debug!("execute switch to workspace {workspace_id}");
78 Dispatch::call(DispatchType::Workspace(WorkspaceIdentifierWithSpecial::Id(
79 workspace_id,
80 )))?;
81 Ok(())
82}
83
84fn deactivate_special_workspace_if_needed() -> anyhow::Result<()> {
88 let active_ws = Workspace::get_active()
89 .map(|w| w.name)
90 .context("active workspace failed")?;
91 let active_ws = Client::get_active()
92 .context("active client failed")?
93 .map_or(active_ws, |a| a.workspace.name);
94 trace!("current workspace: {active_ws}");
95 if active_ws.starts_with("special:") {
96 debug!("current client is on special workspace, deactivating special workspace");
97 Dispatch::call(DispatchType::ToggleSpecialWorkspace(Some(
99 active_ws.trim_start_matches("special:").to_string(),
100 )))?;
101 }
102 Ok(())
103}