1use alloc::string::String;
5use alloc::vec::Vec;
6use core::cell::{Cell, RefCell};
7
8use serde::de::DeserializeOwned;
9
10use crate::api::{self, Json};
11use crate::bindings::ui as wit;
12use crate::error::Result;
13use crate::geometry::Geometry;
14pub use crate::input::{Button, Key, KeyCode, KeyPhase, Modifiers, Pointer, PointerKind};
15
16pub trait UiPlugin: Sized + 'static {
24 fn activate(cx: &mut Context) -> Self;
27
28 fn frame(&mut self, frame: &Frame);
33
34 fn event(&mut self, event: Event, cx: &mut Context) {
37 let _ = (event, cx);
38 }
39
40 fn deactivate(&mut self) {}
42}
43
44#[derive(Debug, Default)]
47pub struct Context {
48 config: String,
49}
50
51impl Context {
52 pub const fn new() -> Self {
53 Self {
54 config: String::new(),
55 }
56 }
57
58 pub fn config_json(&self) -> Json {
60 Json(self.config.clone())
61 }
62
63 pub fn config<T: DeserializeOwned>(&self) -> Result<T> {
65 if self.config.trim().is_empty() {
66 return Json(String::from("null")).parse();
67 }
68 Json(self.config.clone()).parse()
69 }
70
71 pub fn values(&self) -> api::values::Values {
72 api::values()
73 }
74
75 pub fn live(&self) -> api::live::Live {
76 api::live()
77 }
78
79 pub fn events(&self) -> api::events::Events {
80 api::events()
81 }
82
83 pub fn calls(&self) -> api::calls::Calls {
84 api::calls()
85 }
86
87 pub fn claims(&self) -> api::claims::Claims {
88 api::claims()
89 }
90
91 pub fn is_driving(&self) -> bool {
92 api::view::is_driving()
93 }
94
95 pub fn capabilities(&self) -> api::capabilities::Capabilities {
96 api::capabilities::get()
97 }
98
99 pub fn request_frame(&self) {
104 crate::host::request_frame();
105 }
106
107 pub fn wake_at(&self, at_ms: u64) {
109 crate::host::wake_at(at_ms);
110 }
111
112 pub fn open(&self, surface: &str) -> crate::Result<()> {
116 crate::host::surface_open(surface)
117 }
118
119 pub fn focus_pane(&self, pane: &str) -> crate::Result<()> {
123 crate::host::focus_pane(pane)
124 }
125
126 pub fn open_url(&self, url: &str) -> crate::Result<()> {
129 crate::host::url_open(url)
130 }
131
132 pub fn close(&self, surface: &str) -> crate::Result<()> {
134 crate::host::surface_close(surface)
135 }
136
137 pub fn request_size(&self, surface: &str, cols: u32, rows: u32) -> crate::Result<()> {
143 crate::host::surface_request_size(surface, cols, rows)
144 }
145
146 pub fn set_label(&self, surface: &str, label: Option<&str>) -> crate::Result<()> {
151 crate::host::surface_set_label(surface, label)
152 }
153
154 pub fn set_caret(&self, surface: &str, caret: Option<(u32, u32)>) -> crate::Result<()> {
160 crate::host::surface_set_caret(surface, caret)
161 }
162}
163
164#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
166pub enum Power {
167 #[default]
168 Mains,
169 SavePower,
170}
171
172impl From<wit::Power> for Power {
173 fn from(power: wit::Power) -> Self {
174 match power {
175 wit::Power::Mains => Self::Mains,
176 wit::Power::SavePower => Self::SavePower,
177 }
178 }
179}
180
181#[derive(Debug)]
183pub struct Frame {
184 now_ms: u64,
185 interval_ms: u32,
186 power: Power,
187 elapsed_ms: u64,
188 next: Cell<Option<u64>>,
189}
190
191impl Frame {
192 pub fn new(now_ms: u64, interval_ms: u32, power: Power) -> Self {
194 Self {
195 now_ms,
196 interval_ms,
197 power,
198 elapsed_ms: 0,
199 next: Cell::new(None),
200 }
201 }
202
203 pub fn elapsed(&self) -> u64 {
207 self.elapsed_ms
208 }
209
210 pub fn now_ms(&self) -> u64 {
212 self.now_ms
213 }
214
215 pub fn interval_ms(&self) -> u32 {
217 self.interval_ms
218 }
219
220 pub fn power(&self) -> Power {
221 self.power
222 }
223
224 pub fn request_frame(&self) {
226 self.wake_at(self.now_ms);
227 }
228
229 pub fn wake_at(&self, at_ms: u64) {
232 let next = self.next.get().map_or(at_ms, |next| next.min(at_ms));
233 self.next.set(Some(next));
234 }
235
236 pub fn wake_after(&self, after_ms: u64) {
238 self.wake_at(self.now_ms.saturating_add(after_ms));
239 }
240
241 pub fn next_wake(&self) -> Option<u64> {
243 self.next.get()
244 }
245}
246
247#[derive(Clone, Debug, PartialEq, Eq)]
249#[non_exhaustive]
250pub enum Event {
251 Resize { surface: String, geometry: Geometry },
253 Visibility { surface: String, visible: bool },
255 CapabilitiesChanged,
258 Driving(bool),
260 ValueChanged { key: String, value: Option<Json> },
262 Live { key: String, payload: Json },
264 LiveDeleted { key: String },
266 Plugin { name: String, payload: Json },
268 AccountChanged,
270 Key(Key),
272 Paste { surface: String, text: String },
274 Pointer(Pointer),
276 ThemeChanged,
278 Focus { surface: String, focused: bool },
280 Command(String),
282 CallResult {
286 id: crate::api::calls::CallId,
287 result: Result<Json>,
288 },
289 FileChanged { watch: u32, paths: Vec<String> },
292 WatchFailed { watch: u32, error: String },
294 ProcessOutput {
296 pid: u32,
297 stderr: bool,
298 bytes: Vec<u8>,
299 },
300 ProcessExited { pid: u32, status: i32 },
303 PaneChanged {
306 kind: crate::daemon::PaneChangeKind,
307 pane: crate::api::account::Pane,
308 },
309 Interest(crate::daemon::Interest),
314}
315
316impl From<wit::Event> for Event {
317 fn from(event: wit::Event) -> Self {
318 match event {
319 wit::Event::Resize((surface, g)) => Self::Resize {
320 surface,
321 geometry: Geometry {
322 cols: g.cols,
323 rows: g.rows,
324 px_w: g.px_w,
325 px_h: g.px_h,
326 cell_px_w: g.cell_px_w,
327 cell_px_h: g.cell_px_h,
328 },
329 },
330 wit::Event::Visibility((surface, visible)) => Self::Visibility { surface, visible },
331 wit::Event::CapabilitiesChanged => Self::CapabilitiesChanged,
332 wit::Event::Driving(driving) => Self::Driving(driving),
333 wit::Event::ValueChanged((key, value)) => Self::ValueChanged {
334 key,
335 value: value.map(Json),
336 },
337 wit::Event::Live((key, payload)) => Self::Live {
338 key,
339 payload: Json(payload),
340 },
341 wit::Event::LiveDeleted(key) => Self::LiveDeleted { key },
342 wit::Event::PluginEvent((name, payload)) => Self::Plugin {
343 name,
344 payload: Json(payload),
345 },
346 wit::Event::AccountChanged => Self::AccountChanged,
347 wit::Event::Key(key) => Self::Key(Key {
348 surface: key.surface,
349 code: KeyCode::parse(&key.code),
350 text: key.text,
351 modifiers: Modifiers(key.modifiers),
352 phase: match key.phase {
353 wit::standard::plugin::event_types::KeyPhase::Press => KeyPhase::Press,
354 wit::standard::plugin::event_types::KeyPhase::Repeat => KeyPhase::Repeat,
355 wit::standard::plugin::event_types::KeyPhase::Release => KeyPhase::Release,
356 },
357 }),
358 wit::Event::Paste((surface, text)) => Self::Paste { surface, text },
359 wit::Event::Pointer(pointer) => Self::Pointer(Pointer {
360 surface: pointer.surface,
361 x: pointer.x,
362 y: pointer.y,
363 col: pointer.col,
364 row: pointer.row,
365 button: Button::from_wire(pointer.button),
366 kind: match pointer.kind {
367 wit::standard::plugin::event_types::PointerKind::Down => PointerKind::Down,
368 wit::standard::plugin::event_types::PointerKind::Up => PointerKind::Up,
369 wit::standard::plugin::event_types::PointerKind::Move => PointerKind::Move,
370 wit::standard::plugin::event_types::PointerKind::Drag => PointerKind::Drag,
371 wit::standard::plugin::event_types::PointerKind::Wheel => PointerKind::Wheel,
372 wit::standard::plugin::event_types::PointerKind::Enter => PointerKind::Enter,
373 wit::standard::plugin::event_types::PointerKind::Leave => PointerKind::Leave,
374 },
375 modifiers: Modifiers(pointer.modifiers),
376 }),
377 wit::Event::ThemeChanged => Self::ThemeChanged,
378 wit::Event::Focus((surface, focused)) => Self::Focus { surface, focused },
379 wit::Event::Command(id) => Self::Command(id),
380 wit::Event::CallResult(answer) => Self::CallResult {
381 id: crate::api::calls::CallId(answer.id),
382 result: answer.outcome.map(Json).map_err(Into::into),
383 },
384 }
385 }
386}
387
388#[doc(hidden)]
390pub struct UiRuntime<P> {
391 plugin: RefCell<Option<P>>,
392 cx: RefCell<Context>,
393 last_frame_ms: Cell<Option<u64>>,
396 visible: RefCell<alloc::collections::BTreeSet<String>>,
398}
399
400unsafe impl<P> Sync for UiRuntime<P> {}
405
406impl<P> Default for UiRuntime<P> {
407 fn default() -> Self {
408 Self::new()
409 }
410}
411
412impl<P> UiRuntime<P> {
413 pub const fn new() -> Self {
414 Self {
415 plugin: RefCell::new(None),
416 cx: RefCell::new(Context::new()),
417 last_frame_ms: Cell::new(None),
418 visible: RefCell::new(alloc::collections::BTreeSet::new()),
419 }
420 }
421}
422
423impl<P: UiPlugin> UiRuntime<P> {
424 pub fn activate(&self, config: String) {
425 let mut cx = self.cx.borrow_mut();
426 cx.config = config;
427 let plugin = P::activate(&mut cx);
428 *self.plugin.borrow_mut() = Some(plugin);
429 }
430
431 pub fn frame(&self, now_ms: u64, interval_ms: u32, power: Power) -> Option<u64> {
432 let mut frame = Frame::new(now_ms, interval_ms, power);
433 frame.elapsed_ms = self
434 .last_frame_ms
435 .get()
436 .map_or(0, |last| now_ms.saturating_sub(last));
437 self.last_frame_ms.set(Some(now_ms));
438 if let Some(plugin) = self.plugin.borrow_mut().as_mut() {
439 plugin.frame(&frame);
440 }
441 frame.next_wake()
442 }
443
444 pub fn event(&self, event: Event) {
445 match &event {
446 Event::Resize { surface, .. } => crate::surface::rebind_surfaces(surface),
447 Event::Visibility { surface, visible } => {
448 let mut shown = self.visible.borrow_mut();
449 if *visible {
450 shown.insert(surface.clone());
451 } else {
452 shown.remove(surface);
453 if shown.is_empty() {
454 self.last_frame_ms.set(None);
455 }
456 }
457 }
458 _ => {}
459 }
460 let mut cx = self.cx.borrow_mut();
461 if let Some(plugin) = self.plugin.borrow_mut().as_mut() {
462 plugin.event(event, &mut cx);
463 }
464 }
465
466 pub fn deactivate(&self) {
467 let plugin = self.plugin.borrow_mut().take();
468 if let Some(mut plugin) = plugin {
469 plugin.deactivate();
470 }
471 }
472
473 pub fn with_plugin<R>(&self, f: impl FnOnce(&mut P) -> R) -> Option<R> {
475 self.plugin.borrow_mut().as_mut().map(f)
476 }
477}