MCP: given-up stale pointer requests do not use up a frame's request limit

A ui.pointer request made before synthetic input was dropped counted
toward the per-frame request limit even when its caller had already
given it up and no reply was sent. A backlog of such requests could hold
live requests back for a frame. It now counts only when the app claims
it and replies with the error, as the other given-up requests are
skipped.
This commit is contained in:
dotCooCoo
2026-11-05 17:17:36 +07:00
committed by bflatastic
parent c1f7e4ed6d
commit 7e97255334
2 changed files with 23 additions and 1 deletions
+2 -1
View File
@@ -13,8 +13,9 @@ impl VectorcraftApp {
// Synthetic input sent before this pointer request was dropped (see
// `discard_stale_injected_batch`): its events would reach the tool without that input.
if req.method == "ui.pointer" && req.made_before(self.synthetic_dropped_before) {
handled += 1;
// One its caller gave up is skipped without counting toward this frame's limit.
if req.claim() {
handled += 1;
let _ = req.reply.send(json!({
"ok": false,
"error": "input sent before this pointer request was dropped because the document, dialog or focus changed"
+21
View File
@@ -142,6 +142,27 @@ fn a_given_up_pointer_does_not_hold_back_later_requests() {
assert!(up_reply.try_recv().is_err(), "the given-up pointer request did not run");
}
/// Given-up `ui.pointer` requests made before synthetic input was dropped do not count toward a
/// frame's request limit: a request queued behind a frame's worth of them runs in that frame.
#[test]
fn given_up_stale_pointer_requests_do_not_use_up_the_frame() {
let (tx, rx) = std::sync::mpsc::channel();
let mut app = VectorcraftApp::new(Session::new(), Default::default()).with_control(rx);
app.run("file.new", json!({"width": 300, "height": 300})).unwrap();
let ctx = egui::Context::default();
for _ in 0..crate::control::MAX_CONTROL_REQUESTS_PER_FRAME {
let (up, _up_reply) = ControlRequest::new("ui.pointer", json!({"events": [{"kind": "up", "x": 200, "y": 180}]}));
assert!(up.ticket().give_up(), "the pointer request had not started");
tx.send(up).unwrap();
}
app.synthetic_dropped_before = ControlRequest::next_seq();
let (list, list_reply) = ControlRequest::new("ui.tool.list", json!({}));
tx.send(list).unwrap();
app.drain_control(&ctx);
let r = list_reply.try_recv().expect("the list ran in the same frame");
assert_eq!(r["ok"], true, "{r}");
}
/// A `ui.pointer` release sent after a `ui.key` fails when the key's batch is discarded because a
/// dialog opened before a UI pass took it, whether the release was waiting for the key
/// (`waiting`) or still on the channel. A release made after the discard runs.