Skip to main content

pfx_editor_shell/
lib.rs

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}