use super::*;
struct RuntimeConstruction<'a> {
context: &'a mut Context,
platform: Rc<WinitPlatformControl>,
control: Rc<RuntimeControl>,
#[cfg(test)]
owned_platform: Option<crate::WinitPlatform>,
prepared_monitors: Option<PreparedMonitors>,
runtime_installed: bool,
runtime_registered: bool,
main_viewport_initialized: bool,
callbacks_claimed: bool,
committed: bool,
}
impl<'a> RuntimeConstruction<'a> {
fn new(
context: &'a mut Context,
platform: Rc<WinitPlatformControl>,
control: Rc<RuntimeControl>,
#[cfg(test)] owned_platform: Option<crate::WinitPlatform>,
prepared_monitors: PreparedMonitors,
) -> Self {
Self {
context,
platform,
control,
#[cfg(test)]
owned_platform,
prepared_monitors: Some(prepared_monitors),
runtime_installed: false,
runtime_registered: false,
main_viewport_initialized: false,
callbacks_claimed: false,
committed: false,
}
}
fn commit(mut self) -> Result<WinitPlatformRuntime, WinitPlatformError> {
self.control.state.set(RuntimeState::Attached);
self.committed = true;
Ok(WinitPlatformRuntime {
control: Rc::clone(&self.control),
platform: Rc::clone(&self.platform),
#[cfg(test)]
owned_platform: self.owned_platform.take(),
})
}
}
impl Drop for RuntimeConstruction<'_> {
fn drop(&mut self) {
if self.committed {
return;
}
self.control.binding.with_bound_context(|| {
let _ = self.control.restore_monitors_in_current_context();
if self.callbacks_claimed {
let _ = release_platform_callbacks(&self.control);
}
if self.main_viewport_initialized {
self.control.drop_all_viewports();
}
});
if self.runtime_registered {
unregister_runtime(self.control.binding.id());
}
if self.runtime_installed {
self.platform.clear_runtime(&self.control);
}
self.control.main_window.borrow_mut().take();
self.control.state.set(RuntimeState::Detached);
}
}
pub(crate) struct WinitPlatformRuntime {
control: Rc<RuntimeControl>,
platform: Rc<WinitPlatformControl>,
#[cfg(test)]
owned_platform: Option<crate::WinitPlatform>,
}
impl WinitPlatformRuntime {
pub(crate) fn new(
context: &mut Context,
platform: &crate::WinitPlatform,
) -> Result<Self, WinitPlatformError> {
if !dear_imgui_rs::sys::HAS_PLATFORM_IO_AGGREGATE_HOOKS {
return Err(WinitPlatformError::AggregateCallbackHooksUnavailable);
}
validate_multi_viewport_hidpi_mode(platform.hidpi_mode())?;
let platform_control = platform.control();
platform_control.ensure_context(context)?;
let main_window = platform_control.attached_window()?;
platform_control.validate_operational_contract()?;
preflight_window_system(&main_window)?;
let prepared_monitors = prepare_monitors(context, &main_window)?;
preflight_platform_callbacks(context)?;
preflight_main_viewport(context)?;
let control = Rc::new(RuntimeControl::new(
context,
&platform_control,
Arc::clone(&main_window),
));
let runtime = Self::construct(
context,
platform_control,
control,
#[cfg(test)]
None,
Some(Arc::clone(&main_window)),
prepared_monitors,
|_, _| Ok(()),
)?;
let io = context.io_mut();
io.set_display_size(super::super::desktop_size_for_window(&main_window));
io.set_display_framebuffer_scale(super::super::framebuffer_scale_for_window(&main_window));
invalidate_mouse_coordinate_cache(io);
Ok(runtime)
}
fn from_platform(platform: &crate::WinitPlatform) -> Result<Self, WinitPlatformError> {
let platform_control = platform.control();
let control = platform_control.runtime_control()?;
Ok(Self {
control,
platform: platform_control,
#[cfg(test)]
owned_platform: None,
})
}
#[cfg(test)]
pub(in super::super) fn new_for_test(
context: &mut Context,
) -> Result<Self, WinitPlatformError> {
Self::new_for_test_with(context, vec![test_monitor()], |_, _| Ok(()))
}
#[cfg(test)]
pub(in super::super) fn new_for_test_with_platform(
context: &mut Context,
platform: &crate::WinitPlatform,
) -> Result<Self, WinitPlatformError> {
let platform_control = platform.control();
platform_control.ensure_context(context)?;
platform_control.validate_operational_contract()?;
let prepared_monitors =
super::super::callbacks::prepare_monitors_for_test(context, vec![test_monitor()])?;
preflight_platform_callbacks(context)?;
preflight_main_viewport(context)?;
let control = Rc::new(RuntimeControl::new_for_test(context, &platform_control));
Self::construct(
context,
platform_control,
control,
None,
None,
prepared_monitors,
|_, _| Ok(()),
)
}
#[cfg(test)]
pub(in super::super) fn new_for_test_with(
context: &mut Context,
monitors: Vec<dear_imgui_rs::sys::ImGuiPlatformMonitor>,
checkpoint: impl FnMut(ConstructionStage, &mut Context) -> Result<(), WinitPlatformError>,
) -> Result<Self, WinitPlatformError> {
let platform = crate::WinitPlatform::new(context)?;
let platform_control = platform.control();
let prepared_monitors =
super::super::callbacks::prepare_monitors_for_test(context, monitors)?;
preflight_platform_callbacks(context)?;
preflight_main_viewport(context)?;
let control = Rc::new(RuntimeControl::new_for_test(context, &platform_control));
Self::construct(
context,
platform_control,
control,
Some(platform),
None,
prepared_monitors,
checkpoint,
)
}
fn construct(
context: &mut Context,
platform: Rc<WinitPlatformControl>,
control: Rc<RuntimeControl>,
#[cfg(test)] owned_platform: Option<crate::WinitPlatform>,
main_window: Option<Arc<Window>>,
prepared_monitors: PreparedMonitors,
mut checkpoint: impl FnMut(ConstructionStage, &mut Context) -> Result<(), WinitPlatformError>,
) -> Result<Self, WinitPlatformError> {
let mut transaction = RuntimeConstruction::new(
context,
platform,
control,
#[cfg(test)]
owned_platform,
prepared_monitors,
);
transaction
.platform
.install_runtime(Rc::clone(&transaction.control))?;
transaction.runtime_installed = true;
checkpoint(ConstructionStage::Attachment, transaction.context)?;
register_runtime(&transaction.control);
transaction.runtime_registered = true;
checkpoint(ConstructionStage::Registry, transaction.context)?;
if let Some(main_window) = main_window {
init_main_viewport(&transaction.control, main_window)?;
transaction.main_viewport_initialized = true;
}
checkpoint(ConstructionStage::MainViewport, transaction.context)?;
let callback_contract = claim_platform_callbacks(transaction.context);
transaction
.control
.install_platform_callback_contract(callback_contract);
transaction.callbacks_claimed = true;
checkpoint(ConstructionStage::Callbacks, transaction.context)?;
let prepared_monitors = transaction
.prepared_monitors
.take()
.expect("monitor storage is present until publication");
let ownership = publish_monitors(transaction.context, prepared_monitors);
transaction.control.install_monitor_ownership(ownership);
checkpoint(ConstructionStage::Monitors, transaction.context)?;
claim_backend_flags(&transaction.control, transaction.context);
checkpoint(ConstructionStage::BackendFlags, transaction.context)?;
transaction.commit()
}
pub fn validate_renderer_owner(&self, context: &Context) -> Result<(), WinitPlatformError> {
self.ensure_context(context)?;
self.poll_fault()?;
self.ensure_attached()
}
#[cfg(test)]
pub(in super::super) fn owned_platform_for_test_mut(&mut self) -> &mut crate::WinitPlatform {
self.owned_platform
.as_mut()
.expect("test runtimes always retain their synthetic platform owner")
}
pub fn with_event_loop<R>(
&self,
event_loop: &ActiveEventLoop,
callback: impl for<'scope> FnOnce(EventLoopScope<'scope>) -> R,
) -> WinitViewportAttempt<R> {
let faults = self.control.drain_faults();
if !faults.is_empty() {
return WinitViewportAttempt::skipped(faults);
}
if let Err(error) = self.ensure_attached() {
return WinitViewportAttempt::skipped(vec![error]);
}
let output = self.control.enter_event_loop(event_loop, callback);
WinitViewportAttempt::completed(output, self.control.drain_faults())
}
pub fn poll_fault(&self) -> Result<(), WinitPlatformError> {
self.control.poll_fault()
}
#[cfg(test)]
pub(in super::super) fn route_secondary_event<T>(
&self,
context: &mut Context,
event: &Event<T>,
) -> Result<bool, WinitPlatformError> {
self.ensure_context(context)?;
self.poll_fault()?;
self.ensure_attached()?;
let consumed = super::super::events::route_secondary_event(&self.control, context, event)?;
self.poll_fault()?;
Ok(consumed)
}
pub fn shutdown(&mut self, context: &mut Context) -> Result<(), WinitPlatformError> {
self.ensure_context(context)?;
if matches!(
self.control.state(),
RuntimeState::Detached | RuntimeState::ContextDestroyed
) {
return Ok(());
}
let attachment = self.platform.attachment_handle()?;
let (result, released) = {
let mut release = context.prepare_platform_attachment_release(&attachment)?;
let context = release.context_mut();
let result = self.control.shutdown_from_platform(context);
let released = matches!(
self.control.state(),
RuntimeState::Detached | RuntimeState::ContextDestroyed
);
(result, released)
};
if released {
self.platform.clear_runtime(&self.control);
}
#[cfg(test)]
let result = if released {
if let Some(platform) = self.owned_platform.as_mut() {
let platform_result = platform.shutdown(context);
match (result, platform_result) {
(Err(primary), Err(secondary)) => {
self.control.record_fault(secondary);
Err(primary)
}
(Ok(()), platform_result) => platform_result,
(result, Ok(())) => result,
}
} else {
result
}
} else {
result
};
result
}
fn ensure_context(&self, context: &Context) -> Result<(), WinitPlatformError> {
if context.id() == self.control.binding.id() {
Ok(())
} else {
Err(WinitPlatformError::ContextMismatch)
}
}
fn ensure_attached(&self) -> Result<(), WinitPlatformError> {
if self.control.state() != RuntimeState::Attached {
return self
.control
.poll_fault()
.and(Err(WinitPlatformError::RuntimeDetached));
}
self.control
.platform_control()?
.validate_operational_contract()
}
#[cfg(test)]
pub(in super::super) fn control(&self) -> &Rc<RuntimeControl> {
&self.control
}
}
impl WinitViewportRendererAdapter {
#[must_use]
pub fn context_id(&self) -> dear_imgui_rs::ContextId {
self.control.binding().id()
}
pub fn with_event_loop<R>(
&self,
event_loop: &ActiveEventLoop,
callback: impl for<'scope> FnOnce(EventLoopScope<'scope>) -> R,
) -> WinitViewportAttempt<R> {
let faults = self.control.drain_faults();
if !faults.is_empty() {
return WinitViewportAttempt::skipped(faults);
}
let runtime = WinitPlatformRuntime {
control: Rc::clone(&self.control),
platform: Rc::clone(&self.platform),
#[cfg(test)]
owned_platform: None,
};
if let Err(error) = runtime.ensure_attached() {
return WinitViewportAttempt::skipped(vec![error]);
}
let output = self.control.enter_event_loop(event_loop, callback);
WinitViewportAttempt::completed(output, self.control.drain_faults())
}
}
impl crate::WinitPlatform {
pub fn enable_viewports(&mut self, context: &mut Context) -> Result<(), WinitPlatformError> {
WinitPlatformRuntime::new(context, self).map(drop)
}
#[must_use]
pub fn viewports_enabled(&self) -> bool {
self.control().has_live_runtime()
}
#[doc(hidden)]
pub fn viewport_renderer_adapter(
&self,
context: &Context,
) -> Result<WinitViewportRendererAdapter, WinitPlatformError> {
let runtime = WinitPlatformRuntime::from_platform(self)?;
runtime.validate_renderer_owner(context)?;
Ok(WinitViewportRendererAdapter {
control: Rc::clone(&runtime.control),
platform: Rc::clone(&runtime.platform),
})
}
pub fn with_event_loop<R>(
&self,
event_loop: &ActiveEventLoop,
callback: impl for<'scope> FnOnce(EventLoopScope<'scope>) -> R,
) -> WinitViewportAttempt<R> {
match WinitPlatformRuntime::from_platform(self) {
Ok(runtime) => runtime.with_event_loop(event_loop, callback),
Err(error) => WinitViewportAttempt::skipped(vec![error]),
}
}
pub fn drain_viewport_faults(&self) -> Result<Vec<WinitPlatformError>, WinitPlatformError> {
Ok(WinitPlatformRuntime::from_platform(self)?
.control
.drain_faults())
}
pub fn monitor_publication_report(
&self,
) -> Result<crate::multi_viewport::WinitMonitorPublicationReport, WinitPlatformError> {
WinitPlatformRuntime::from_platform(self)?
.control
.monitor_publication_report()
}
pub fn disable_viewports(&mut self, context: &mut Context) -> Result<(), WinitPlatformError> {
match WinitPlatformRuntime::from_platform(self) {
Ok(mut runtime) => runtime.shutdown(context),
Err(WinitPlatformError::RuntimeDetached) => Ok(()),
Err(error) => Err(error),
}
}
}
fn validate_window_system(
is_supported_desktop: bool,
is_wayland: bool,
) -> Result<(), WinitPlatformError> {
if !is_supported_desktop {
return Err(WinitPlatformError::UnsupportedWindowSystem {
target: std::env::consts::OS,
});
}
if is_wayland {
Err(WinitPlatformError::WaylandUnsupported)
} else {
Ok(())
}
}
pub(in super::super) fn validate_multi_viewport_hidpi_mode(
mode: crate::HiDpiMode,
) -> Result<(), WinitPlatformError> {
if mode == crate::HiDpiMode::Default {
Ok(())
} else {
Err(WinitPlatformError::CustomHiDpiModeUnsupported)
}
}
#[cfg(target_os = "linux")]
fn preflight_window_system(window: &Window) -> Result<(), WinitPlatformError> {
let handle = window
.window_handle()
.map_err(|error| WinitPlatformError::WindowOperation {
operation: "query raw window handle",
message: error.to_string(),
})?
.as_raw();
validate_window_system(true, matches!(handle, RawWindowHandle::Wayland(_)))
}
#[cfg(any(target_os = "windows", target_os = "macos"))]
fn preflight_window_system(_window: &Window) -> Result<(), WinitPlatformError> {
validate_window_system(true, false)
}
#[cfg(not(any(target_os = "linux", target_os = "windows", target_os = "macos")))]
fn preflight_window_system(_window: &Window) -> Result<(), WinitPlatformError> {
validate_window_system(false, false)
}
#[cfg(test)]
pub(in super::super) fn validate_window_system_for_test(
is_supported_desktop: bool,
is_wayland: bool,
) -> Result<(), WinitPlatformError> {
validate_window_system(is_supported_desktop, is_wayland)
}
fn claim_backend_flags(control: &RuntimeControl, context: &mut Context) {
control.binding().with_bound_context(|| {
let io = context.io_mut();
io.set_backend_flags(io.backend_flags() | crate::platform::WINIT_VIEWPORT_FLAGS);
});
}
#[cfg(test)]
fn test_monitor() -> dear_imgui_rs::sys::ImGuiPlatformMonitor {
dear_imgui_rs::sys::ImGuiPlatformMonitor {
MainPos: dear_imgui_rs::sys::ImVec2 { x: 0.0, y: 0.0 },
MainSize: dear_imgui_rs::sys::ImVec2 {
x: 1920.0,
y: 1080.0,
},
WorkPos: dear_imgui_rs::sys::ImVec2 { x: 0.0, y: 0.0 },
WorkSize: dear_imgui_rs::sys::ImVec2 {
x: 1920.0,
y: 1040.0,
},
DpiScale: 1.0,
PlatformHandle: std::ptr::null_mut(),
}
}