1use crate::automation::command::{lookup, Ctx, Envelope, Handler, Notice, NoticeKind, Outcome, Phase, Reply};
10use crate::automation::pointer::Pointer;
11use std::collections::VecDeque;
12use std::sync::mpsc::{channel, Receiver, Sender};
13use std::sync::{Arc, Mutex};
14
15pub struct Pending {
16 pub envelope: Envelope,
17 pub reply: Sender<Reply>,
18}
19
20struct Awaiting {
21 token: u64,
22 region: crate::automation::cmd_capture::Region,
23 pending: Pending,
24}
25
26#[derive(Default)]
27struct Inner {
28 pending: VecDeque<Pending>,
29 awaiting: Vec<Awaiting>,
30 notices: Vec<Notice>,
31 pointer: Pointer,
32 next_token: u64,
33 frame: u64,
34 poisoned: Option<String>,
35}
36
37#[derive(Default)]
38pub struct AutomationQueue {
39 inner: Mutex<Inner>,
40 ctx: Mutex<Option<egui::Context>>,
41}
42
43impl AutomationQueue {
44 pub fn new() -> Arc<Self> {
45 Arc::new(Self::default())
46 }
47
48 pub fn attach(&self, ctx: &egui::Context) {
50 *self.ctx.lock().unwrap() = Some(ctx.clone());
51 }
52
53 pub fn submit(&self, envelope: Envelope) -> Receiver<Reply> {
57 let (tx, rx) = channel();
58 let mut inner = self.inner.lock().unwrap();
59 if let Some(why) = &inner.poisoned {
60 let _ = tx.send(Reply::err(envelope.id, inner.frame, format!("session poisoned: {why}"), vec![]));
61 return rx;
62 }
63 if lookup(&envelope.cmd).is_none() {
64 let _ = tx.send(Reply::err(envelope.id, inner.frame, format!("unknown command `{}`", envelope.cmd), vec![]));
65 return rx;
66 }
67 inner.pending.push_back(Pending { envelope, reply: tx });
68 drop(inner);
69 if let Some(ctx) = self.ctx.lock().unwrap().as_ref() {
70 ctx.request_repaint();
71 }
72 rx
73 }
74
75 pub fn push_notice(&self, kind: NoticeKind, text: impl Into<String>) {
76 let mut inner = self.inner.lock().unwrap();
77 let frame = inner.frame;
78 inner.notices.push(Notice { kind, frame, text: text.into() });
79 }
80
81 pub fn take_notices(&self) -> Vec<Notice> {
82 std::mem::take(&mut self.inner.lock().unwrap().notices)
83 }
84
85 pub fn pointer(&self) -> Pointer {
86 self.inner.lock().unwrap().pointer.clone()
87 }
88
89 pub fn frame(&self) -> u64 {
90 self.inner.lock().unwrap().frame
91 }
92
93 pub fn has_pending(&self) -> bool {
94 let inner = self.inner.lock().unwrap();
95 !inner.pending.is_empty() || !inner.awaiting.is_empty()
96 }
97
98 pub fn poison(&self, why: impl Into<String>) {
101 let why = why.into();
102 let mut inner = self.inner.lock().unwrap();
103 inner.poisoned = Some(why.clone());
104 let frame = inner.frame;
105 for p in inner.pending.drain(..) {
106 let _ = p.reply.send(Reply::err(p.envelope.id, frame, format!("session poisoned: {why}"), vec![]));
107 }
108 for a in inner.awaiting.drain(..) {
109 let _ = a.pending.reply.send(Reply::err(a.pending.envelope.id, frame, format!("session poisoned: {why}"), vec![]));
110 }
111 }
112
113 pub fn is_poisoned(&self) -> bool {
114 self.inner.lock().unwrap().poisoned.is_some()
115 }
116
117 pub fn drain_input(&self, raw: &mut egui::RawInput, frame: u64, ppp: f32, view: Option<egui::Rect>) {
121 let mut inner = self.inner.lock().unwrap();
122 inner.frame = frame;
123
124 if !inner.awaiting.is_empty() {
126 let mut done = Vec::new();
127 for event in &raw.events {
128 if let egui::Event::Screenshot { user_data, image, .. } = event {
129 let token = user_data.data.as_ref().and_then(|d| d.downcast_ref::<u64>()).copied();
130 if let Some(token) = token {
131 if let Some(pos) = inner.awaiting.iter().position(|a| a.token == token) {
132 let a = inner.awaiting.remove(pos);
133 done.push((a, image.clone()));
134 }
135 }
136 }
137 }
138 for (a, image) in done {
139 let notices = std::mem::take(&mut inner.notices);
140 let reply = match crate::automation::cmd_capture::encode_capture(&image, ppp, view, &a.region) {
141 Ok((json, png)) => {
142 let mut r = Reply::ok(a.pending.envelope.id, frame, json, notices);
143 r.blob = Some(png);
144 r
145 }
146 Err(e) => Reply::err(a.pending.envelope.id, frame, e, notices),
147 };
148 let _ = a.pending.reply.send(reply);
149 }
150 }
151
152 let mut keep = VecDeque::new();
154 let mut events = Vec::new();
155 while let Some(p) = inner.pending.pop_front() {
156 let Some(spec) = lookup(&p.envelope.cmd) else { continue };
157 match (&spec.handler, spec.phase) {
158 (Handler::Input(f), Phase::Input) => {
159 let result = f(&mut inner.pointer, p.envelope.args.clone(), &mut events);
160 let notices = std::mem::take(&mut inner.notices);
161 let reply = match result {
162 Ok(v) => Reply::ok(p.envelope.id, frame, v, notices),
163 Err(e) => Reply::err(p.envelope.id, frame, e, notices),
164 };
165 let _ = p.reply.send(reply);
166 }
167 _ => keep.push_back(p),
168 }
169 }
170 inner.pending = keep;
171 raw.modifiers = inner.pointer.modifiers.egui();
172 raw.events.extend(events);
173 }
174
175 pub fn drain_app(&self, phase: Phase, ctx: &mut Ctx<'_>, frame: u64) {
178 let batch: Vec<Pending> = {
179 let mut inner = self.inner.lock().unwrap();
180 inner.frame = frame;
181 let mut keep = VecDeque::new();
182 let mut batch = Vec::new();
183 while let Some(p) = inner.pending.pop_front() {
184 match lookup(&p.envelope.cmd) {
185 Some(spec) if spec.phase == phase => batch.push(p),
186 _ => keep.push_back(p),
187 }
188 }
189 inner.pending = keep;
190 batch
191 };
192 for p in batch {
193 let spec = lookup(&p.envelope.cmd).expect("checked at submit");
194 let Handler::App(f) = &spec.handler else { continue };
195 let result = f(ctx, p.envelope.args.clone());
196 let mut inner = self.inner.lock().unwrap();
197 let notices = std::mem::take(&mut inner.notices);
198 match result {
199 Ok(Outcome::Done(v)) => {
200 let _ = p.reply.send(Reply::ok(p.envelope.id, frame, v, notices));
201 }
202 Ok(Outcome::Blob { json, bytes }) => {
203 let mut r = Reply::ok(p.envelope.id, frame, json, notices);
204 r.blob = Some(bytes);
205 let _ = p.reply.send(r);
206 }
207 Ok(Outcome::AwaitScreenshot { token, region }) => {
208 inner.notices = notices;
209 inner.awaiting.push(Awaiting { token, region, pending: p });
210 }
211 Err(e) => {
212 let _ = p.reply.send(Reply::err(p.envelope.id, frame, e, notices));
213 }
214 }
215 }
216 }
217
218 pub fn next_token(&self) -> u64 {
220 let mut inner = self.inner.lock().unwrap();
221 inner.next_token += 1;
222 inner.next_token
223 }
224}