Skip to content

Commit 515bf9c

Browse files
authored
Merge branch 'emilk:master' into patch7
2 parents 78d6bfa + ba060a2 commit 515bf9c

File tree

29 files changed

+880
-329
lines changed

29 files changed

+880
-329
lines changed

Cargo.lock

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,23 @@ version = "0.1.1"
189189
source = "registry+https://github.com/rust-lang/crates.io-index"
190190
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
191191

192+
[[package]]
193+
name = "android_log-sys"
194+
version = "0.3.1"
195+
source = "registry+https://github.com/rust-lang/crates.io-index"
196+
checksum = "5ecc8056bf6ab9892dcd53216c83d1597487d7dacac16c8df6b877d127df9937"
197+
198+
[[package]]
199+
name = "android_logger"
200+
version = "0.14.1"
201+
source = "registry+https://github.com/rust-lang/crates.io-index"
202+
checksum = "05b07e8e73d720a1f2e4b6014766e6039fd2e96a4fa44e2a78d0e1fa2ff49826"
203+
dependencies = [
204+
"android_log-sys",
205+
"env_filter",
206+
"log",
207+
]
208+
192209
[[package]]
193210
name = "android_system_properties"
194211
version = "0.1.5"
@@ -1473,6 +1490,16 @@ dependencies = [
14731490
"syn",
14741491
]
14751492

1493+
[[package]]
1494+
name = "env_filter"
1495+
version = "0.1.2"
1496+
source = "registry+https://github.com/rust-lang/crates.io-index"
1497+
checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab"
1498+
dependencies = [
1499+
"log",
1500+
"regex",
1501+
]
1502+
14761503
[[package]]
14771504
name = "env_logger"
14781505
version = "0.10.2"
@@ -1935,6 +1962,17 @@ dependencies = [
19351962
"allocator-api2",
19361963
]
19371964

1965+
[[package]]
1966+
name = "hello_android"
1967+
version = "0.1.0"
1968+
dependencies = [
1969+
"android_logger",
1970+
"eframe",
1971+
"egui_extras",
1972+
"log",
1973+
"winit",
1974+
]
1975+
19381976
[[package]]
19391977
name = "hello_world"
19401978
version = "0.1.0"

crates/eframe/src/epi.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,16 @@ pub struct NativeOptions {
364364
///
365365
/// Defaults to true.
366366
pub dithering: bool,
367+
368+
/// Android application for `winit`'s event loop.
369+
///
370+
/// This value is required on Android to correctly create the event loop. See
371+
/// [`EventLoopBuilder::build`] and [`with_android_app`] for details.
372+
///
373+
/// [`EventLoopBuilder::build`]: winit::event_loop::EventLoopBuilder::build
374+
/// [`with_android_app`]: winit::platform::android::EventLoopBuilderExtAndroid::with_android_app
375+
#[cfg(target_os = "android")]
376+
pub android_app: Option<winit::platform::android::activity::AndroidApp>,
367377
}
368378

369379
#[cfg(not(target_arch = "wasm32"))]
@@ -383,6 +393,9 @@ impl Clone for NativeOptions {
383393

384394
persistence_path: self.persistence_path.clone(),
385395

396+
#[cfg(target_os = "android")]
397+
android_app: self.android_app.clone(),
398+
386399
..*self
387400
}
388401
}
@@ -424,6 +437,9 @@ impl Default for NativeOptions {
424437
persistence_path: None,
425438

426439
dithering: true,
440+
441+
#[cfg(target_os = "android")]
442+
android_app: None,
427443
}
428444
}
429445
}

crates/eframe/src/native/run.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,20 @@ use crate::{
1717

1818
// ----------------------------------------------------------------------------
1919
fn create_event_loop(native_options: &mut epi::NativeOptions) -> Result<EventLoop<UserEvent>> {
20+
#[cfg(target_os = "android")]
21+
use winit::platform::android::EventLoopBuilderExtAndroid as _;
22+
2023
crate::profile_function!();
2124
let mut builder = winit::event_loop::EventLoop::with_user_event();
2225

26+
#[cfg(target_os = "android")]
27+
let mut builder =
28+
builder.with_android_app(native_options.android_app.take().ok_or_else(|| {
29+
crate::Error::AppCreation(Box::from(
30+
"`NativeOptions` is missing required `android_app`",
31+
))
32+
})?);
33+
2334
if let Some(hook) = std::mem::take(&mut native_options.event_loop_builder) {
2435
hook(&mut builder);
2536
}

crates/eframe/src/native/wgpu_integration.rs

Lines changed: 6 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ impl<'app> WgpuWinitApp<'app> {
185185

186186
#[allow(unsafe_code, unused_mut, unused_unsafe)]
187187
let mut painter = egui_wgpu::winit::Painter::new(
188+
egui_ctx.clone(),
188189
self.native_options.wgpu_options.clone(),
189190
self.native_options.multisampling.max(1) as _,
190191
egui_wgpu::depth_format_from_bits(
@@ -593,6 +594,8 @@ impl<'app> WgpuWinitRunning<'app> {
593594
.map(|(id, viewport)| (*id, viewport.info.clone()))
594595
.collect();
595596

597+
painter.handle_screenshots(&mut raw_input.events);
598+
596599
(viewport_ui_cb, raw_input)
597600
};
598601

@@ -652,37 +655,14 @@ impl<'app> WgpuWinitRunning<'app> {
652655
true
653656
}
654657
});
655-
let screenshot_requested = !screenshot_commands.is_empty();
656-
let (vsync_secs, screenshot) = painter.paint_and_update_textures(
658+
let vsync_secs = painter.paint_and_update_textures(
657659
viewport_id,
658660
pixels_per_point,
659661
app.clear_color(&egui_ctx.style().visuals),
660662
&clipped_primitives,
661663
&textures_delta,
662-
screenshot_requested,
664+
screenshot_commands,
663665
);
664-
match (screenshot_requested, screenshot) {
665-
(false, None) => {}
666-
(true, Some(screenshot)) => {
667-
let screenshot = Arc::new(screenshot);
668-
for user_data in screenshot_commands {
669-
egui_winit
670-
.egui_input_mut()
671-
.events
672-
.push(egui::Event::Screenshot {
673-
viewport_id,
674-
user_data,
675-
image: screenshot.clone(),
676-
});
677-
}
678-
}
679-
(true, None) => {
680-
log::error!("Bug in egui_wgpu: screenshot requested, but no screenshot was taken");
681-
}
682-
(false, Some(_)) => {
683-
log::warn!("Bug in egui_wgpu: Got screenshot without requesting it");
684-
}
685-
}
686666

687667
for action in viewport.actions_requested.drain() {
688668
match action {
@@ -1024,7 +1004,7 @@ fn render_immediate_viewport(
10241004
[0.0, 0.0, 0.0, 0.0],
10251005
&clipped_primitives,
10261006
&textures_delta,
1027-
false,
1007+
vec![],
10281008
);
10291009

10301010
egui_winit.handle_platform_output(window, platform_output);

crates/eframe/src/web/app_runner.rs

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
use egui::TexturesDelta;
1+
use egui::{TexturesDelta, UserData, ViewportCommand};
2+
use std::mem;
23

34
use crate::{epi, App};
45

@@ -16,6 +17,9 @@ pub struct AppRunner {
1617
last_save_time: f64,
1718
pub(crate) text_agent: TextAgent,
1819

20+
// If not empty, the painter should capture the next frame
21+
screenshot_commands: Vec<UserData>,
22+
1923
// Output for the last run:
2024
textures_delta: TexturesDelta,
2125
clipped_primitives: Option<Vec<egui::ClippedPrimitive>>,
@@ -36,7 +40,8 @@ impl AppRunner {
3640
app_creator: epi::AppCreator<'static>,
3741
text_agent: TextAgent,
3842
) -> Result<Self, String> {
39-
let painter = super::ActiveWebPainter::new(canvas, &web_options).await?;
43+
let egui_ctx = egui::Context::default();
44+
let painter = super::ActiveWebPainter::new(egui_ctx.clone(), canvas, &web_options).await?;
4045

4146
let info = epi::IntegrationInfo {
4247
web_info: epi::WebInfo {
@@ -47,7 +52,6 @@ impl AppRunner {
4752
};
4853
let storage = LocalStorage::default();
4954

50-
let egui_ctx = egui::Context::default();
5155
egui_ctx.set_os(egui::os::OperatingSystem::from_user_agent(
5256
&super::user_agent().unwrap_or_default(),
5357
));
@@ -110,6 +114,7 @@ impl AppRunner {
110114
needs_repaint,
111115
last_save_time: now_sec(),
112116
text_agent,
117+
screenshot_commands: vec![],
113118
textures_delta: Default::default(),
114119
clipped_primitives: None,
115120
};
@@ -205,6 +210,8 @@ impl AppRunner {
205210
pub fn logic(&mut self) {
206211
// We sometimes miss blur/focus events due to the text agent, so let's just poll each frame:
207212
self.update_focus();
213+
// We might have received a screenshot
214+
self.painter.handle_screenshots(&mut self.input.raw.events);
208215

209216
let canvas_size = super::canvas_size_in_points(self.canvas(), self.egui_ctx());
210217
let mut raw_input = self.input.new_frame(canvas_size);
@@ -225,12 +232,19 @@ impl AppRunner {
225232
if viewport_output.len() > 1 {
226233
log::warn!("Multiple viewports not yet supported on the web");
227234
}
228-
for viewport_output in viewport_output.values() {
229-
for command in &viewport_output.commands {
230-
// TODO(emilk): handle some of the commands
231-
log::warn!(
232-
"Unhandled egui viewport command: {command:?} - not implemented in web backend"
233-
);
235+
for (_viewport_id, viewport_output) in viewport_output {
236+
for command in viewport_output.commands {
237+
match command {
238+
ViewportCommand::Screenshot(user_data) => {
239+
self.screenshot_commands.push(user_data);
240+
}
241+
_ => {
242+
// TODO(emilk): handle some of the commands
243+
log::warn!(
244+
"Unhandled egui viewport command: {command:?} - not implemented in web backend"
245+
);
246+
}
247+
}
234248
}
235249
}
236250

@@ -250,6 +264,7 @@ impl AppRunner {
250264
&clipped_primitives,
251265
self.egui_ctx.pixels_per_point(),
252266
&textures_delta,
267+
mem::take(&mut self.screenshot_commands),
253268
) {
254269
log::error!("Failed to paint: {}", super::string_from_js_value(&err));
255270
}

crates/eframe/src/web/web_painter.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use egui::{Event, UserData};
12
use wasm_bindgen::JsValue;
23

34
/// Renderer for a browser canvas.
@@ -16,14 +17,19 @@ pub(crate) trait WebPainter {
1617
fn max_texture_side(&self) -> usize;
1718

1819
/// Update all internal textures and paint gui.
20+
/// When `capture` isn't empty, the rendered screen should be captured.
21+
/// Once the screenshot is ready, the screenshot should be returned via [`Self::handle_screenshots`].
1922
fn paint_and_update_textures(
2023
&mut self,
2124
clear_color: [f32; 4],
2225
clipped_primitives: &[egui::ClippedPrimitive],
2326
pixels_per_point: f32,
2427
textures_delta: &egui::TexturesDelta,
28+
capture: Vec<UserData>,
2529
) -> Result<(), JsValue>;
2630

31+
fn handle_screenshots(&mut self, events: &mut Vec<Event>);
32+
2733
/// Destroy all resources.
2834
fn destroy(&mut self);
2935
}

crates/eframe/src/web/web_painter_glow.rs

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,30 @@
1+
use egui::{Event, UserData, ViewportId};
2+
use egui_glow::glow;
3+
use std::sync::Arc;
14
use wasm_bindgen::JsCast;
25
use wasm_bindgen::JsValue;
36
use web_sys::HtmlCanvasElement;
47

5-
use egui_glow::glow;
6-
78
use crate::{WebGlContextOption, WebOptions};
89

910
use super::web_painter::WebPainter;
1011

1112
pub(crate) struct WebPainterGlow {
1213
canvas: HtmlCanvasElement,
1314
painter: egui_glow::Painter,
15+
screenshots: Vec<(egui::ColorImage, Vec<UserData>)>,
1416
}
1517

1618
impl WebPainterGlow {
1719
pub fn gl(&self) -> &std::sync::Arc<glow::Context> {
1820
self.painter.gl()
1921
}
2022

21-
pub async fn new(canvas: HtmlCanvasElement, options: &WebOptions) -> Result<Self, String> {
23+
pub async fn new(
24+
_ctx: egui::Context,
25+
canvas: HtmlCanvasElement,
26+
options: &WebOptions,
27+
) -> Result<Self, String> {
2228
let (gl, shader_prefix) =
2329
init_glow_context_from_canvas(&canvas, options.webgl_context_option)?;
2430
#[allow(clippy::arc_with_non_send_sync)]
@@ -27,7 +33,11 @@ impl WebPainterGlow {
2733
let painter = egui_glow::Painter::new(gl, shader_prefix, None, options.dithering)
2834
.map_err(|err| format!("Error starting glow painter: {err}"))?;
2935

30-
Ok(Self { canvas, painter })
36+
Ok(Self {
37+
canvas,
38+
painter,
39+
screenshots: Vec::new(),
40+
})
3141
}
3242
}
3343

@@ -46,6 +56,7 @@ impl WebPainter for WebPainterGlow {
4656
clipped_primitives: &[egui::ClippedPrimitive],
4757
pixels_per_point: f32,
4858
textures_delta: &egui::TexturesDelta,
59+
capture: Vec<UserData>,
4960
) -> Result<(), JsValue> {
5061
let canvas_dimension = [self.canvas.width(), self.canvas.height()];
5162

@@ -57,6 +68,11 @@ impl WebPainter for WebPainterGlow {
5768
self.painter
5869
.paint_primitives(canvas_dimension, pixels_per_point, clipped_primitives);
5970

71+
if !capture.is_empty() {
72+
let image = self.painter.read_screen_rgba(canvas_dimension);
73+
self.screenshots.push((image, capture));
74+
}
75+
6076
for &id in &textures_delta.free {
6177
self.painter.free_texture(id);
6278
}
@@ -67,6 +83,19 @@ impl WebPainter for WebPainterGlow {
6783
fn destroy(&mut self) {
6884
self.painter.destroy();
6985
}
86+
87+
fn handle_screenshots(&mut self, events: &mut Vec<Event>) {
88+
for (image, data) in self.screenshots.drain(..) {
89+
let image = Arc::new(image);
90+
for data in data {
91+
events.push(Event::Screenshot {
92+
viewport_id: ViewportId::default(),
93+
image: image.clone(),
94+
user_data: data,
95+
});
96+
}
97+
}
98+
}
7099
}
71100

72101
/// Returns glow context and shader prefix.

0 commit comments

Comments
 (0)