1pub mod focus;
2mod offscreen;
3pub mod paint;
4pub mod pgpu;
5mod script;
6mod window;
7pub mod worker;
8
9use std::fmt;
10use std::path::PathBuf;
11use std::time::Duration;
12
13use egui::{Context, Ui};
14use egui_wgpu::Renderer;
15use pfx_editor_style::theme;
16use pfx_gpu::Gpu;
17
18pub use offscreen::{MAX_SIDE, check, drive, encode_png, pixels};
19pub use pfx_editor_style::Theme;
20pub use pgpu::Pgpu;
21pub use window::{Pulse, window};
22pub use worker::Wake;
23
24pub const WATCH_EVERY: Duration = Duration::from_millis(50);
25
26#[derive(Clone, Debug, PartialEq, Eq)]
27pub struct Watch {
28 pub files: Vec<PathBuf>,
29 pub every: Duration,
30}
31
32impl Default for Watch {
33 fn default() -> Watch {
34 Watch {
35 files: Vec::new(),
36 every: WATCH_EVERY,
37 }
38 }
39}
40
41#[derive(Clone, Copy, Debug, Default, PartialEq)]
42pub struct Timing {
43 pub gpu_ms: Option<f64>,
44 pub drops: u64,
45 pub drawn: bool,
46}
47
48pub trait App {
49 fn ui(&mut self, ui: &mut Ui);
50
51 fn theme(&self) -> Theme;
52
53 fn viewport(&self) -> bool {
54 false
55 }
56
57 fn frame(&mut self, _gpu: &Gpu, _renderer: &mut Renderer, _pixels_per_point: f32) -> bool {
58 false
59 }
60
61 fn forget(&mut self) {}
62
63 fn name(&self) -> &str {
64 "editor"
65 }
66
67 fn label(&self) -> Option<String> {
68 None
69 }
70
71 fn watched(&self) -> Watch {
72 Watch::default()
73 }
74
75 fn changed(&mut self, _files: &[PathBuf]) {}
76
77 fn waker(&mut self, _wake: Wake) {}
78
79 fn opened(&mut self) -> bool {
80 false
81 }
82
83 fn pgpu(&mut self, _state: Pgpu) {}
84
85 fn timing(&self) -> Timing {
86 Timing::default()
87 }
88}
89
90#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
91pub struct Mods {
92 pub ctrl: bool,
93 pub shift: bool,
94 pub alt: bool,
95}
96
97#[derive(Clone, Debug, PartialEq)]
98pub enum Step {
99 Key(String, Mods),
100 Text(String),
101 Click([f32; 2]),
102 Move([f32; 2]),
103 Press {
104 at: [f32; 2],
105 mods: Mods,
106 },
107 Release([f32; 2]),
108 Drag {
109 from: [f32; 2],
110 to: [f32; 2],
111 middle: bool,
112 mods: Mods,
113 },
114 Scroll {
115 at: [f32; 2],
116 lines: f32,
117 },
118 Frame,
119}
120
121#[derive(Clone, Debug, Default, PartialEq)]
122pub struct Script {
123 pub steps: Vec<Step>,
124}
125
126impl Script {
127 pub fn parse(text: &str) -> Result<Script, Error> {
128 script::parse(text)
129 }
130}
131
132#[derive(Clone, Debug, PartialEq)]
133pub struct Error(pub String);
134
135impl fmt::Display for Error {
136 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137 f.write_str(&self.0)
138 }
139}
140
141impl std::error::Error for Error {}
142
143pub fn context(theme: &Theme) -> Context {
144 let ctx = Context::default();
145 theme::apply_theme(&ctx, theme);
146 ctx
147}
148
149pub fn shot(app: &mut impl App, size: [u32; 2], script: &Script) -> Result<Vec<u8>, Error> {
150 let rgba = pixels(app, size, script)?;
151 encode_png(size, &rgba)
152}
153
154pub fn block_on<F: Future>(future: F) -> F::Output {
155 let mut future = std::pin::pin!(future);
156 let mut context = std::task::Context::from_waker(std::task::Waker::noop());
157 loop {
158 if let std::task::Poll::Ready(value) = future.as_mut().poll(&mut context) {
159 return value;
160 }
161 std::thread::yield_now();
162 }
163}
164
165pub fn gpu_turn(since: std::time::Instant) {
166 let ms = (since.elapsed().as_secs_f64() * 1000.0).ceil().to_string();
167 let _ = std::process::Command::new("pgpu")
168 .args(["turn", "--work-ms", &ms, "--longest-ms", &ms])
169 .status();
170}