use std::fmt::{Debug, Error, Formatter};
use std::time::Duration;
use base::id::{PipelineId, TopLevelBrowsingContextId};
use embedder_traits::EventLoopWaker;
use euclid::Scale;
use keyboard_types::{CompositionEvent, KeyboardEvent};
use libc::c_void;
use net::protocols::ProtocolRegistry;
use script_traits::{
GamepadEvent, MediaSessionActionType, MouseButton, Theme, TouchEventType, TouchId,
TraversalDirection, WheelDelta,
};
use servo_geometry::{DeviceIndependentIntRect, DeviceIndependentIntSize, DeviceIndependentPixel};
use servo_url::ServoUrl;
use webrender_api::units::{
DeviceIntPoint, DeviceIntRect, DeviceIntSize, DevicePixel, DevicePoint, DeviceRect,
};
use webrender_api::ScrollLocation;
use webrender_traits::RenderingContext;
#[derive(Clone)]
pub enum MouseWindowEvent {
Click(MouseButton, DevicePoint),
MouseDown(MouseButton, DevicePoint),
MouseUp(MouseButton, DevicePoint),
}
#[derive(Clone)]
pub enum WebRenderDebugOption {
Profiler,
TextureCacheDebug,
RenderTargetDebug,
}
#[derive(Clone)]
pub enum EmbedderEvent {
Idle,
Refresh,
WindowResize,
ThemeChange(Theme),
AllowNavigationResponse(PipelineId, bool),
LoadUrl(TopLevelBrowsingContextId, ServoUrl),
MouseWindowEventClass(MouseWindowEvent),
MouseWindowMoveEventClass(DevicePoint),
Touch(TouchEventType, TouchId, DevicePoint),
Wheel(WheelDelta, DevicePoint),
Scroll(ScrollLocation, DeviceIntPoint, TouchEventType),
Zoom(f32),
PinchZoom(f32),
ResetZoom,
Navigation(TopLevelBrowsingContextId, TraversalDirection),
Quit,
ExitFullScreen(TopLevelBrowsingContextId),
Keyboard(KeyboardEvent),
IMEComposition(CompositionEvent),
Reload(TopLevelBrowsingContextId),
NewWebView(ServoUrl, TopLevelBrowsingContextId),
CloseWebView(TopLevelBrowsingContextId),
SendError(Option<TopLevelBrowsingContextId>, String),
MoveResizeWebView(TopLevelBrowsingContextId, DeviceRect),
ShowWebView(TopLevelBrowsingContextId, bool),
HideWebView(TopLevelBrowsingContextId),
RaiseWebViewToTop(TopLevelBrowsingContextId, bool),
FocusWebView(TopLevelBrowsingContextId),
BlurWebView,
ToggleWebRenderDebug(WebRenderDebugOption),
CaptureWebRender,
ClearCache,
ToggleSamplingProfiler(Duration, Duration),
MediaSessionAction(MediaSessionActionType),
SetWebViewThrottled(TopLevelBrowsingContextId, bool),
IMEDismissed,
InvalidateNativeSurface,
ReplaceNativeSurface(*mut c_void, DeviceIntSize),
Gamepad(GamepadEvent),
Vsync,
}
impl Debug for EmbedderEvent {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
match *self {
EmbedderEvent::Idle => write!(f, "Idle"),
EmbedderEvent::Refresh => write!(f, "Refresh"),
EmbedderEvent::WindowResize => write!(f, "Resize"),
EmbedderEvent::ThemeChange(..) => write!(f, "ThemeChange"),
EmbedderEvent::Keyboard(..) => write!(f, "Keyboard"),
EmbedderEvent::IMEComposition(..) => write!(f, "IMEComposition"),
EmbedderEvent::AllowNavigationResponse(..) => write!(f, "AllowNavigationResponse"),
EmbedderEvent::LoadUrl(..) => write!(f, "LoadUrl"),
EmbedderEvent::MouseWindowEventClass(..) => write!(f, "Mouse"),
EmbedderEvent::MouseWindowMoveEventClass(..) => write!(f, "MouseMove"),
EmbedderEvent::Touch(..) => write!(f, "Touch"),
EmbedderEvent::Wheel(..) => write!(f, "Wheel"),
EmbedderEvent::Scroll(..) => write!(f, "Scroll"),
EmbedderEvent::Zoom(..) => write!(f, "Zoom"),
EmbedderEvent::PinchZoom(..) => write!(f, "PinchZoom"),
EmbedderEvent::ResetZoom => write!(f, "ResetZoom"),
EmbedderEvent::Navigation(..) => write!(f, "Navigation"),
EmbedderEvent::Quit => write!(f, "Quit"),
EmbedderEvent::Reload(..) => write!(f, "Reload"),
EmbedderEvent::NewWebView(_, TopLevelBrowsingContextId(webview_id)) => {
write!(f, "NewWebView({webview_id:?})")
},
EmbedderEvent::SendError(..) => write!(f, "SendError"),
EmbedderEvent::CloseWebView(TopLevelBrowsingContextId(webview_id)) => {
write!(f, "CloseWebView({webview_id:?})")
},
EmbedderEvent::MoveResizeWebView(webview_id, _) => {
write!(f, "MoveResizeWebView({webview_id:?})")
},
EmbedderEvent::ShowWebView(TopLevelBrowsingContextId(webview_id), hide_others) => {
write!(f, "ShowWebView({webview_id:?}, {hide_others})")
},
EmbedderEvent::HideWebView(TopLevelBrowsingContextId(webview_id)) => {
write!(f, "HideWebView({webview_id:?})")
},
EmbedderEvent::RaiseWebViewToTop(
TopLevelBrowsingContextId(webview_id),
hide_others,
) => {
write!(f, "RaiseWebViewToTop({webview_id:?}, {hide_others})")
},
EmbedderEvent::FocusWebView(TopLevelBrowsingContextId(webview_id)) => {
write!(f, "FocusWebView({webview_id:?})")
},
EmbedderEvent::BlurWebView => write!(f, "BlurWebView"),
EmbedderEvent::ToggleWebRenderDebug(..) => write!(f, "ToggleWebRenderDebug"),
EmbedderEvent::CaptureWebRender => write!(f, "CaptureWebRender"),
EmbedderEvent::ToggleSamplingProfiler(..) => write!(f, "ToggleSamplingProfiler"),
EmbedderEvent::ExitFullScreen(..) => write!(f, "ExitFullScreen"),
EmbedderEvent::MediaSessionAction(..) => write!(f, "MediaSessionAction"),
EmbedderEvent::SetWebViewThrottled(..) => write!(f, "SetWebViewThrottled"),
EmbedderEvent::IMEDismissed => write!(f, "IMEDismissed"),
EmbedderEvent::ClearCache => write!(f, "ClearCache"),
EmbedderEvent::InvalidateNativeSurface => write!(f, "InvalidateNativeSurface"),
EmbedderEvent::ReplaceNativeSurface(..) => write!(f, "ReplaceNativeSurface"),
EmbedderEvent::Gamepad(..) => write!(f, "Gamepad"),
EmbedderEvent::Vsync => write!(f, "Vsync"),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum AnimationState {
Idle,
Animating,
}
pub trait WindowMethods {
fn get_coordinates(&self) -> EmbedderCoordinates;
fn set_animation_state(&self, _state: AnimationState);
fn rendering_context(&self) -> RenderingContext;
}
pub trait EmbedderMethods {
fn create_event_loop_waker(&mut self) -> Box<dyn EventLoopWaker>;
#[cfg(feature = "webxr")]
fn register_webxr(
&mut self,
_: &mut webxr::MainThreadRegistry,
_: embedder_traits::EmbedderProxy,
) {
}
fn get_user_agent_string(&self) -> Option<String> {
None
}
fn get_version_string(&self) -> Option<String> {
None
}
fn get_protocol_handlers(&self) -> ProtocolRegistry {
ProtocolRegistry::default()
}
}
#[derive(Clone, Copy, Debug)]
pub struct EmbedderCoordinates {
pub hidpi_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
pub screen_size: DeviceIndependentIntSize,
pub available_screen_size: DeviceIndependentIntSize,
pub window_rect: DeviceIndependentIntRect,
pub framebuffer: DeviceIntSize,
pub viewport: DeviceIntRect,
}
impl EmbedderCoordinates {
pub fn get_viewport(&self) -> DeviceIntRect {
self.viewport
}
pub fn flip_rect(&self, rect: &DeviceIntRect) -> DeviceIntRect {
let mut result = *rect;
let min_y = self.framebuffer.height - result.max.y;
let max_y = self.framebuffer.height - result.min.y;
result.min.y = min_y;
result.max.y = max_y;
result
}
pub fn get_flipped_viewport(&self) -> DeviceIntRect {
self.flip_rect(&self.get_viewport())
}
}
#[cfg(test)]
mod test {
use euclid::{Box2D, Point2D, Scale, Size2D};
use webrender_api::units::DeviceIntRect;
use super::EmbedderCoordinates;
#[test]
fn test() {
let screen_size = Size2D::new(1080, 720);
let viewport = Box2D::from_origin_and_size(Point2D::zero(), Size2D::new(800, 600));
let window_rect = Box2D::from_origin_and_size(Point2D::zero(), Size2D::new(800, 600));
let coordinates = EmbedderCoordinates {
hidpi_factor: Scale::new(1.),
screen_size,
available_screen_size: screen_size,
window_rect,
framebuffer: viewport.size(),
viewport,
};
let viewport = DeviceIntRect::new(Point2D::new(0, 0), Point2D::new(800, 600));
assert_eq!(coordinates.get_viewport(), viewport);
assert_eq!(coordinates.get_flipped_viewport(), viewport);
let rect1 = DeviceIntRect::new(Point2D::new(0, 0), Point2D::new(800, 400));
let rect2 = DeviceIntRect::new(Point2D::new(0, 100), Point2D::new(800, 600));
let rect3 = DeviceIntRect::new(Point2D::new(0, 200), Point2D::new(800, 500));
assert_eq!(
coordinates.flip_rect(&rect1),
DeviceIntRect::new(Point2D::new(0, 200), Point2D::new(800, 600))
);
assert_eq!(
coordinates.flip_rect(&rect2),
DeviceIntRect::new(Point2D::new(0, 0), Point2D::new(800, 500))
);
assert_eq!(
coordinates.flip_rect(&rect3),
DeviceIntRect::new(Point2D::new(0, 100), Point2D::new(800, 400))
);
let rect1 = DeviceIntRect::new(Point2D::new(0, 0), Point2D::new(700, 400));
let rect2 = DeviceIntRect::new(Point2D::new(100, 100), Point2D::new(800, 600));
let rect3 = DeviceIntRect::new(Point2D::new(300, 200), Point2D::new(600, 500));
assert_eq!(
coordinates.flip_rect(&rect1),
DeviceIntRect::new(Point2D::new(0, 200), Point2D::new(700, 600))
);
assert_eq!(
coordinates.flip_rect(&rect2),
DeviceIntRect::new(Point2D::new(100, 0), Point2D::new(800, 500))
);
assert_eq!(
coordinates.flip_rect(&rect3),
DeviceIntRect::new(Point2D::new(300, 100), Point2D::new(600, 400))
);
}
}