hyprshell_exec_lib/
switch.rs1use crate::util::to_client_address;
2use anyhow::Context;
3use core_lib::{ClientId, Warn};
4use hyprland::data::{Client, Monitors, Workspace, Workspaces};
5use hyprland::dispatch::{Dispatch, DispatchType};
6use hyprland::prelude::*;
7use hyprland::shared::WorkspaceId;
8use tracing::{debug, instrument, trace};
9
10#[instrument(level = "debug", ret(level = "trace"))]
11pub fn switch_client(address: ClientId) -> anyhow::Result<()> {
12 debug!("execute switch to client: {address}");
13 deactivate_special_workspace_if_needed().warn();
14 let disp = hyprland::dispatch_new::Dispatch::FocusWindow(
15 hyprland::dispatch_new::WindowIdentifier::Address(to_client_address(address)),
16 );
17 disp.apply().context("failed to execute dispatch")?;
18 let disp2 = hyprland::dispatch_new::Dispatch::WindowAlterZ(
19 hyprland::dispatch_new::ZOption::Top,
20 Some(hyprland::dispatch_new::WindowIdentifier::Address(
21 to_client_address(address),
22 )),
23 );
24 disp2.apply().context("failed to execute dispatch2")?;
25 Ok(())
26}
27
28#[instrument(level = "debug", ret(level = "trace"))]
29pub fn switch_client_by_initial_class(class: &str) -> anyhow::Result<()> {
30 debug!("execute switch to client: {class} by initial_class");
31 deactivate_special_workspace_if_needed().warn();
32
33 let disp = hyprland::dispatch_new::Dispatch::FocusWindow(
34 hyprland::dispatch_new::WindowIdentifier::InitialClassRegularExpression(
35 class.to_ascii_lowercase(),
36 ),
37 );
38 disp.apply().context("failed to execute dispatch")?;
39 let disp2 = hyprland::dispatch_new::Dispatch::WindowAlterZ(
40 hyprland::dispatch_new::ZOption::Top,
41 Some(
42 hyprland::dispatch_new::WindowIdentifier::InitialClassRegularExpression(
43 class.to_ascii_lowercase(),
44 ),
45 ),
46 );
47 disp2.apply().context("failed to execute dispatch2")?;
48 Ok(())
49}
50
51#[instrument(level = "debug", ret(level = "trace"))]
52pub fn switch_workspace(workspace_id: WorkspaceId) -> anyhow::Result<()> {
53 deactivate_special_workspace_if_needed().warn();
54
55 let current_workspace = Workspace::get_active();
57 if let Ok(workspace) = current_workspace
58 && workspace_id == workspace.id
59 {
60 trace!("Already on workspace {}", workspace_id);
61 return Ok(());
62 }
63
64 if workspace_id < 0 {
65 switch_special_workspace(workspace_id).with_context(|| {
66 format!("Failed to execute switch special workspace with id {workspace_id}")
67 })?;
68 } else {
69 switch_normal_workspace(workspace_id).with_context(|| {
70 format!("Failed to execute switch workspace with id {workspace_id}")
71 })?;
72 }
73 Ok(())
74}
75
76#[instrument(level = "debug", ret(level = "trace"))]
77fn switch_special_workspace(workspace_id: WorkspaceId) -> anyhow::Result<()> {
78 let special = Monitors::get()?
79 .into_iter()
80 .find(|m| m.special_workspace.id == workspace_id);
81 if let Some(special) = special {
82 trace!("Special workspace already toggled: {special:?}");
83 return Ok(());
84 }
85 let ws = Workspaces::get()?
86 .into_iter()
87 .find(|w| w.id == workspace_id)
88 .context("workspace not found")?;
89
90 let disp = hyprland::dispatch_new::Dispatch::FocusWorkspace(
91 hyprland::dispatch_new::WorkspaceIdentifier::Special(Some(
92 ws.name.trim_start_matches("special:").to_string(),
93 )),
94 false,
95 );
96 disp.apply().context("failed to execute dispatch")?;
97 Ok(())
98}
99
100#[instrument(level = "debug", ret(level = "trace"))]
101fn switch_normal_workspace(workspace_id: WorkspaceId) -> anyhow::Result<()> {
102 debug!("execute switch to workspace {workspace_id}");
103 let disp = hyprland::dispatch_new::Dispatch::FocusWorkspace(
104 hyprland::dispatch_new::WorkspaceIdentifier::Id(workspace_id),
105 false,
106 );
107 disp.apply().context("failed to execute dispatch")?;
108 Ok(())
109}
110
111#[instrument(level = "debug", ret(level = "trace"))]
115fn deactivate_special_workspace_if_needed() -> anyhow::Result<()> {
116 let active_ws = Workspace::get_active()
117 .map(|w| w.name)
118 .context("active workspace failed")?;
119 let active_ws = Client::get_active()
120 .context("active client failed")?
121 .map_or(active_ws, |a| a.workspace.name);
122 trace!("current workspace: {active_ws}");
123 if active_ws.starts_with("special:") {
124 debug!("current client is on special workspace, deactivating special workspace");
125 Dispatch::call(DispatchType::ToggleSpecialWorkspace(Some(
127 active_ws.trim_start_matches("special:").to_string(),
128 )))?;
129 }
130 Ok(())
131}