use std::path::PathBuf;
use std::sync::Arc;
use tokio::sync::RwLock;
pub use egui_mcp_protocol::{MouseButton, Request, Response};
mod server;
pub use server::IpcServer;
#[derive(Debug, Clone)]
pub enum PendingInput {
Click { x: f32, y: f32, button: MouseButton },
MoveMouse { x: f32, y: f32 },
Keyboard { key: String },
Scroll {
x: f32,
y: f32,
delta_x: f32,
delta_y: f32,
},
Drag {
start_x: f32,
start_y: f32,
end_x: f32,
end_y: f32,
button: MouseButton,
},
}
#[derive(Clone)]
pub struct McpClient {
state: Arc<RwLock<ClientState>>,
}
struct ClientState {
socket_path: PathBuf,
screenshot_data: Option<Vec<u8>>,
screenshot_requested: bool,
pending_inputs: Vec<PendingInput>,
}
impl McpClient {
pub fn new() -> Self {
Self::with_socket_path(egui_mcp_protocol::default_socket_path())
}
pub fn with_socket_path(socket_path: PathBuf) -> Self {
Self {
state: Arc::new(RwLock::new(ClientState {
socket_path,
screenshot_data: None,
screenshot_requested: false,
pending_inputs: Vec::new(),
})),
}
}
pub async fn socket_path(&self) -> PathBuf {
self.state.read().await.socket_path.clone()
}
pub async fn set_screenshot(&self, data: Vec<u8>) {
self.state.write().await.screenshot_data = Some(data);
}
pub async fn get_screenshot(&self) -> Option<Vec<u8>> {
self.state.read().await.screenshot_data.clone()
}
pub async fn clear_screenshot(&self) {
self.state.write().await.screenshot_data = None;
}
pub async fn request_screenshot(&self) {
self.state.write().await.screenshot_requested = true;
}
pub async fn take_screenshot_request(&self) -> bool {
let mut state = self.state.write().await;
let requested = state.screenshot_requested;
state.screenshot_requested = false;
requested
}
pub async fn queue_input(&self, input: PendingInput) {
self.state.write().await.pending_inputs.push(input);
}
pub async fn take_pending_inputs(&self) -> Vec<PendingInput> {
std::mem::take(&mut self.state.write().await.pending_inputs)
}
pub fn start_server(&self) -> tokio::task::JoinHandle<()> {
let client = self.clone();
tokio::spawn(async move {
if let Err(e) = IpcServer::run(client).await {
tracing::error!("IPC server error: {}", e);
}
})
}
}
impl Default for McpClient {
fn default() -> Self {
Self::new()
}
}