#![allow(
clippy::needless_pass_by_value,
clippy::unused_self,
clippy::too_many_lines,
unexpected_cfgs
)]
use std::collections::BTreeMap;
use std::marker::PhantomData;
use std::num::NonZeroU32;
use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::mpsc;
use std::time::Duration;
use cookie::Cookie;
use raw_window_handle::{DisplayHandle, HandleError, WindowHandle};
use softbuffer::{Context, Surface};
use tauri_runtime::dpi::{PhysicalPosition, PhysicalRect, PhysicalSize, Position, Rect, Size};
use tauri_runtime::monitor::Monitor;
use tauri_runtime::webview::{DetachedWebview, PendingWebview};
use tauri_runtime::window::{
CursorIcon, DetachedWindow, PendingWindow, RawWindow, WebviewEvent, WindowBuilder,
WindowBuilderBase, WindowEvent as TauriWindowEvent, WindowId, WindowSizeConstraints,
};
use tauri_runtime::{
DeviceEventFilter, Error, EventLoopProxy as TauriEventLoopProxy, Icon, ProgressBarState,
Result, RunEvent, Runtime, RuntimeHandle, RuntimeInitArgs, UserEvent, WebviewDispatch,
WebviewEventId, WindowDispatch, WindowEventId,
};
use tauri_utils::config::{Color, WindowConfig};
use tauri_utils::{Theme, TitleBarStyle};
use url::Url;
use winit::application::ApplicationHandler;
use winit::dpi::{LogicalPosition, LogicalSize, PhysicalPosition as WinitPhysicalPosition};
use winit::event::WindowEvent as WinitWindowEvent;
use winit::event_loop::{ActiveEventLoop, EventLoop, EventLoopProxy};
use winit::window::{Fullscreen, Window, WindowAttributes, WindowId as WinitWindowId};
use crate::frame::Frame;
use crate::ipc::HostCommands;
use crate::raster::{PixelBuffer, render_to_pixels_with};
use crate::script::run_script_with_backprop;
use crate::text::TextRenderer;
use layout_cat::Viewport;
static NEXT_WINDOW_ID: AtomicU32 = AtomicU32::new(1);
fn allocate_window_id() -> WindowId {
WindowId::from(NEXT_WINDOW_ID.fetch_add(1, Ordering::SeqCst))
}
fn raw_window_id(id: WindowId) -> u32 {
let debug = format!("{id:?}");
debug
.trim_start_matches("WindowId(")
.trim_end_matches(')')
.parse::<u32>()
.unwrap_or(0)
}
pub enum RuntimeEvent<T: UserEvent> {
User(T),
CreateWindow {
id: WindowId,
label: String,
attributes: ServocatWindowBuilder,
},
SetTitle { id: WindowId, title: String },
SetSize { id: WindowId, size: Size },
SetPosition { id: WindowId, position: Position },
Show { id: WindowId },
Hide { id: WindowId },
Focus { id: WindowId },
Close { id: WindowId },
Destroy { id: WindowId },
Maximize { id: WindowId },
Unmaximize { id: WindowId },
Minimize { id: WindowId },
Unminimize { id: WindowId },
SetResizable { id: WindowId, resizable: bool },
SetDecorations { id: WindowId, decorations: bool },
SetFullscreen { id: WindowId, fullscreen: bool },
CreateWebview {
window_id: WindowId,
label: String,
url: String,
},
Navigate { window_id: WindowId, url: String },
Reload { window_id: WindowId },
EvalScript { window_id: WindowId, script: String },
EvalScriptWithCallback {
window_id: WindowId,
script: String,
callback: Box<dyn Fn(String) + Send + 'static>,
},
QueryInnerSize {
id: WindowId,
reply: mpsc::Sender<PhysicalSize<u32>>,
},
QueryOuterSize {
id: WindowId,
reply: mpsc::Sender<PhysicalSize<u32>>,
},
QueryInnerPosition {
id: WindowId,
reply: mpsc::Sender<PhysicalPosition<i32>>,
},
QueryOuterPosition {
id: WindowId,
reply: mpsc::Sender<PhysicalPosition<i32>>,
},
QueryScaleFactor {
id: WindowId,
reply: mpsc::Sender<f64>,
},
QueryTitle {
id: WindowId,
reply: mpsc::Sender<String>,
},
QueryIsVisible {
id: WindowId,
reply: mpsc::Sender<bool>,
},
QueryIsFocused {
id: WindowId,
reply: mpsc::Sender<bool>,
},
QueryIsMaximized {
id: WindowId,
reply: mpsc::Sender<bool>,
},
QueryIsMinimized {
id: WindowId,
reply: mpsc::Sender<bool>,
},
QueryIsFullscreen {
id: WindowId,
reply: mpsc::Sender<bool>,
},
QueryIsDecorated {
id: WindowId,
reply: mpsc::Sender<bool>,
},
QueryIsResizable {
id: WindowId,
reply: mpsc::Sender<bool>,
},
QueryTheme {
id: WindowId,
reply: mpsc::Sender<Theme>,
},
QueryPrimaryMonitor {
reply: mpsc::Sender<Option<Monitor>>,
},
QueryAvailableMonitors { reply: mpsc::Sender<Vec<Monitor>> },
QueryMonitorFromPoint {
x: f64,
y: f64,
reply: mpsc::Sender<Option<Monitor>>,
},
QueryCurrentMonitor {
id: WindowId,
reply: mpsc::Sender<Option<Monitor>>,
},
RunOnMainThread {
thunk: Box<dyn FnOnce() + Send + 'static>,
},
Exit { code: i32 },
}
impl<T: UserEvent> std::fmt::Debug for RuntimeEvent<T> {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let name = match self {
Self::User(_) => "User",
Self::CreateWindow { .. } => "CreateWindow",
Self::SetTitle { .. } => "SetTitle",
Self::SetSize { .. } => "SetSize",
Self::SetPosition { .. } => "SetPosition",
Self::Show { .. } => "Show",
Self::Hide { .. } => "Hide",
Self::Focus { .. } => "Focus",
Self::Close { .. } => "Close",
Self::Destroy { .. } => "Destroy",
Self::Maximize { .. } => "Maximize",
Self::Unmaximize { .. } => "Unmaximize",
Self::Minimize { .. } => "Minimize",
Self::Unminimize { .. } => "Unminimize",
Self::SetResizable { .. } => "SetResizable",
Self::SetDecorations { .. } => "SetDecorations",
Self::SetFullscreen { .. } => "SetFullscreen",
Self::CreateWebview { .. } => "CreateWebview",
Self::Navigate { .. } => "Navigate",
Self::Reload { .. } => "Reload",
Self::EvalScript { .. } => "EvalScript",
Self::EvalScriptWithCallback { .. } => "EvalScriptWithCallback",
Self::QueryInnerSize { .. } => "QueryInnerSize",
Self::QueryOuterSize { .. } => "QueryOuterSize",
Self::QueryInnerPosition { .. } => "QueryInnerPosition",
Self::QueryOuterPosition { .. } => "QueryOuterPosition",
Self::QueryScaleFactor { .. } => "QueryScaleFactor",
Self::QueryTitle { .. } => "QueryTitle",
Self::QueryIsVisible { .. } => "QueryIsVisible",
Self::QueryIsFocused { .. } => "QueryIsFocused",
Self::QueryIsMaximized { .. } => "QueryIsMaximized",
Self::QueryIsMinimized { .. } => "QueryIsMinimized",
Self::QueryIsFullscreen { .. } => "QueryIsFullscreen",
Self::QueryIsDecorated { .. } => "QueryIsDecorated",
Self::QueryIsResizable { .. } => "QueryIsResizable",
Self::QueryTheme { .. } => "QueryTheme",
Self::QueryPrimaryMonitor { .. } => "QueryPrimaryMonitor",
Self::QueryAvailableMonitors { .. } => "QueryAvailableMonitors",
Self::QueryMonitorFromPoint { .. } => "QueryMonitorFromPoint",
Self::QueryCurrentMonitor { .. } => "QueryCurrentMonitor",
Self::RunOnMainThread { .. } => "RunOnMainThread",
Self::Exit { .. } => "Exit",
};
formatter.write_str(name)
}
}
pub struct ServocatRuntime<T: UserEvent> {
event_loop: Option<EventLoop<RuntimeEvent<T>>>,
proxy: EventLoopProxy<RuntimeEvent<T>>,
}
impl<T: UserEvent> std::fmt::Debug for ServocatRuntime<T> {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("ServocatRuntime")
.field("event_loop", &self.event_loop.is_some())
.finish_non_exhaustive()
}
}
pub struct ServocatHandle<T: UserEvent> {
proxy: EventLoopProxy<RuntimeEvent<T>>,
}
impl<T: UserEvent> std::fmt::Debug for ServocatHandle<T> {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("ServocatHandle")
.finish_non_exhaustive()
}
}
impl<T: UserEvent> Clone for ServocatHandle<T> {
fn clone(&self) -> Self {
Self {
proxy: self.proxy.clone(),
}
}
}
pub struct ServocatWindowDispatch<T: UserEvent> {
proxy: EventLoopProxy<RuntimeEvent<T>>,
window_id: WindowId,
_marker: PhantomData<fn() -> T>,
}
impl<T: UserEvent> std::fmt::Debug for ServocatWindowDispatch<T> {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("ServocatWindowDispatch")
.field("window_id", &self.window_id)
.finish_non_exhaustive()
}
}
impl<T: UserEvent> Clone for ServocatWindowDispatch<T> {
fn clone(&self) -> Self {
Self {
proxy: self.proxy.clone(),
window_id: self.window_id,
_marker: PhantomData,
}
}
}
pub struct ServocatWebviewDispatch<T: UserEvent> {
proxy: EventLoopProxy<RuntimeEvent<T>>,
window_id: WindowId,
_marker: PhantomData<fn() -> T>,
}
impl<T: UserEvent> std::fmt::Debug for ServocatWebviewDispatch<T> {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("ServocatWebviewDispatch")
.field("window_id", &self.window_id)
.finish_non_exhaustive()
}
}
impl<T: UserEvent> Clone for ServocatWebviewDispatch<T> {
fn clone(&self) -> Self {
Self {
proxy: self.proxy.clone(),
window_id: self.window_id,
_marker: PhantomData,
}
}
}
pub struct ServocatEventLoopProxy<T: UserEvent> {
proxy: EventLoopProxy<RuntimeEvent<T>>,
}
impl<T: UserEvent> std::fmt::Debug for ServocatEventLoopProxy<T> {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("ServocatEventLoopProxy")
.finish_non_exhaustive()
}
}
impl<T: UserEvent> Clone for ServocatEventLoopProxy<T> {
fn clone(&self) -> Self {
Self {
proxy: self.proxy.clone(),
}
}
}
#[derive(Debug, Default, Clone)]
pub struct ServocatWindowBuilder {
title: Option<String>,
inner_size: Option<(f64, f64)>,
min_inner_size: Option<(f64, f64)>,
max_inner_size: Option<(f64, f64)>,
position: Option<(f64, f64)>,
resizable: Option<bool>,
maximized: Option<bool>,
visible: Option<bool>,
decorations: Option<bool>,
fullscreen: Option<bool>,
transparent: Option<bool>,
has_icon: bool,
theme: Option<Theme>,
}
impl ServocatWindowBuilder {
fn to_winit_attributes(&self) -> WindowAttributes {
let base = Window::default_attributes();
let with_title = self
.title
.as_ref()
.map_or(base.clone(), |title| base.clone().with_title(title));
let with_inner = self.inner_size.map_or(with_title.clone(), |(w, h)| {
with_title.clone().with_inner_size(LogicalSize::new(w, h))
});
let with_min = self.min_inner_size.map_or(with_inner.clone(), |(w, h)| {
with_inner
.clone()
.with_min_inner_size(LogicalSize::new(w, h))
});
let with_max = self.max_inner_size.map_or(with_min.clone(), |(w, h)| {
with_min.clone().with_max_inner_size(LogicalSize::new(w, h))
});
let with_position = self.position.map_or(with_max.clone(), |(x, y)| {
with_max.clone().with_position(LogicalPosition::new(x, y))
});
let with_resizable = self.resizable.map_or(with_position.clone(), |resizable| {
with_position.clone().with_resizable(resizable)
});
let with_maximized = self.maximized.map_or(with_resizable.clone(), |maximized| {
with_resizable.clone().with_maximized(maximized)
});
let with_visible = self.visible.map_or(with_maximized.clone(), |visible| {
with_maximized.clone().with_visible(visible)
});
let with_decorations = self
.decorations
.map_or(with_visible.clone(), |decorations| {
with_visible.clone().with_decorations(decorations)
});
let with_fullscreen =
self.fullscreen
.filter(|f| *f)
.map_or(with_decorations.clone(), |_fullscreen| {
with_decorations
.clone()
.with_fullscreen(Some(Fullscreen::Borderless(None)))
});
self.transparent
.map_or(with_fullscreen.clone(), |transparent| {
with_fullscreen.clone().with_transparent(transparent)
})
}
}
impl WindowBuilderBase for ServocatWindowBuilder {}
impl WindowBuilder for ServocatWindowBuilder {
fn new() -> Self {
Self::default()
}
fn with_config(_config: &WindowConfig) -> Self {
Self::default()
}
fn center(self) -> Self {
self
}
fn position(self, x: f64, y: f64) -> Self {
Self {
position: Some((x, y)),
..self
}
}
fn inner_size(self, width: f64, height: f64) -> Self {
Self {
inner_size: Some((width, height)),
..self
}
}
fn min_inner_size(self, min_width: f64, min_height: f64) -> Self {
Self {
min_inner_size: Some((min_width, min_height)),
..self
}
}
fn max_inner_size(self, max_width: f64, max_height: f64) -> Self {
Self {
max_inner_size: Some((max_width, max_height)),
..self
}
}
fn inner_size_constraints(self, _constraints: WindowSizeConstraints) -> Self {
self
}
fn prevent_overflow(self) -> Self {
self
}
fn prevent_overflow_with_margin(self, _margin: tauri_runtime::dpi::Size) -> Self {
self
}
fn resizable(self, resizable: bool) -> Self {
Self {
resizable: Some(resizable),
..self
}
}
fn maximizable(self, _maximizable: bool) -> Self {
self
}
fn minimizable(self, _minimizable: bool) -> Self {
self
}
fn closable(self, _closable: bool) -> Self {
self
}
fn title<S: Into<String>>(self, title: S) -> Self {
Self {
title: Some(title.into()),
..self
}
}
fn fullscreen(self, fullscreen: bool) -> Self {
Self {
fullscreen: Some(fullscreen),
..self
}
}
fn focused(self, _focused: bool) -> Self {
self
}
fn focusable(self, _focusable: bool) -> Self {
self
}
fn maximized(self, maximized: bool) -> Self {
Self {
maximized: Some(maximized),
..self
}
}
fn visible(self, visible: bool) -> Self {
Self {
visible: Some(visible),
..self
}
}
#[cfg(any(not(target_os = "macos"), feature = "macos-private-api"))]
fn transparent(self, transparent: bool) -> Self {
Self {
transparent: Some(transparent),
..self
}
}
fn decorations(self, decorations: bool) -> Self {
Self {
decorations: Some(decorations),
..self
}
}
fn always_on_bottom(self, _always_on_bottom: bool) -> Self {
self
}
fn always_on_top(self, _always_on_top: bool) -> Self {
self
}
fn visible_on_all_workspaces(self, _visible_on_all_workspaces: bool) -> Self {
self
}
fn content_protected(self, _protected: bool) -> Self {
self
}
fn icon(self, _icon: Icon<'_>) -> Result<Self> {
Ok(Self {
has_icon: true,
..self
})
}
fn skip_taskbar(self, _skip: bool) -> Self {
self
}
fn background_color(self, _color: Color) -> Self {
self
}
fn shadow(self, _enable: bool) -> Self {
self
}
#[cfg(target_os = "macos")]
fn parent(self, _parent: *mut std::ffi::c_void) -> Self {
self
}
#[cfg(target_os = "macos")]
fn title_bar_style(self, _style: TitleBarStyle) -> Self {
self
}
#[cfg(target_os = "macos")]
fn traffic_light_position<P: Into<tauri_runtime::dpi::Position>>(self, _position: P) -> Self {
self
}
#[cfg(target_os = "macos")]
fn hidden_title(self, _hidden: bool) -> Self {
self
}
#[cfg(target_os = "macos")]
fn tabbing_identifier(self, _identifier: &str) -> Self {
self
}
fn theme(self, theme: Option<Theme>) -> Self {
Self { theme, ..self }
}
fn has_icon(&self) -> bool {
self.has_icon
}
fn get_theme(&self) -> Option<Theme> {
self.theme
}
fn window_classname<S: Into<String>>(self, _window_classname: S) -> Self {
self
}
}
impl<T: UserEvent> TauriEventLoopProxy<T> for ServocatEventLoopProxy<T> {
fn send_event(&self, event: T) -> Result<()> {
self.proxy
.send_event(RuntimeEvent::User(event))
.map_err(|_e| Error::EventLoopClosed)
}
}
impl<T: UserEvent> Runtime<T> for ServocatRuntime<T> {
type WindowDispatcher = ServocatWindowDispatch<T>;
type WebviewDispatcher = ServocatWebviewDispatch<T>;
type Handle = ServocatHandle<T>;
type EventLoopProxy = ServocatEventLoopProxy<T>;
fn new(_args: RuntimeInitArgs) -> Result<Self> {
let event_loop = EventLoop::<RuntimeEvent<T>>::with_user_event()
.build()
.map_err(|_e| Error::CreateWindow)?;
let proxy = event_loop.create_proxy();
Ok(Self {
event_loop: Some(event_loop),
proxy,
})
}
fn create_proxy(&self) -> Self::EventLoopProxy {
ServocatEventLoopProxy {
proxy: self.proxy.clone(),
}
}
fn handle(&self) -> Self::Handle {
ServocatHandle {
proxy: self.proxy.clone(),
}
}
fn create_window<F: Fn(RawWindow) + Send + 'static>(
&self,
pending: PendingWindow<T, Self>,
_after_window_creation: Option<F>,
) -> Result<DetachedWindow<T, Self>> {
queue_window(&self.proxy, pending)
}
fn create_webview(
&self,
window_id: WindowId,
pending: PendingWebview<T, Self>,
) -> Result<DetachedWebview<T, Self>> {
queue_webview(&self.proxy, window_id, pending)
}
fn primary_monitor(&self) -> Option<Monitor> {
None
}
fn monitor_from_point(&self, _x: f64, _y: f64) -> Option<Monitor> {
None
}
fn available_monitors(&self) -> Vec<Monitor> {
Vec::new()
}
fn cursor_position(&self) -> Result<PhysicalPosition<f64>> {
Err(Error::FailedToGetCursorPosition)
}
fn set_theme(&self, _theme: Option<Theme>) {}
#[cfg(target_os = "macos")]
fn set_activation_policy(&mut self, _activation_policy: tauri_runtime::ActivationPolicy) {}
#[cfg(target_os = "macos")]
fn set_dock_visibility(&mut self, _visible: bool) {}
#[cfg(target_os = "macos")]
fn show(&self) {}
#[cfg(target_os = "macos")]
fn hide(&self) {}
fn set_device_event_filter(&mut self, _filter: DeviceEventFilter) {}
#[cfg(any(
target_os = "windows",
target_os = "macos",
target_os = "linux",
target_os = "freebsd",
target_os = "dragonfly",
target_os = "netbsd",
target_os = "openbsd"
))]
fn run_iteration<F: FnMut(RunEvent<T>) + 'static>(&mut self, _callback: F) {}
fn run_return<F: FnMut(RunEvent<T>) + 'static>(self, callback: F) -> i32 {
self.run(callback);
0
}
fn run<F: FnMut(RunEvent<T>) + 'static>(mut self, callback: F) {
let _ = self.event_loop.take().map(|event_loop| {
let mut app = AppHandler::new(callback);
let _ = event_loop.run_app(&mut app);
});
}
}
impl<T: UserEvent> RuntimeHandle<T> for ServocatHandle<T> {
type Runtime = ServocatRuntime<T>;
fn create_proxy(&self) -> <Self::Runtime as Runtime<T>>::EventLoopProxy {
ServocatEventLoopProxy {
proxy: self.proxy.clone(),
}
}
#[cfg(target_os = "macos")]
fn set_activation_policy(
&self,
_activation_policy: tauri_runtime::ActivationPolicy,
) -> Result<()> {
Ok(())
}
#[cfg(target_os = "macos")]
fn set_dock_visibility(&self, _visible: bool) -> Result<()> {
Ok(())
}
fn request_exit(&self, code: i32) -> Result<()> {
self.proxy
.send_event(RuntimeEvent::Exit { code })
.map_err(|_e| Error::EventLoopClosed)
}
fn create_window<F: Fn(RawWindow) + Send + 'static>(
&self,
pending: PendingWindow<T, Self::Runtime>,
_after_window_creation: Option<F>,
) -> Result<DetachedWindow<T, Self::Runtime>> {
queue_window(&self.proxy, pending)
}
fn create_webview(
&self,
window_id: WindowId,
pending: PendingWebview<T, Self::Runtime>,
) -> Result<DetachedWebview<T, Self::Runtime>> {
queue_webview(&self.proxy, window_id, pending)
}
fn run_on_main_thread<F: FnOnce() + Send + 'static>(&self, f: F) -> Result<()> {
self.proxy
.send_event(RuntimeEvent::RunOnMainThread { thunk: Box::new(f) })
.map_err(|_e| Error::EventLoopClosed)
}
fn display_handle(&self) -> std::result::Result<DisplayHandle<'_>, HandleError> {
Err(HandleError::Unavailable)
}
fn primary_monitor(&self) -> Option<Monitor> {
query(&self.proxy, |reply| RuntimeEvent::QueryPrimaryMonitor {
reply,
})
.ok()
.flatten()
}
fn monitor_from_point(&self, x: f64, y: f64) -> Option<Monitor> {
query(&self.proxy, |reply| RuntimeEvent::QueryMonitorFromPoint {
x,
y,
reply,
})
.ok()
.flatten()
}
fn available_monitors(&self) -> Vec<Monitor> {
query(&self.proxy, |reply| RuntimeEvent::QueryAvailableMonitors {
reply,
})
.unwrap_or_default()
}
fn cursor_position(&self) -> Result<PhysicalPosition<f64>> {
Err(Error::FailedToGetCursorPosition)
}
fn set_theme(&self, _theme: Option<Theme>) {}
#[cfg(target_os = "macos")]
fn show(&self) -> Result<()> {
Ok(())
}
#[cfg(target_os = "macos")]
fn hide(&self) -> Result<()> {
Ok(())
}
fn set_device_event_filter(&self, _filter: DeviceEventFilter) {}
#[cfg(any(target_os = "macos", target_os = "ios"))]
fn fetch_data_store_identifiers<F: FnOnce(Vec<[u8; 16]>) + Send + 'static>(
&self,
_cb: F,
) -> Result<()> {
Err(Error::FailedToRemoveDataStore)
}
#[cfg(any(target_os = "macos", target_os = "ios"))]
fn remove_data_store<F: FnOnce(Result<()>) + Send + 'static>(
&self,
_uuid: [u8; 16],
_cb: F,
) -> Result<()> {
Err(Error::FailedToRemoveDataStore)
}
}
impl<T: UserEvent> WindowDispatch<T> for ServocatWindowDispatch<T> {
type Runtime = ServocatRuntime<T>;
type WindowBuilder = ServocatWindowBuilder;
fn run_on_main_thread<F: FnOnce() + Send + 'static>(&self, f: F) -> Result<()> {
self.proxy
.send_event(RuntimeEvent::RunOnMainThread { thunk: Box::new(f) })
.map_err(|_e| Error::EventLoopClosed)
}
fn on_window_event<F: Fn(&TauriWindowEvent) + Send + 'static>(&self, _f: F) -> WindowEventId {
0
}
fn scale_factor(&self) -> Result<f64> {
query(&self.proxy, |reply| RuntimeEvent::QueryScaleFactor {
id: self.window_id,
reply,
})
}
fn inner_position(&self) -> Result<PhysicalPosition<i32>> {
query(&self.proxy, |reply| RuntimeEvent::QueryInnerPosition {
id: self.window_id,
reply,
})
}
fn outer_position(&self) -> Result<PhysicalPosition<i32>> {
query(&self.proxy, |reply| RuntimeEvent::QueryOuterPosition {
id: self.window_id,
reply,
})
}
fn inner_size(&self) -> Result<PhysicalSize<u32>> {
query(&self.proxy, |reply| RuntimeEvent::QueryInnerSize {
id: self.window_id,
reply,
})
}
fn outer_size(&self) -> Result<PhysicalSize<u32>> {
query(&self.proxy, |reply| RuntimeEvent::QueryOuterSize {
id: self.window_id,
reply,
})
}
fn is_fullscreen(&self) -> Result<bool> {
query(&self.proxy, |reply| RuntimeEvent::QueryIsFullscreen {
id: self.window_id,
reply,
})
}
fn is_minimized(&self) -> Result<bool> {
query(&self.proxy, |reply| RuntimeEvent::QueryIsMinimized {
id: self.window_id,
reply,
})
}
fn is_maximized(&self) -> Result<bool> {
query(&self.proxy, |reply| RuntimeEvent::QueryIsMaximized {
id: self.window_id,
reply,
})
}
fn is_focused(&self) -> Result<bool> {
query(&self.proxy, |reply| RuntimeEvent::QueryIsFocused {
id: self.window_id,
reply,
})
}
fn is_decorated(&self) -> Result<bool> {
query(&self.proxy, |reply| RuntimeEvent::QueryIsDecorated {
id: self.window_id,
reply,
})
}
fn is_resizable(&self) -> Result<bool> {
query(&self.proxy, |reply| RuntimeEvent::QueryIsResizable {
id: self.window_id,
reply,
})
}
fn is_maximizable(&self) -> Result<bool> {
Ok(false)
}
fn is_minimizable(&self) -> Result<bool> {
Ok(false)
}
fn is_closable(&self) -> Result<bool> {
Ok(false)
}
fn is_visible(&self) -> Result<bool> {
query(&self.proxy, |reply| RuntimeEvent::QueryIsVisible {
id: self.window_id,
reply,
})
}
fn is_enabled(&self) -> Result<bool> {
Ok(false)
}
fn is_always_on_top(&self) -> Result<bool> {
Ok(false)
}
fn title(&self) -> Result<String> {
query(&self.proxy, |reply| RuntimeEvent::QueryTitle {
id: self.window_id,
reply,
})
}
fn current_monitor(&self) -> Result<Option<Monitor>> {
query(&self.proxy, |reply| RuntimeEvent::QueryCurrentMonitor {
id: self.window_id,
reply,
})
}
fn primary_monitor(&self) -> Result<Option<Monitor>> {
query(&self.proxy, |reply| RuntimeEvent::QueryPrimaryMonitor {
reply,
})
}
fn monitor_from_point(&self, x: f64, y: f64) -> Result<Option<Monitor>> {
query(&self.proxy, |reply| RuntimeEvent::QueryMonitorFromPoint {
x,
y,
reply,
})
}
fn available_monitors(&self) -> Result<Vec<Monitor>> {
query(&self.proxy, |reply| RuntimeEvent::QueryAvailableMonitors {
reply,
})
}
fn window_handle(&self) -> std::result::Result<WindowHandle<'_>, HandleError> {
Err(HandleError::Unavailable)
}
fn theme(&self) -> Result<Theme> {
query(&self.proxy, |reply| RuntimeEvent::QueryTheme {
id: self.window_id,
reply,
})
}
fn center(&self) -> Result<()> {
Ok(())
}
fn request_user_attention(
&self,
_request_type: Option<tauri_runtime::UserAttentionType>,
) -> Result<()> {
Ok(())
}
fn create_window<F: Fn(RawWindow) + Send + 'static>(
&mut self,
pending: PendingWindow<T, Self::Runtime>,
_after_window_creation: Option<F>,
) -> Result<DetachedWindow<T, Self::Runtime>> {
queue_window(&self.proxy, pending)
}
fn create_webview(
&mut self,
pending: PendingWebview<T, Self::Runtime>,
) -> Result<DetachedWebview<T, Self::Runtime>> {
queue_webview(&self.proxy, self.window_id, pending)
}
fn set_resizable(&self, resizable: bool) -> Result<()> {
self.proxy
.send_event(RuntimeEvent::SetResizable {
id: self.window_id,
resizable,
})
.map_err(|_e| Error::EventLoopClosed)
}
fn set_enabled(&self, _enabled: bool) -> Result<()> {
Ok(())
}
fn set_maximizable(&self, _maximizable: bool) -> Result<()> {
Ok(())
}
fn set_minimizable(&self, _minimizable: bool) -> Result<()> {
Ok(())
}
fn set_closable(&self, _closable: bool) -> Result<()> {
Ok(())
}
fn set_title<S: Into<String>>(&self, title: S) -> Result<()> {
self.proxy
.send_event(RuntimeEvent::SetTitle {
id: self.window_id,
title: title.into(),
})
.map_err(|_e| Error::EventLoopClosed)
}
fn maximize(&self) -> Result<()> {
self.proxy
.send_event(RuntimeEvent::Maximize { id: self.window_id })
.map_err(|_e| Error::EventLoopClosed)
}
fn unmaximize(&self) -> Result<()> {
self.proxy
.send_event(RuntimeEvent::Unmaximize { id: self.window_id })
.map_err(|_e| Error::EventLoopClosed)
}
fn minimize(&self) -> Result<()> {
self.proxy
.send_event(RuntimeEvent::Minimize { id: self.window_id })
.map_err(|_e| Error::EventLoopClosed)
}
fn unminimize(&self) -> Result<()> {
self.proxy
.send_event(RuntimeEvent::Unminimize { id: self.window_id })
.map_err(|_e| Error::EventLoopClosed)
}
fn show(&self) -> Result<()> {
self.proxy
.send_event(RuntimeEvent::Show { id: self.window_id })
.map_err(|_e| Error::EventLoopClosed)
}
fn hide(&self) -> Result<()> {
self.proxy
.send_event(RuntimeEvent::Hide { id: self.window_id })
.map_err(|_e| Error::EventLoopClosed)
}
fn close(&self) -> Result<()> {
self.proxy
.send_event(RuntimeEvent::Close { id: self.window_id })
.map_err(|_e| Error::EventLoopClosed)
}
fn destroy(&self) -> Result<()> {
self.proxy
.send_event(RuntimeEvent::Destroy { id: self.window_id })
.map_err(|_e| Error::EventLoopClosed)
}
fn set_decorations(&self, decorations: bool) -> Result<()> {
self.proxy
.send_event(RuntimeEvent::SetDecorations {
id: self.window_id,
decorations,
})
.map_err(|_e| Error::EventLoopClosed)
}
fn set_shadow(&self, _enable: bool) -> Result<()> {
Ok(())
}
fn set_always_on_bottom(&self, _always_on_bottom: bool) -> Result<()> {
Ok(())
}
fn set_always_on_top(&self, _always_on_top: bool) -> Result<()> {
Ok(())
}
fn set_visible_on_all_workspaces(&self, _visible_on_all_workspaces: bool) -> Result<()> {
Ok(())
}
fn set_background_color(&self, _color: Option<Color>) -> Result<()> {
Ok(())
}
fn set_content_protected(&self, _protected: bool) -> Result<()> {
Ok(())
}
fn set_size(&self, size: Size) -> Result<()> {
self.proxy
.send_event(RuntimeEvent::SetSize {
id: self.window_id,
size,
})
.map_err(|_e| Error::EventLoopClosed)
}
fn set_min_size(&self, _size: Option<Size>) -> Result<()> {
Ok(())
}
fn set_max_size(&self, _size: Option<Size>) -> Result<()> {
Ok(())
}
fn set_size_constraints(&self, _constraints: WindowSizeConstraints) -> Result<()> {
Ok(())
}
fn set_position(&self, position: Position) -> Result<()> {
self.proxy
.send_event(RuntimeEvent::SetPosition {
id: self.window_id,
position,
})
.map_err(|_e| Error::EventLoopClosed)
}
fn set_fullscreen(&self, fullscreen: bool) -> Result<()> {
self.proxy
.send_event(RuntimeEvent::SetFullscreen {
id: self.window_id,
fullscreen,
})
.map_err(|_e| Error::EventLoopClosed)
}
#[cfg(target_os = "macos")]
fn set_simple_fullscreen(&self, _enable: bool) -> Result<()> {
Ok(())
}
fn set_focus(&self) -> Result<()> {
self.proxy
.send_event(RuntimeEvent::Focus { id: self.window_id })
.map_err(|_e| Error::EventLoopClosed)
}
fn set_focusable(&self, _focusable: bool) -> Result<()> {
Ok(())
}
fn set_icon(&self, _icon: Icon<'_>) -> Result<()> {
Ok(())
}
fn set_skip_taskbar(&self, _skip: bool) -> Result<()> {
Ok(())
}
fn set_cursor_grab(&self, _grab: bool) -> Result<()> {
Ok(())
}
fn set_cursor_visible(&self, _visible: bool) -> Result<()> {
Ok(())
}
fn set_cursor_icon(&self, _icon: CursorIcon) -> Result<()> {
Ok(())
}
fn set_cursor_position<Pos: Into<Position>>(&self, _position: Pos) -> Result<()> {
Ok(())
}
fn set_ignore_cursor_events(&self, _ignore: bool) -> Result<()> {
Ok(())
}
fn start_dragging(&self) -> Result<()> {
Ok(())
}
fn start_resize_dragging(&self, _direction: tauri_runtime::ResizeDirection) -> Result<()> {
Ok(())
}
fn set_badge_count(
&self,
_count: Option<i64>,
_desktop_filename: Option<String>,
) -> Result<()> {
Ok(())
}
fn set_badge_label(&self, _label: Option<String>) -> Result<()> {
Ok(())
}
fn set_overlay_icon(&self, _icon: Option<Icon<'_>>) -> Result<()> {
Ok(())
}
fn set_progress_bar(&self, _progress_state: ProgressBarState) -> Result<()> {
Ok(())
}
fn set_title_bar_style(&self, _style: TitleBarStyle) -> Result<()> {
Ok(())
}
fn set_traffic_light_position(&self, _position: Position) -> Result<()> {
Ok(())
}
fn set_theme(&self, _theme: Option<Theme>) -> Result<()> {
Ok(())
}
}
impl<T: UserEvent> WebviewDispatch<T> for ServocatWebviewDispatch<T> {
type Runtime = ServocatRuntime<T>;
fn run_on_main_thread<F: FnOnce() + Send + 'static>(&self, f: F) -> Result<()> {
self.proxy
.send_event(RuntimeEvent::RunOnMainThread { thunk: Box::new(f) })
.map_err(|_e| Error::EventLoopClosed)
}
fn on_webview_event<F: Fn(&WebviewEvent) + Send + 'static>(&self, _f: F) -> WebviewEventId {
0
}
fn with_webview<F: FnOnce(Box<dyn std::any::Any>) + Send + 'static>(
&self,
_f: F,
) -> Result<()> {
Err(Error::FailedToSendMessage)
}
#[cfg(any(debug_assertions, feature = "devtools"))]
fn open_devtools(&self) {}
#[cfg(any(debug_assertions, feature = "devtools"))]
fn close_devtools(&self) {}
#[cfg(any(debug_assertions, feature = "devtools"))]
fn is_devtools_open(&self) -> Result<bool> {
Ok(false)
}
fn url(&self) -> Result<String> {
Ok(String::new())
}
fn bounds(&self) -> Result<Rect> {
Ok(Rect {
position: tauri_runtime::dpi::Position::Physical(PhysicalPosition::new(0, 0)),
size: tauri_runtime::dpi::Size::Physical(PhysicalSize::new(0, 0)),
})
}
fn position(&self) -> Result<PhysicalPosition<i32>> {
Ok(PhysicalPosition::new(0, 0))
}
fn size(&self) -> Result<PhysicalSize<u32>> {
Ok(PhysicalSize::new(0, 0))
}
fn navigate(&self, url: Url) -> Result<()> {
self.proxy
.send_event(RuntimeEvent::Navigate {
window_id: self.window_id,
url: url.into(),
})
.map_err(|_e| Error::EventLoopClosed)
}
fn reload(&self) -> Result<()> {
self.proxy
.send_event(RuntimeEvent::Reload {
window_id: self.window_id,
})
.map_err(|_e| Error::EventLoopClosed)
}
fn print(&self) -> Result<()> {
Err(Error::FailedToSendMessage)
}
fn close(&self) -> Result<()> {
self.proxy
.send_event(RuntimeEvent::Close { id: self.window_id })
.map_err(|_e| Error::EventLoopClosed)
}
fn set_bounds(&self, _bounds: Rect) -> Result<()> {
Ok(())
}
fn set_size(&self, _size: Size) -> Result<()> {
Ok(())
}
fn set_position(&self, _position: Position) -> Result<()> {
Ok(())
}
fn set_focus(&self) -> Result<()> {
self.proxy
.send_event(RuntimeEvent::Focus { id: self.window_id })
.map_err(|_e| Error::EventLoopClosed)
}
fn hide(&self) -> Result<()> {
Ok(())
}
fn show(&self) -> Result<()> {
Ok(())
}
fn eval_script<S: Into<String>>(&self, script: S) -> Result<()> {
self.proxy
.send_event(RuntimeEvent::EvalScript {
window_id: self.window_id,
script: script.into(),
})
.map_err(|_e| Error::EventLoopClosed)
}
fn eval_script_with_callback<S: Into<String>>(
&self,
script: S,
callback: impl Fn(String) + Send + 'static,
) -> Result<()> {
self.proxy
.send_event(RuntimeEvent::EvalScriptWithCallback {
window_id: self.window_id,
script: script.into(),
callback: Box::new(callback),
})
.map_err(|_e| Error::EventLoopClosed)
}
fn reparent(&self, _window_id: WindowId) -> Result<()> {
Err(Error::WindowNotFound)
}
fn cookies_for_url(&self, _url: Url) -> Result<Vec<Cookie<'static>>> {
Ok(Vec::new())
}
fn cookies(&self) -> Result<Vec<Cookie<'static>>> {
Ok(Vec::new())
}
fn set_cookie(&self, _cookie: Cookie<'_>) -> Result<()> {
Err(Error::FailedToSendMessage)
}
fn delete_cookie(&self, _cookie: Cookie<'_>) -> Result<()> {
Err(Error::FailedToSendMessage)
}
fn set_auto_resize(&self, _auto_resize: bool) -> Result<()> {
Ok(())
}
fn set_zoom(&self, _scale_factor: f64) -> Result<()> {
Ok(())
}
fn set_background_color(&self, _color: Option<Color>) -> Result<()> {
Ok(())
}
fn clear_all_browsing_data(&self) -> Result<()> {
Ok(())
}
}
fn queue_window<T: UserEvent>(
proxy: &EventLoopProxy<RuntimeEvent<T>>,
pending: PendingWindow<T, ServocatRuntime<T>>,
) -> Result<DetachedWindow<T, ServocatRuntime<T>>> {
let id = allocate_window_id();
let label = pending.label;
let attributes = pending.window_builder.clone();
proxy
.send_event(RuntimeEvent::CreateWindow {
id,
label: label.clone(),
attributes,
})
.map_err(|_e| Error::CreateWindow)?;
Ok(DetachedWindow {
id,
label,
dispatcher: ServocatWindowDispatch {
proxy: proxy.clone(),
window_id: id,
_marker: PhantomData,
},
webview: None,
})
}
fn query<T, R, F>(proxy: &EventLoopProxy<RuntimeEvent<T>>, builder: F) -> Result<R>
where
T: UserEvent,
R: Send + 'static,
F: FnOnce(mpsc::Sender<R>) -> RuntimeEvent<T>,
{
let (tx, rx) = mpsc::channel::<R>();
proxy
.send_event(builder(tx))
.map_err(|_e| Error::EventLoopClosed)?;
rx.recv_timeout(Duration::from_millis(500))
.map_err(|_e| Error::FailedToReceiveMessage)
}
fn queue_webview<T: UserEvent>(
proxy: &EventLoopProxy<RuntimeEvent<T>>,
window_id: WindowId,
pending: PendingWebview<T, ServocatRuntime<T>>,
) -> Result<DetachedWebview<T, ServocatRuntime<T>>> {
let label = pending.label;
proxy
.send_event(RuntimeEvent::CreateWebview {
window_id,
label: label.clone(),
url: pending.url,
})
.map_err(|err| Error::CreateWebview(skeleton_error_from(err.to_string())))?;
Ok(DetachedWebview {
label,
dispatcher: ServocatWebviewDispatch {
proxy: proxy.clone(),
window_id,
_marker: PhantomData,
},
})
}
struct WebviewState {
html: String,
css: String,
viewport: Viewport,
current_frame: Option<Frame>,
text_renderer: TextRenderer,
}
impl WebviewState {
fn new(html: String, viewport: Viewport) -> Self {
Self {
html,
css: String::new(),
viewport,
current_frame: None,
text_renderer: TextRenderer::new(),
}
}
fn rebuild_frame(&mut self) {
self.current_frame = crate::pipeline::render(&self.html, &self.css, self.viewport).ok();
}
fn eval(&mut self, script: &str) -> Option<String> {
let frame = run_script_with_backprop(
&self.html,
&self.css,
script,
self.viewport,
&HostCommands::new(),
)
.ok()?;
let value = format!("{}", frame.script_value());
self.current_frame = Some(frame);
Some(value)
}
}
fn html_from_url(url: &str) -> String {
try_data_url(url)
.or_else(|| try_file_url(url))
.or_else(|| try_http_url(url))
.unwrap_or_else(|| {
format!(
"<html><body><h1>tauri-runtime-servocat</h1>\
<p>Unsupported URL scheme: <code>{url}</code></p>\
</body></html>"
)
})
}
fn try_data_url(url: &str) -> Option<String> {
let parsed = url::Url::parse(url).ok()?;
(parsed.scheme() == "data").then_some(())?;
let (_mime, body) = parsed.path().split_once(',')?;
let decoded = percent_encoding::percent_decode_str(body)
.decode_utf8()
.ok()?;
Some(decoded.into_owned())
}
fn try_file_url(url: &str) -> Option<String> {
let parsed = url::Url::parse(url).ok()?;
(parsed.scheme() == "file").then_some(())?;
let path = parsed.to_file_path().ok()?;
std::fs::read_to_string(path).ok()
}
fn try_http_url(url: &str) -> Option<String> {
let parsed = url::Url::parse(url).ok()?;
(parsed.scheme() == "http").then_some(())?;
let net_url = net_cat::url::Url::parse(url).ok()?;
let request = net_cat::request::Request::new(net_cat::method::Method::Get, net_url);
let response = net_cat::fetch(&request).ok()?;
Some(response.body_text())
}
fn winit_monitor_to_tauri(handle: &winit::monitor::MonitorHandle) -> Monitor {
let size = handle.size();
let position = handle.position();
let physical_size = PhysicalSize::new(size.width, size.height);
let physical_position = PhysicalPosition::new(position.x, position.y);
Monitor {
name: handle.name(),
size: physical_size,
position: physical_position,
work_area: PhysicalRect {
position: physical_position,
size: physical_size,
},
scale_factor: handle.scale_factor(),
}
}
fn skeleton_error_from(msg: String) -> Box<dyn std::error::Error + Send + Sync> {
Box::from(msg)
}
struct AppHandler<T: UserEvent, F: FnMut(RunEvent<T>) + 'static> {
callback: F,
windows: BTreeMap<u32, Window>,
labels: BTreeMap<u32, String>,
winit_to_tauri: BTreeMap<WinitWindowId, u32>,
webviews: BTreeMap<u32, WebviewState>,
ready_dispatched: bool,
_marker: PhantomData<fn() -> T>,
}
impl<T: UserEvent, F: FnMut(RunEvent<T>) + 'static> AppHandler<T, F> {
fn new(callback: F) -> Self {
Self {
callback,
windows: BTreeMap::new(),
labels: BTreeMap::new(),
winit_to_tauri: BTreeMap::new(),
webviews: BTreeMap::new(),
ready_dispatched: false,
_marker: PhantomData,
}
}
fn dispatch_window_event(&mut self, raw_id: u32, event: TauriWindowEvent) {
let label = self.labels.get(&raw_id).cloned().unwrap_or_default();
(self.callback)(RunEvent::WindowEvent { label, event });
}
fn request_redraw(&self, raw_id: u32) {
let _ = self.windows.get(&raw_id).map(Window::request_redraw);
}
fn present(&mut self, raw_id: u32) {
let window_opt = self.windows.get(&raw_id);
let webview_opt = self.webviews.get_mut(&raw_id);
let _ = window_opt
.zip(webview_opt)
.and_then(|(window, webview)| present_webview(window, webview));
}
}
fn present_webview(window: &Window, webview: &mut WebviewState) -> Option<()> {
let size = window.inner_size();
let width = NonZeroU32::new(size.width)?;
let height = NonZeroU32::new(size.height)?;
let frame = webview.current_frame.as_ref()?;
let pixels = render_to_pixels_with(frame, size.width, size.height, &mut webview.text_renderer);
let context = Context::new(window).ok()?;
let mut surface = Surface::new(&context, window).ok()?;
surface.resize(width, height).ok()?;
let mut buffer = surface.buffer_mut().ok()?;
write_pixels(&pixels, &mut buffer);
buffer.present().ok()?;
Some(())
}
fn write_pixels(pixels: &PixelBuffer, buffer: &mut softbuffer::Buffer<'_, &Window, &Window>) {
pixels
.rgba()
.chunks_exact(4)
.map(rgba_to_softbuffer_word)
.zip(buffer.iter_mut())
.for_each(|(word, slot)| *slot = word);
}
fn rgba_to_softbuffer_word(chunk: &[u8]) -> u32 {
let red = chunk.first().copied().unwrap_or(0);
let green = chunk.get(1).copied().unwrap_or(0);
let blue = chunk.get(2).copied().unwrap_or(0);
let alpha = chunk.get(3).copied().unwrap_or(0);
let inv = 255_u8 - alpha;
let out_r = red.saturating_add(inv);
let out_g = green.saturating_add(inv);
let out_b = blue.saturating_add(inv);
(u32::from(out_r) << 16) | (u32::from(out_g) << 8) | u32::from(out_b)
}
impl<T: UserEvent, F: FnMut(RunEvent<T>) + 'static> ApplicationHandler<RuntimeEvent<T>>
for AppHandler<T, F>
{
fn resumed(&mut self, _event_loop: &ActiveEventLoop) {
if !self.ready_dispatched {
(self.callback)(RunEvent::Ready);
self.ready_dispatched = true;
}
(self.callback)(RunEvent::Resumed);
}
fn user_event(&mut self, event_loop: &ActiveEventLoop, event: RuntimeEvent<T>) {
match event {
RuntimeEvent::User(payload) => (self.callback)(RunEvent::UserEvent(payload)),
RuntimeEvent::CreateWindow {
id,
label,
attributes,
} => {
let raw = raw_window_id(id);
let attrs = attributes.to_winit_attributes();
let _ = event_loop.create_window(attrs).map(|window| {
let winit_id = window.id();
let _ = self.winit_to_tauri.insert(winit_id, raw);
let _ = self.windows.insert(raw, window);
let _ = self.labels.insert(raw, label);
});
}
RuntimeEvent::SetTitle { id, title } => {
let raw = raw_window_id(id);
let _ = self
.windows
.get(&raw)
.map(|window| window.set_title(&title));
}
RuntimeEvent::SetSize { id, size } => {
let raw = raw_window_id(id);
let _ = self.windows.get(&raw).map(|window| match size {
Size::Logical(s) => {
let _ = window.request_inner_size(LogicalSize::new(s.width, s.height));
}
Size::Physical(s) => {
let _ = window
.request_inner_size(winit::dpi::PhysicalSize::new(s.width, s.height));
}
});
}
RuntimeEvent::SetPosition { id, position } => {
let raw = raw_window_id(id);
let _ = self.windows.get(&raw).map(|window| match position {
Position::Logical(p) => {
window.set_outer_position(LogicalPosition::new(p.x, p.y));
}
Position::Physical(p) => {
window.set_outer_position(WinitPhysicalPosition::new(p.x, p.y));
}
});
}
RuntimeEvent::Show { id } => {
let raw = raw_window_id(id);
let _ = self
.windows
.get(&raw)
.map(|window| window.set_visible(true));
}
RuntimeEvent::Hide { id } => {
let raw = raw_window_id(id);
let _ = self
.windows
.get(&raw)
.map(|window| window.set_visible(false));
}
RuntimeEvent::Focus { id } => {
let raw = raw_window_id(id);
let _ = self.windows.get(&raw).map(Window::focus_window);
}
RuntimeEvent::Close { id } | RuntimeEvent::Destroy { id } => {
let raw = raw_window_id(id);
let _ = self.windows.remove(&raw).map(|window| {
let winit_id = window.id();
let _ = self.winit_to_tauri.remove(&winit_id);
drop(window);
});
self.dispatch_window_event(raw, TauriWindowEvent::Destroyed);
let _ = self.labels.remove(&raw);
}
RuntimeEvent::Maximize { id } => {
let raw = raw_window_id(id);
let _ = self
.windows
.get(&raw)
.map(|window| window.set_maximized(true));
}
RuntimeEvent::Unmaximize { id } => {
let raw = raw_window_id(id);
let _ = self
.windows
.get(&raw)
.map(|window| window.set_maximized(false));
}
RuntimeEvent::Minimize { id } => {
let raw = raw_window_id(id);
let _ = self
.windows
.get(&raw)
.map(|window| window.set_minimized(true));
}
RuntimeEvent::Unminimize { id } => {
let raw = raw_window_id(id);
let _ = self
.windows
.get(&raw)
.map(|window| window.set_minimized(false));
}
RuntimeEvent::SetResizable { id, resizable } => {
let raw = raw_window_id(id);
let _ = self
.windows
.get(&raw)
.map(|window| window.set_resizable(resizable));
}
RuntimeEvent::SetDecorations { id, decorations } => {
let raw = raw_window_id(id);
let _ = self
.windows
.get(&raw)
.map(|window| window.set_decorations(decorations));
}
RuntimeEvent::SetFullscreen { id, fullscreen } => {
let raw = raw_window_id(id);
let _ = self.windows.get(&raw).map(|window| {
let mode = fullscreen.then(|| Fullscreen::Borderless(None));
window.set_fullscreen(mode);
});
}
RuntimeEvent::CreateWebview {
window_id,
label: _label,
url,
} => {
let raw = raw_window_id(window_id);
let viewport = self.windows.get(&raw).map_or_else(
|| Viewport::new(800, 600),
|window| {
let size = window.inner_size();
Viewport::new(size.width, size.height)
},
);
let html = html_from_url(&url);
let mut state = WebviewState::new(html, viewport);
state.rebuild_frame();
let _ = self.webviews.insert(raw, state);
self.request_redraw(raw);
}
RuntimeEvent::Navigate { window_id, url } => {
let raw = raw_window_id(window_id);
let html = html_from_url(&url);
let _ = self.webviews.get_mut(&raw).map(|webview| {
webview.html = html;
webview.rebuild_frame();
});
self.request_redraw(raw);
}
RuntimeEvent::Reload { window_id } => {
let raw = raw_window_id(window_id);
let _ = self.webviews.get_mut(&raw).map(WebviewState::rebuild_frame);
self.request_redraw(raw);
}
RuntimeEvent::EvalScript { window_id, script } => {
let raw = raw_window_id(window_id);
let _ = self
.webviews
.get_mut(&raw)
.map(|webview| webview.eval(&script));
self.request_redraw(raw);
}
RuntimeEvent::EvalScriptWithCallback {
window_id,
script,
callback,
} => {
let raw = raw_window_id(window_id);
let result = self
.webviews
.get_mut(&raw)
.and_then(|webview| webview.eval(&script))
.unwrap_or_default();
callback(result);
self.request_redraw(raw);
}
RuntimeEvent::QueryInnerSize { id, reply } => {
let raw = raw_window_id(id);
let size = self.windows.get(&raw).map_or_else(
|| PhysicalSize::new(0, 0),
|window| {
let s = window.inner_size();
PhysicalSize::new(s.width, s.height)
},
);
let _ = reply.send(size);
}
RuntimeEvent::QueryOuterSize { id, reply } => {
let raw = raw_window_id(id);
let size = self.windows.get(&raw).map_or_else(
|| PhysicalSize::new(0, 0),
|window| {
let s = window.outer_size();
PhysicalSize::new(s.width, s.height)
},
);
let _ = reply.send(size);
}
RuntimeEvent::QueryInnerPosition { id, reply } => {
let raw = raw_window_id(id);
let position = self.windows.get(&raw).map_or_else(
|| PhysicalPosition::new(0, 0),
|window| {
window.inner_position().map_or_else(
|_e| PhysicalPosition::new(0, 0),
|p| PhysicalPosition::new(p.x, p.y),
)
},
);
let _ = reply.send(position);
}
RuntimeEvent::QueryOuterPosition { id, reply } => {
let raw = raw_window_id(id);
let position = self.windows.get(&raw).map_or_else(
|| PhysicalPosition::new(0, 0),
|window| {
window.outer_position().map_or_else(
|_e| PhysicalPosition::new(0, 0),
|p| PhysicalPosition::new(p.x, p.y),
)
},
);
let _ = reply.send(position);
}
RuntimeEvent::QueryScaleFactor { id, reply } => {
let raw = raw_window_id(id);
let factor = self
.windows
.get(&raw)
.map_or(1.0, winit::window::Window::scale_factor);
let _ = reply.send(factor);
}
RuntimeEvent::QueryTitle { id, reply } => {
let raw = raw_window_id(id);
let title = self
.windows
.get(&raw)
.map_or_else(String::new, winit::window::Window::title);
let _ = reply.send(title);
}
RuntimeEvent::QueryIsVisible { id, reply } => {
let raw = raw_window_id(id);
let visible = self
.windows
.get(&raw)
.and_then(winit::window::Window::is_visible)
.unwrap_or(false);
let _ = reply.send(visible);
}
RuntimeEvent::QueryIsFocused { id, reply } => {
let raw = raw_window_id(id);
let focused = self
.windows
.get(&raw)
.is_some_and(winit::window::Window::has_focus);
let _ = reply.send(focused);
}
RuntimeEvent::QueryIsMaximized { id, reply } => {
let raw = raw_window_id(id);
let maximized = self
.windows
.get(&raw)
.is_some_and(winit::window::Window::is_maximized);
let _ = reply.send(maximized);
}
RuntimeEvent::QueryIsMinimized { id, reply } => {
let raw = raw_window_id(id);
let minimized = self
.windows
.get(&raw)
.and_then(winit::window::Window::is_minimized)
.unwrap_or(false);
let _ = reply.send(minimized);
}
RuntimeEvent::QueryIsFullscreen { id, reply } => {
let raw = raw_window_id(id);
let fullscreen = self
.windows
.get(&raw)
.is_some_and(|w| w.fullscreen().is_some());
let _ = reply.send(fullscreen);
}
RuntimeEvent::QueryIsDecorated { id, reply } => {
let raw = raw_window_id(id);
let decorated = self
.windows
.get(&raw)
.is_some_and(winit::window::Window::is_decorated);
let _ = reply.send(decorated);
}
RuntimeEvent::QueryIsResizable { id, reply } => {
let raw = raw_window_id(id);
let resizable = self
.windows
.get(&raw)
.is_some_and(winit::window::Window::is_resizable);
let _ = reply.send(resizable);
}
RuntimeEvent::QueryPrimaryMonitor { reply } => {
let monitor = event_loop
.primary_monitor()
.as_ref()
.map(winit_monitor_to_tauri);
let _ = reply.send(monitor);
}
RuntimeEvent::QueryAvailableMonitors { reply } => {
let monitors: Vec<Monitor> = event_loop
.available_monitors()
.map(|handle| winit_monitor_to_tauri(&handle))
.collect();
let _ = reply.send(monitors);
}
RuntimeEvent::QueryMonitorFromPoint { x, y, reply } => {
let position = winit::dpi::PhysicalPosition::new(x, y);
let monitor = event_loop
.available_monitors()
.find(|handle| {
let pos = handle.position();
let size = handle.size();
let in_x = position.x >= f64::from(pos.x)
&& position.x < f64::from(pos.x) + f64::from(size.width);
let in_y = position.y >= f64::from(pos.y)
&& position.y < f64::from(pos.y) + f64::from(size.height);
in_x && in_y
})
.as_ref()
.map(winit_monitor_to_tauri);
let _ = reply.send(monitor);
}
RuntimeEvent::QueryCurrentMonitor { id, reply } => {
let raw = raw_window_id(id);
let monitor = self
.windows
.get(&raw)
.and_then(winit::window::Window::current_monitor)
.as_ref()
.map(winit_monitor_to_tauri);
let _ = reply.send(monitor);
}
RuntimeEvent::RunOnMainThread { thunk } => {
thunk();
}
RuntimeEvent::QueryTheme { id, reply } => {
let raw = raw_window_id(id);
let theme = self
.windows
.get(&raw)
.and_then(winit::window::Window::theme)
.map_or(Theme::Light, |t| match t {
winit::window::Theme::Light => Theme::Light,
winit::window::Theme::Dark => Theme::Dark,
});
let _ = reply.send(theme);
}
RuntimeEvent::Exit { code: _ } => {
event_loop.exit();
(self.callback)(RunEvent::Exit);
}
}
}
fn window_event(
&mut self,
event_loop: &ActiveEventLoop,
window_id: WinitWindowId,
event: WinitWindowEvent,
) {
let raw_opt = self.winit_to_tauri.get(&window_id).copied();
match event {
WinitWindowEvent::CloseRequested => {
let _ = raw_opt.map(|raw| {
let (signal_tx, signal_rx) = mpsc::channel::<bool>();
self.dispatch_window_event(raw, TauriWindowEvent::CloseRequested { signal_tx });
let prevent = signal_rx.try_recv().unwrap_or(false);
if !prevent {
let _ = self.windows.remove(&raw).map(|w| {
let winit_id = w.id();
let _ = self.winit_to_tauri.remove(&winit_id);
});
self.dispatch_window_event(raw, TauriWindowEvent::Destroyed);
let _ = self.labels.remove(&raw);
if self.windows.is_empty() {
event_loop.exit();
}
}
});
}
WinitWindowEvent::Destroyed => {
let _ = raw_opt.map(|raw| {
self.dispatch_window_event(raw, TauriWindowEvent::Destroyed);
let _ = self.windows.remove(&raw);
let _ = self.labels.remove(&raw);
});
}
WinitWindowEvent::Resized(size) => {
let _ = raw_opt.map(|raw| {
self.dispatch_window_event(
raw,
TauriWindowEvent::Resized(PhysicalSize::new(size.width, size.height)),
);
});
}
WinitWindowEvent::Moved(position) => {
let _ = raw_opt.map(|raw| {
self.dispatch_window_event(
raw,
TauriWindowEvent::Moved(PhysicalPosition::new(position.x, position.y)),
);
});
}
WinitWindowEvent::Focused(focused) => {
let _ = raw_opt.map(|raw| {
self.dispatch_window_event(raw, TauriWindowEvent::Focused(focused));
});
}
WinitWindowEvent::RedrawRequested => {
let _ = raw_opt.map(|raw| self.present(raw));
}
WinitWindowEvent::ScaleFactorChanged { scale_factor, .. } => {
let _ = raw_opt.map(|raw| {
let new_inner = self.windows.get(&raw).map_or_else(
|| PhysicalSize::new(0, 0),
|window| {
let size = window.inner_size();
PhysicalSize::new(size.width, size.height)
},
);
self.dispatch_window_event(
raw,
TauriWindowEvent::ScaleFactorChanged {
scale_factor,
new_inner_size: new_inner,
},
);
});
}
_other => (),
}
}
fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) {
(self.callback)(RunEvent::MainEventsCleared);
}
fn exiting(&mut self, _event_loop: &ActiveEventLoop) {
(self.callback)(RunEvent::Exit);
}
}