This commit is contained in:
2025-02-10 10:20:13 +01:00
parent 33593c8175
commit 613f66abea
3 changed files with 55 additions and 68 deletions

View File

@@ -171,11 +171,9 @@ pub async fn playlist_set_looping(mpv: Mpv, r#loop: bool) -> anyhow::Result<()>
.map_err(|e| e.into()) .map_err(|e| e.into())
} }
use swayipc::{Connection, Fallible}; // pub async fn sway_run_command(command: String) -> anyhow::Result<()> {
// tokio::task::spawn_blocking(move || -> anyhow::Result<()> {
// pub async fn sway_run_command(command: String) -> Fallible<()> { // let mut connection = swayipc::Connection::new()?;
// tokio::task::spawn_blocking(move || -> Fallible<()> {
// let mut connection = Connection::new()?;
// connection.run_command(&command)?; // connection.run_command(&command)?;
// Ok(()) // Ok(())
// }) // })
@@ -184,21 +182,21 @@ use swayipc::{Connection, Fallible};
// } // }
//only to check if workspace exists. //only to check if workspace exists.
fn get_workspace_names(connection: &mut Connection) -> Fallible<Vec<String>> { fn get_workspace_names(connection: &mut swayipc::Connection) -> anyhow::Result<Vec<String>> {
let workspaces = connection.get_workspaces()?; let workspaces = connection.get_workspaces()?;
Ok(workspaces.iter().map(|w| w.name.clone()).collect()) Ok(workspaces.iter().map(|w| w.name.clone()).collect())
} }
pub async fn sway_get_workspace_names() -> Fallible<Vec<String>> { pub async fn sway_get_workspace_names() -> anyhow::Result<Vec<String>> {
tokio::task::spawn_blocking(|| -> Fallible<Vec<String>> { tokio::task::spawn_blocking(|| -> anyhow::Result<Vec<String>> {
let mut connection = Connection::new()?; let mut connection = swayipc::Connection::new()?;
get_workspace_names(&mut connection) get_workspace_names(&mut connection)
}) })
.await .await
.map_err(|e| swayipc::Error::CommandFailed(e.to_string()))? .map_err(|e| swayipc::Error::CommandFailed(e.to_string()))?
} }
fn is_valid_workspace(workspace: &str, connection: &mut Connection) -> Fallible<bool> { fn is_valid_workspace(workspace: &str, connection: &mut swayipc::Connection) -> anyhow::Result<bool> {
let workspace_names = get_workspace_names(connection)?; let workspace_names = get_workspace_names(connection)?;
Ok(workspace_names.contains(&workspace.to_string()) || Ok(workspace_names.contains(&workspace.to_string()) ||
workspace.parse::<u32>() workspace.parse::<u32>()
@@ -206,14 +204,12 @@ fn is_valid_workspace(workspace: &str, connection: &mut Connection) -> Fallible<
.unwrap_or(false)) .unwrap_or(false))
} }
pub async fn sway_change_workspace(workspace: String) -> Fallible<()> { pub async fn sway_change_workspace(workspace: String) -> anyhow::Result<()> {
tokio::task::spawn_blocking(move || -> Fallible<()> { tokio::task::spawn_blocking(move || -> anyhow::Result<()> {
let mut connection = Connection::new()?; let mut connection = swayipc::Connection::new()?;
if !is_valid_workspace(&workspace, &mut connection)? { if !is_valid_workspace(&workspace, &mut connection)? {
return Err(swayipc::Error::CommandFailed( anyhow::bail!("Invalid workspace name");
"Invalid workspace name. Must be existing workspace or number 1-10".to_string()
));
} }
connection.run_command(&format!("workspace {}", workspace))?; connection.run_command(&format!("workspace {}", workspace))?;
@@ -225,18 +221,18 @@ pub async fn sway_change_workspace(workspace: String) -> Fallible<()> {
use url::Url; use url::Url;
pub async fn sway_launch_browser(url: &str) -> Fallible<()> { pub async fn sway_launch_browser(url: &str) -> anyhow::Result<()> {
// Validate URL // Validate URL
let url = Url::parse(url) let url = Url::parse(url)
.map_err(|e| swayipc::Error::CommandFailed(format!("Invalid URL: {}", e)))?; .map_err(|e| swayipc::Error::CommandFailed(format!("Invalid URL: {}", e)))?;
// Ensure URL scheme is http or https // Ensure URL scheme is http or https
if url.scheme() != "http" && url.scheme() != "https" { if url.scheme() != "http" && url.scheme() != "https" {
return Err(swayipc::Error::CommandFailed("URL must use http or https protocol".into())); anyhow::bail!("URL must use http or https protocol");
} }
tokio::task::spawn_blocking(move || -> Fallible<()> { tokio::task::spawn_blocking(move || -> anyhow::Result<()> {
let mut connection = Connection::new()?; let mut connection = swayipc::Connection::new()?;
// connection.run_command(&format!("exec xdg-open {}", url))?; // connection.run_command(&format!("exec xdg-open {}", url))?;
// connection.run_command(&format!("exec firefox --kiosk {}", url))?; //moved to firefox to pin in kiosk mode. potentially add --new-window // connection.run_command(&format!("exec firefox --kiosk {}", url))?; //moved to firefox to pin in kiosk mode. potentially add --new-window
@@ -250,15 +246,13 @@ pub async fn sway_launch_browser(url: &str) -> Fallible<()> {
.map_err(|e| swayipc::Error::CommandFailed(e.to_string()))? .map_err(|e| swayipc::Error::CommandFailed(e.to_string()))?
} }
pub async fn sway_close_workspace(workspace: String) -> Fallible<()> { pub async fn sway_close_workspace(workspace: String) -> anyhow::Result<()> {
tokio::task::spawn_blocking(move || -> Fallible<()> { tokio::task::spawn_blocking(move || -> anyhow::Result<()> {
let mut connection = Connection::new()?; let mut connection = swayipc::Connection::new()?;
// Validate workspace exists // Validate workspace exists
if !is_valid_workspace(&workspace, &mut connection)? { if !is_valid_workspace(&workspace, &mut connection)? {
return Err(swayipc::Error::CommandFailed( anyhow::bail!("Invalid workspace name");
"Invalid workspace name".to_string()
));
} }
// // Get workspace tree and find all nodes in target workspace // // Get workspace tree and find all nodes in target workspace
@@ -316,24 +310,22 @@ lazy_static! {
static ref CLEANUP_PATTERN: Regex = Regex::new(r"[^0-9: \t]").unwrap(); static ref CLEANUP_PATTERN: Regex = Regex::new(r"[^0-9: \t]").unwrap();
} }
fn validate_keypress_string(input: &str) -> Fallible<String> { fn validate_keypress_string(input: &str) -> anyhow::Result<String> {
let cleaned = CLEANUP_PATTERN.replace_all(input, "").to_string(); let cleaned = CLEANUP_PATTERN.replace_all(input, "").to_string();
let cleaned = cleaned.trim(); let cleaned = cleaned.trim();
if !KEYPRESS_PATTERN.is_match(cleaned) { if !KEYPRESS_PATTERN.is_match(cleaned) {
return Err(swayipc::Error::CommandFailed( anyhow::bail!("Invalid keypress string");
"Invalid keypress format. Expected 'number:1 number:0'".into()
));
} }
Ok(cleaned.to_string()) Ok(cleaned.to_string())
} }
//to simulate keypresses 42:1 38:1 38:0 24:1 24:0 38:1 38:0 42:0 -> LOL //to simulate keypresses 42:1 38:1 38:0 24:1 24:0 38:1 38:0 42:0 -> LOL
pub async fn input(keys: String) -> Fallible<()> { pub async fn input(keys: String) -> anyhow::Result<()> {
let validated_input = validate_keypress_string(&keys)?; let validated_input = validate_keypress_string(&keys)?;
tokio::task::spawn_blocking(move || -> Fallible<()> { tokio::task::spawn_blocking(move || -> anyhow::Result<()> {
let mut connection = Connection::new()?; let mut connection = swayipc::Connection::new()?;
connection.run_command(&format!("exec ydotool key {}", validated_input))?; connection.run_command(&format!("exec ydotool key {}", validated_input))?;
// instead of running through swaycmf // instead of running through swaycmf
Ok(()) Ok(())
@@ -343,9 +335,9 @@ pub async fn input(keys: String) -> Fallible<()> {
} }
// simulate mouse movement // simulate mouse movement
pub async fn mouse_move(x: i32, y: i32) -> Fallible<()> { pub async fn mouse_move(x: i32, y: i32) -> anyhow::Result<()> {
tokio::task::spawn_blocking(move || -> Fallible<()> { tokio::task::spawn_blocking(move || -> anyhow::Result<()> {
let mut connection = Connection::new()?; let mut connection = swayipc::Connection::new()?;
connection.run_command(&format!("exec ydotool mousemove -x {} -y {}", x, y))?; connection.run_command(&format!("exec ydotool mousemove -x {} -y {}", x, y))?;
Ok(()) Ok(())
}) })
@@ -355,9 +347,9 @@ pub async fn mouse_move(x: i32, y: i32) -> Fallible<()> {
//simulate scroll //simulate scroll
pub async fn mouse_scroll(x: i32, y: i32) -> Fallible<()> { pub async fn mouse_scroll(x: i32, y: i32) -> anyhow::Result<()> {
tokio::task::spawn_blocking(move || -> Fallible<()> { tokio::task::spawn_blocking(move || -> anyhow::Result<()> {
let mut connection = Connection::new()?; let mut connection = swayipc::Connection::new()?;
connection.run_command(&format!("exec ydotool mousemove -w -x {} -y {}", x, y))?; connection.run_command(&format!("exec ydotool mousemove -w -x {} -y {}", x, y))?;
Ok(()) Ok(())
}) })
@@ -391,15 +383,17 @@ impl MouseButton {
} }
} }
fn from_str(s: &str) -> Fallible<Self> { fn from_str(s: &str) -> anyhow::Result<Self> {
match s.to_uppercase().as_str() { match s.to_uppercase().as_str() {
"LEFT" => Ok(MouseButton::Left),
"RIGHT" => Ok(MouseButton::Right),
"MIDDLE" => Ok(MouseButton::Middle), "MIDDLE" => Ok(MouseButton::Middle),
"SIDE" => Ok(MouseButton::Side), "SIDE" => Ok(MouseButton::Side),
"EXTRA" => Ok(MouseButton::Extra), "EXTRA" => Ok(MouseButton::Extra),
"FORWARD" => Ok(MouseButton::Forward), "FORWARD" => Ok(MouseButton::Forward),
"BACK" => Ok(MouseButton::Back), "BACK" => Ok(MouseButton::Back),
"TASK" => Ok(MouseButton::Task), "TASK" => Ok(MouseButton::Task),
_ => Err(swayipc::Error::CommandFailed(format!("Invalid mouse button: {}", s))), _ => anyhow::bail!("Invalid mouse button"),
} }
} }
@@ -408,12 +402,12 @@ impl MouseButton {
} }
} }
pub async fn mouse_click(button: String) -> Fallible<()> { pub async fn mouse_click(button: String) -> anyhow::Result<()> {
let mouse_button = MouseButton::from_str(&button)?; let mouse_button = MouseButton::from_str(&button)?;
let click_value = mouse_button.click_value(); let click_value = mouse_button.click_value();
tokio::task::spawn_blocking(move || -> Fallible<()> { tokio::task::spawn_blocking(move || -> anyhow::Result<()> {
let mut connection = Connection::new()?; let mut connection = swayipc::Connection::new()?;
connection.run_command(&format!("exec ydotool click {:#04x}", click_value))?; connection.run_command(&format!("exec ydotool click {:#04x}", click_value))?;
Ok(()) Ok(())
}) })

View File

@@ -11,7 +11,7 @@ use serde_json::{json, Value};
use utoipa::OpenApi; use utoipa::OpenApi;
use utoipa_axum::{router::OpenApiRouter, routes}; use utoipa_axum::{router::OpenApiRouter, routes};
use utoipa_swagger_ui::SwaggerUi; use utoipa_swagger_ui::SwaggerUi;
use futures::FutureExt; ///use futures::FutureExt;
use super::base; use super::base;
@@ -413,10 +413,10 @@ async fn playlist_set_looping(
} }
#[derive(serde::Deserialize, utoipa::ToSchema)] // #[derive(serde::Deserialize, utoipa::ToSchema)]
struct SwayCommandBody { // struct SwayCommandBody {
command: String, // command: String,
} // }
// #[utoipa::path( // #[utoipa::path(
// post, // post,
@@ -446,7 +446,7 @@ struct SwayBrowserBody {
) )
)] )]
async fn sway_launch_browser_handler(Json(body): Json<SwayBrowserBody>) -> RestResponse { async fn sway_launch_browser_handler(Json(body): Json<SwayBrowserBody>) -> RestResponse {
base::sway_launch_browser(&body.url).await.map_err(anyhow::Error::new).into() base::sway_launch_browser(&body.url).await.into()
} }
#[derive(serde::Deserialize, utoipa::ToSchema)] #[derive(serde::Deserialize, utoipa::ToSchema)]
@@ -464,7 +464,7 @@ struct SwayWorkspaceBody {
) )
)] )]
async fn sway_close_workspace_handler(Json(body): Json<SwayWorkspaceBody>) -> RestResponse { async fn sway_close_workspace_handler(Json(body): Json<SwayWorkspaceBody>) -> RestResponse {
base::sway_close_workspace(body.workspace).await.map_err(anyhow::Error::new).into() base::sway_close_workspace(body.workspace).await.into()
} }
#[utoipa::path( #[utoipa::path(
@@ -477,7 +477,7 @@ async fn sway_close_workspace_handler(Json(body): Json<SwayWorkspaceBody>) -> Re
) )
)] )]
async fn sway_change_workspace_handler(Json(body): Json<SwayWorkspaceBody>) -> RestResponse { async fn sway_change_workspace_handler(Json(body): Json<SwayWorkspaceBody>) -> RestResponse {
base::sway_change_workspace(body.workspace).await.map_err(anyhow::Error::new).into() base::sway_change_workspace(body.workspace).await.into()
} }
#[utoipa::path( #[utoipa::path(
@@ -491,7 +491,6 @@ async fn sway_change_workspace_handler(Json(body): Json<SwayWorkspaceBody>) -> R
async fn sway_get_workspace_names_handler() -> RestResponse { async fn sway_get_workspace_names_handler() -> RestResponse {
base::sway_get_workspace_names().await base::sway_get_workspace_names().await
.map(|workspaces| json!(workspaces)) .map(|workspaces| json!(workspaces))
.map_err(anyhow::Error::new)
.into() .into()
} }
@@ -519,7 +518,6 @@ async fn input_handler(
base::input(payload.keys) base::input(payload.keys)
.await .await
.map(|_| json!({})) .map(|_| json!({}))
.map_err(anyhow::Error::new)
.into() .into()
} }
@@ -546,7 +544,6 @@ async fn mouse_move_handler(
base::mouse_move(payload.x, payload.y) base::mouse_move(payload.x, payload.y)
.await .await
.map(|_| json!({})) .map(|_| json!({}))
.map_err(anyhow::Error::new)
.into() .into()
} }
@@ -565,7 +562,6 @@ async fn mouse_scroll_handler(
base::mouse_scroll(payload.x, payload.y) base::mouse_scroll(payload.x, payload.y)
.await .await
.map(|_| json!({})) .map(|_| json!({}))
.map_err(anyhow::Error::new)
.into() .into()
} }
@@ -590,6 +586,5 @@ async fn mouse_click_handler(
base::mouse_click(payload.button) base::mouse_click(payload.button)
.await .await
.map(|_| json!({})) .map(|_| json!({}))
.map_err(anyhow::Error::new)
.into() .into()
} }

View File

@@ -481,29 +481,27 @@ async fn handle_message(
base::input(keys) base::input(keys)
.await .await
.map(|_| json!({})) .map(|_| json!({}))
.map_err(anyhow::Error::new)?; .context("Failed to execute input command")?;
Ok(None) Ok(None)
} }
WSCommand::MouseMove { x, y } => { WSCommand::MouseMove { x, y } => {
base::mouse_move(x, y) let _ = base::mouse_move(x, y)
.await .await
.map(|_| json!({})) .map(|_| json!({}));
.map_err(anyhow::Error::new)?;
Ok(None) Ok(None)
} }
WSCommand::MouseScroll { x, y } => { WSCommand::MouseScroll { x, y } => {
base::mouse_scroll(x, y) let _ = base::mouse_scroll(x, y)
.await .await
.map(|_| json!({})) .map(|_| json!({}));
.map_err(anyhow::Error::new)?;
Ok(None) Ok(None)
} }
WSCommand::MouseClick { button } => { WSCommand::MouseClick { button } => {
base::mouse_click(button) let _ = base::mouse_click(button)
.await .await
.map(|_| json!({})) .map(|_| json!({}));
.map_err(anyhow::Error::new)?;
Ok(None) Ok(None)
} }
} }
} }