Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
822f559
Start Linux multi-cursor plumbing
codex Jun 9, 2026
c1d96e1
Key Linux overlay state by cursor
codex Jun 9, 2026
a5eab26
Fix Linux keyed overlay build
codex Jun 9, 2026
8565d4c
Restore Linux click tool arg helpers
codex Jun 9, 2026
60a4f66
Restore Linux double-click arg helpers
codex Jun 9, 2026
4b9cbc1
Restore Linux right-click arg helpers
codex Jun 9, 2026
22ed9c2
Import Linux tool arg helpers once
codex Jun 9, 2026
925dcb1
Tighten Linux multi-cursor tool validation
codex Jun 9, 2026
f9e2b63
Show Linux cursor state during drags
codex Jun 9, 2026
cdcdbc2
Animate Linux drag overlay in motion engine
codex Jun 9, 2026
6255ede
Add Linux MPX parallel drag tool
codex Jun 9, 2026
09703d4
Fix x11 feature flags for MPX tool
codex Jun 9, 2026
a9b93db
Fix MPX tool FFI usage
codex Jun 9, 2026
d2f6a13
Use XTEST slave devices for Linux MPX drags
codex Jun 9, 2026
6f8b530
Initialize Xlib threading for Linux MPX drags
codex Jun 9, 2026
8297eaa
Fail MPX drags on Xtigervnc and clean up masters
codex Jun 9, 2026
f9bc0e2
Detect Xtigervnc by process for MPX guard
codex Jun 9, 2026
d9874e4
Fix MPX drag delivery: overlay click-through, warp sync, flat accel
codex Jun 9, 2026
76a3963
Track parallel drags with the agent cursor overlay
codex Jun 9, 2026
a712567
Restore the active window after parallel drags
codex Jun 10, 2026
56962a6
Stamp focus-restore activation with real server time
codex Jun 10, 2026
3e1cd1f
Clean up drag masters per call and resync wedged WM focus state
codex Jun 10, 2026
1608bd9
Shield parallel drags from the WM with a device-specific grab + replay
codex Jun 10, 2026
0ac9a51
Replay shield presses per-device to fix dropped concurrent presses
codex Jun 10, 2026
97d5b26
fix(nix): update cua-driver cargoHash for new dependency set
r33drichards Jun 10, 2026
3370796
fix(nix): add pkg-config and X11 libs for the x11 crate build
r33drichards Jun 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 87 additions & 1 deletion libs/cua-driver/rust/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions libs/cua-driver/rust/crates/cursor-overlay/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -283,8 +283,12 @@ pub enum OverlayMsg {
pub enum OverlayCommand {
/// Animate the cursor to a new screen position.
MoveTo { x: f64, y: f64, end_heading_radians: f64 },
/// Snap the cursor immediately to a screen position, optionally updating heading.
SnapTo { x: f64, y: f64, heading_radians: Option<f64> },
/// Start the click-press visual.
ClickPulse { x: f64, y: f64 },
/// Toggle the held-button visual state.
SetPressed(bool),
/// Show or hide the overlay.
SetEnabled(bool),
/// Update the motion/timing config live.
Expand Down
70 changes: 68 additions & 2 deletions libs/cua-driver/rust/crates/cursor-overlay/src/render_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
//!
//! - [`RenderStateCore`] — the platform-agnostic animation fields
//! (`cfg`, `palette`, `motion`, `pos`, `heading`, `path`, `dist`, `spring`,
//! `spring_tgt`, `click_t`, `shape`, `visible`, `idle_secs`, `idle_alpha`,
//! `spring_tgt`, `click_t`, `pressed`, `shape`, `visible`, `idle_secs`, `idle_alpha`,
//! `pinned_wid`, `gradient_colors`, `bloom_override`).
//! - [`RenderStateCore::tick_motion`] — speed-profile + spring physics +
//! click-pulse + idle-fade using runtime [`MotionConfig`] (Windows + Linux).
Expand Down Expand Up @@ -68,6 +68,8 @@ pub struct RenderStateCore {
pub spring_tgt: Option<(f64, f64, f64)>,
/// Click-pulse phase 0..1; `None` = no pulse in flight.
pub click_t: Option<f64>,
/// Whether a button is currently being held for this cursor.
pub pressed: bool,
/// Custom cursor shape; `None` = built-in gradient arrow.
pub shape: Option<CursorShape>,
/// User-controlled visibility.
Expand Down Expand Up @@ -108,6 +110,7 @@ impl RenderStateCore {
spring: None,
spring_tgt: None,
click_t: None,
pressed: false,
visible: true,
idle_secs: 0.0,
idle_alpha: 1.0,
Expand Down Expand Up @@ -417,6 +420,23 @@ impl RenderStateCore {
self.idle_alpha = 1.0;
true
}
OverlayCommand::SnapTo {
x,
y,
heading_radians,
} => {
self.pos = (x, y);
if let Some(heading) = heading_radians {
self.heading = heading;
}
self.path = None;
self.dist = 0.0;
self.spring = None;
self.spring_tgt = None;
self.idle_secs = 0.0;
self.idle_alpha = 1.0;
true
}
OverlayCommand::ClickPulse { x, y } => {
if click_pulse_sentinel_only {
// macOS: only snap position on first placement (sentinel state).
Expand All @@ -438,6 +458,12 @@ impl RenderStateCore {
self.idle_alpha = 1.0;
true
}
OverlayCommand::SetPressed(v) => {
self.pressed = v;
self.idle_secs = 0.0;
self.idle_alpha = 1.0;
true
}
OverlayCommand::SetEnabled(v) => {
self.visible = v;
true
Expand Down Expand Up @@ -534,7 +560,7 @@ pub fn paint_cursor(
let alpha_scale = core.idle_alpha as f32;

// --- Bloom (radial gradient behind the arrow) ---
let bloom_r: f32 = 22.0;
let bloom_r: f32 = if core.pressed { 34.0 } else { 22.0 };
// Use runtime bloom_override if set, otherwise fall back to palette.
let (br, bg, bb) = if let Some([r, g, b, _]) = core.bloom_override {
(r, g, b)
Expand Down Expand Up @@ -580,6 +606,46 @@ pub fn paint_cursor(
pm.fill_rect(r, &bloom_paint, tiny_skia::Transform::identity(), None);
}

if core.pressed {
let [pr, pg, pb, _] = core.palette.cursor_mid;
let ring_color =
tiny_skia::Color::from_rgba8(pr, pg, pb, (210.0 * alpha_scale) as u8);
let mut ring_paint = tiny_skia::Paint::default();
ring_paint.shader = tiny_skia::Shader::SolidColor(ring_color);
ring_paint.anti_alias = true;
let stroke = tiny_skia::Stroke {
width: 3.0,
..Default::default()
};
let core_fill =
tiny_skia::Color::from_rgba8(pr, pg, pb, (110.0 * alpha_scale) as u8);
let mut fill_paint = tiny_skia::Paint::default();
fill_paint.shader = tiny_skia::Shader::SolidColor(core_fill);
fill_paint.anti_alias = true;
let mut pb = tiny_skia::PathBuilder::new();
pb.push_circle(px as f32, py as f32, 6.5);
if let Some(path) = pb.finish() {
pm.fill_path(
&path,
&fill_paint,
tiny_skia::FillRule::Winding,
tiny_skia::Transform::identity(),
None,
);
}
let mut pb = tiny_skia::PathBuilder::new();
pb.push_circle(px as f32, py as f32, 13.0);
if let Some(path) = pb.finish() {
pm.stroke_path(
&path,
&ring_paint,
&stroke,
tiny_skia::Transform::identity(),
None,
);
}
}

// --- Focus rect highlight (macOS only — others pass None) ---
// Cyan glow border + faint fill, matching Swift AgentCursor.showFocusRect.
if let Some(fr) = focus_rect {
Expand Down
2 changes: 2 additions & 0 deletions libs/cua-driver/rust/crates/platform-linux/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ tiny-skia = { version = "0.11", default-features = false, features = ["std"] }
[target.'cfg(target_os = "linux")'.dependencies]
# X11 background input + window enumeration
x11rb = { version = "0.13", features = ["xinput", "randr", "xfixes", "composite", "shape", "xtest"] }
x11 = { version = "2.21", features = ["xlib", "xinput", "xtest"] }
base64 = { workspace = true }
image = { workspace = true }
# kill(2) for the kill_app tool — SIGKILL via libc::kill.
Expand All @@ -29,3 +30,4 @@ libc = "0.2"
# typelibs at runtime. `tokio` matches the driver's async runtime; `zbus`
# re-exports the bus types we need (fdo::DBusProxy for pid resolution).
atspi = { version = "0.30", features = ["tokio", "zbus"] }
evdev = "0.12"
Loading
Loading