mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
Servoshell: Refactor save_output_image and implement into OHOS (#37237)
Split out `save_output_image_if_necessary` into its own file so the code can be shared by servoshell on the desktop and ohos. Additionally, hook it up to the loop in OHOS and have OHOS allow exiting. Testing: Manual testing on ohos and desktop. --------- Signed-off-by: Narfinger <Narfinger@users.noreply.github.com>
This commit is contained in:
parent
f60c857fcf
commit
5114e24db1
6 changed files with 75 additions and 31 deletions
|
@ -7,15 +7,14 @@ use std::collections::HashMap;
|
|||
use std::path::PathBuf;
|
||||
use std::rc::Rc;
|
||||
|
||||
use euclid::{Point2D, Vector2D};
|
||||
use image::{DynamicImage, ImageFormat};
|
||||
use euclid::Vector2D;
|
||||
use keyboard_types::{Key, KeyboardEvent, Modifiers, ShortcutMatcher};
|
||||
use log::{error, info};
|
||||
use servo::base::id::WebViewId;
|
||||
use servo::config::{opts, pref};
|
||||
use servo::ipc_channel::ipc::IpcSender;
|
||||
use servo::webrender_api::ScrollLocation;
|
||||
use servo::webrender_api::units::{DeviceIntPoint, DeviceIntRect, DeviceIntSize};
|
||||
use servo::webrender_api::units::{DeviceIntPoint, DeviceIntSize};
|
||||
use servo::{
|
||||
AllowOrDenyRequest, AuthenticationRequest, FilterPattern, FormControl, GamepadHapticEffectType,
|
||||
LoadStatus, PermissionRequest, Servo, ServoDelegate, ServoError, SimpleDialog, TouchEventType,
|
||||
|
@ -28,6 +27,7 @@ use super::dialog::Dialog;
|
|||
use super::gamepad::GamepadSupport;
|
||||
use super::keyutils::CMD_OR_CONTROL;
|
||||
use super::window_trait::{LINE_HEIGHT, WindowPortsMethods};
|
||||
use crate::output_image::save_output_image_if_necessary;
|
||||
use crate::prefs::ServoShellPreferences;
|
||||
|
||||
pub(crate) enum AppState {
|
||||
|
@ -140,31 +140,6 @@ impl RunningAppState {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn save_output_image_if_necessary(&self) {
|
||||
let Some(output_path) = self.servoshell_preferences.output_image_path.as_ref() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let inner = self.inner();
|
||||
let size = inner.window.rendering_context().size2d().to_i32();
|
||||
let viewport_rect = DeviceIntRect::from_origin_and_size(Point2D::origin(), size);
|
||||
let Some(image) = inner
|
||||
.window
|
||||
.rendering_context()
|
||||
.read_to_image(viewport_rect)
|
||||
else {
|
||||
error!("Failed to read output image.");
|
||||
return;
|
||||
};
|
||||
|
||||
let image_format = ImageFormat::from_path(output_path).unwrap_or(ImageFormat::Png);
|
||||
if let Err(error) =
|
||||
DynamicImage::ImageRgba8(image).save_with_format(output_path, image_format)
|
||||
{
|
||||
error!("Failed to save {output_path}: {error}.");
|
||||
}
|
||||
}
|
||||
|
||||
/// Repaint the Servo view is necessary, returning true if anything was actually
|
||||
/// painted or false otherwise. Something may not be painted if Servo is waiting
|
||||
/// for a stable image to paint.
|
||||
|
@ -181,7 +156,10 @@ impl RunningAppState {
|
|||
|
||||
// This needs to be done before presenting(), because `ReneringContext::read_to_image` reads
|
||||
// from the back buffer.
|
||||
self.save_output_image_if_necessary();
|
||||
save_output_image_if_necessary(
|
||||
&self.servoshell_preferences,
|
||||
&self.inner().window.rendering_context(),
|
||||
);
|
||||
|
||||
let mut inner_mut = self.inner_mut();
|
||||
inner_mut.window.rendering_context().present();
|
||||
|
|
|
@ -25,6 +25,7 @@ use servo::{
|
|||
use url::Url;
|
||||
|
||||
use crate::egl::host_trait::HostTrait;
|
||||
use crate::output_image::save_output_image_if_necessary;
|
||||
use crate::prefs::ServoShellPreferences;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -718,8 +719,14 @@ impl RunningAppState {
|
|||
pub fn present_if_needed(&self) {
|
||||
if self.inner().need_present {
|
||||
self.inner_mut().need_present = false;
|
||||
self.active_webview().paint();
|
||||
if !self.active_webview().paint() {
|
||||
return;
|
||||
}
|
||||
save_output_image_if_necessary(&self.servoshell_preferences, &self.rendering_context);
|
||||
self.rendering_context.present();
|
||||
if self.servoshell_preferences.exit_after_stable_image {
|
||||
self.request_shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -123,6 +123,8 @@ const PROMPT_QUEUE_SIZE: usize = 4;
|
|||
static SET_URL_BAR_CB: OnceLock<
|
||||
ThreadsafeFunction<String, (), String, false, false, UPDATE_URL_QUEUE_SIZE>,
|
||||
> = OnceLock::new();
|
||||
static TERMINATE_CALLBACK: OnceLock<ThreadsafeFunction<(), (), (), false, false, 1>> =
|
||||
OnceLock::new();
|
||||
static PROMPT_TOAST: OnceLock<
|
||||
ThreadsafeFunction<String, (), String, false, false, PROMPT_QUEUE_SIZE>,
|
||||
> = OnceLock::new();
|
||||
|
@ -601,6 +603,15 @@ pub fn register_url_callback(callback: Function<String, ()>) -> napi_ohos::Resul
|
|||
})
|
||||
}
|
||||
|
||||
#[napi(js_name = "registerTerminateCallback")]
|
||||
pub fn register_terminate_callback(callback: Function<(), ()>) -> napi_ohos::Result<()> {
|
||||
let tsfn_builder = callback.build_threadsafe_function();
|
||||
let function = tsfn_builder.max_queue_size::<1>().build()?;
|
||||
TERMINATE_CALLBACK
|
||||
.set(function)
|
||||
.map_err(|_| napi_ohos::Error::from_reason("Failed to set terminate function"))
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn register_prompt_toast_callback(callback: Function<String, ()>) -> napi_ohos::Result<()> {
|
||||
debug!("register_prompt_toast_callback called!");
|
||||
|
@ -850,7 +861,13 @@ impl HostTrait for HostCallbacks {
|
|||
// todo: should we tell the vsync thread that it should perform updates?
|
||||
}
|
||||
|
||||
fn on_shutdown_complete(&self) {}
|
||||
fn on_shutdown_complete(&self) {
|
||||
if let Some(terminate_fn) = TERMINATE_CALLBACK.get() {
|
||||
terminate_fn.call((), ThreadsafeFunctionCallMode::Blocking);
|
||||
} else {
|
||||
error!("Could not shut down despite servo shutting down");
|
||||
}
|
||||
}
|
||||
|
||||
/// Shows the Inputmethod
|
||||
///
|
||||
|
|
|
@ -15,6 +15,7 @@ mod crash_handler;
|
|||
pub(crate) mod desktop;
|
||||
#[cfg(any(target_os = "android", target_env = "ohos"))]
|
||||
mod egl;
|
||||
mod output_image;
|
||||
#[cfg(not(any(target_os = "android", target_env = "ohos")))]
|
||||
mod panic_hook;
|
||||
mod parser;
|
||||
|
|
39
ports/servoshell/output_image.rs
Normal file
39
ports/servoshell/output_image.rs
Normal file
|
@ -0,0 +1,39 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
use euclid::Point2D;
|
||||
use image::{DynamicImage, ImageFormat};
|
||||
use log::error;
|
||||
use servo::RenderingContext;
|
||||
use servo::webrender_api::units::DeviceIntRect;
|
||||
|
||||
use crate::prefs::ServoShellPreferences;
|
||||
|
||||
/// This needs to be done before presenting(), because `ReneringContext::read_to_image` reads
|
||||
/// from the back buffer. This does nothing if the preference `output_image_path` is not set.
|
||||
pub(crate) fn save_output_image_if_necessary<T>(
|
||||
prefs: &ServoShellPreferences,
|
||||
rendering_context: &Rc<T>,
|
||||
) where
|
||||
T: RenderingContext + ?Sized,
|
||||
{
|
||||
let Some(output_path) = prefs.output_image_path.as_ref() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let size = rendering_context.size2d().to_i32();
|
||||
let viewport_rect = DeviceIntRect::from_origin_and_size(Point2D::origin(), size);
|
||||
let Some(image) = rendering_context.read_to_image(viewport_rect) else {
|
||||
error!("Failed to read output image.");
|
||||
return;
|
||||
};
|
||||
|
||||
let image_format = ImageFormat::from_path(output_path).unwrap_or(ImageFormat::Png);
|
||||
if let Err(error) = DynamicImage::ImageRgba8(image).save_with_format(output_path, image_format)
|
||||
{
|
||||
error!("Failed to save {output_path}: {error}.");
|
||||
}
|
||||
}
|
|
@ -8,6 +8,7 @@ interface ServoXComponentInterface {
|
|||
goBack(): void;
|
||||
goForward(): void;
|
||||
registerURLcallback(callback: (url: string) => void): void;
|
||||
registerTerminateCallback(callback: () => void): void;
|
||||
registerPromptToastCallback(callback: (msg: string) => void): void
|
||||
initServo(options: InitOpts): void;
|
||||
}
|
||||
|
@ -130,6 +131,7 @@ struct Index {
|
|||
console.info('New URL from native: ', new_url)
|
||||
this.urlToLoad = new_url
|
||||
})
|
||||
this.xComponentContext.registerTerminateCallback(() => { this.context?.terminateSelf(); })
|
||||
this.xComponentContext.registerPromptToastCallback(prompt_toast)
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue