Phase 2: snapshot - window tree + screenshot via xcap

- Add xcap and image dependencies
- DesktopBackend trait with all 16 methods for future extensibility
- X11Backend with real snapshot() using xcap Window::all() and
  Monitor::all() for z-ordered window enumeration and screenshot
- Stub implementations for input/window management (phases 4-6)
- Wire X11Backend into DaemonState (now returns Result)
- Real snapshot handler replacing placeholder, updates ref map
This commit is contained in:
Harivansh Rathi 2026-03-24 21:24:34 -04:00
parent dfaa339594
commit 79e6e0e25c
8 changed files with 3041 additions and 34 deletions

View file

@ -1,31 +1,52 @@
use std::sync::Arc;
use tokio::sync::Mutex;
use crate::backend::DesktopBackend;
use crate::core::protocol::{Request, Response};
use crate::core::refs::RefEntry;
use super::state::DaemonState;
pub async fn handle_request(
request: &Request,
_state: &Arc<Mutex<DaemonState>>,
state: &Arc<Mutex<DaemonState>>,
) -> Response {
match request.action.as_str() {
"snapshot" => {
Response::ok(serde_json::json!({
"screenshot": "/tmp/desktop-ctl-placeholder.png",
"windows": [
{
"ref_id": "w1",
"xcb_id": 0,
"title": "Placeholder Window",
"app_name": "placeholder",
"x": 0, "y": 0, "width": 1920, "height": 1080,
"focused": true, "minimized": false
}
]
}))
}
action => {
Response::err(format!("Unknown action: {action}"))
}
"snapshot" => handle_snapshot(request, state).await,
action => Response::err(format!("Unknown action: {action}")),
}
}
async fn handle_snapshot(
request: &Request,
state: &Arc<Mutex<DaemonState>>,
) -> Response {
let annotate = request.extra.get("annotate")
.and_then(|v| v.as_bool())
.unwrap_or(false);
let mut state = state.lock().await;
match state.backend.snapshot(annotate) {
Ok(snapshot) => {
// Update ref map
state.ref_map.clear();
for win in &snapshot.windows {
state.ref_map.insert(RefEntry {
xcb_id: win.xcb_id,
app_class: win.app_name.clone(),
title: win.title.clone(),
pid: 0, // xcap doesn't expose PID directly in snapshot
x: win.x,
y: win.y,
width: win.width,
height: win.height,
focused: win.focused,
minimized: win.minimized,
});
}
Response::ok(serde_json::to_value(&snapshot).unwrap_or_default())
}
Err(e) => Response::err(format!("Snapshot failed: {e}")),
}
}