dear_imgui_rs/context/
platform.rs1use crate::sys;
2
3use super::Context;
4#[cfg(feature = "multi-viewport")]
5use super::attachment::{ContextPlatformWindowTeardown, ContextPlatformWindowTeardownError};
6use super::binding::{CTX_MUTEX, with_bound_context};
7
8impl Context {
9 #[doc(alias = "GetPlatformIO")]
14 pub fn platform_io(&self) -> &crate::platform_io::PlatformIo {
15 let _guard = CTX_MUTEX.lock();
16 unsafe {
17 let pio = self.platform_io_ptr("Context::platform_io()");
18 crate::platform_io::PlatformIo::from_raw(pio)
19 }
20 }
21
22 pub fn platform_io_mut(&mut self) -> &mut crate::platform_io::PlatformIo {
27 let _guard = CTX_MUTEX.lock();
28 unsafe {
29 let pio = self.platform_io_ptr("Context::platform_io_mut()");
30 crate::platform_io::PlatformIo::from_raw_mut(pio)
31 }
32 }
33
34 #[doc(alias = "GetMainViewport")]
39 pub fn main_viewport(&mut self) -> &mut crate::platform_io::Viewport {
40 let _guard = CTX_MUTEX.lock();
41 unsafe {
42 with_bound_context(self.raw, || {
43 let ptr = sys::igGetMainViewport();
44 if ptr.is_null() {
45 panic!("Context::main_viewport() requires a valid ImGui context");
46 }
47 crate::platform_io::Viewport::from_raw_mut(ptr)
48 })
49 }
50 }
51
52 #[cfg(feature = "multi-viewport")]
63 pub fn enable_multi_viewport(&mut self) {
64 let io = self.io_mut();
65 let mut flags = io.config_flags();
66 flags.insert(crate::ConfigFlags::VIEWPORTS_ENABLE);
67 io.set_config_flags(flags);
68 }
69
70 #[cfg(feature = "multi-viewport")]
80 #[doc(alias = "UpdatePlatformWindows")]
81 pub fn update_platform_windows(&mut self) {
82 let _guard = CTX_MUTEX.lock();
83 unsafe {
84 with_bound_context(self.raw, || {
85 self.assert_can_update_platform_windows_unlocked(
86 "Context::update_platform_windows()",
87 );
88 sys::igUpdatePlatformWindows();
89 });
90 }
91 }
92
93 #[cfg(feature = "multi-viewport")]
105 #[doc(alias = "RenderPlatformWindowsDefault")]
106 pub fn render_platform_windows_default(&mut self) {
107 let _guard = CTX_MUTEX.lock();
108 unsafe {
109 with_bound_context(self.raw, || {
110 let raw = &*self.raw;
111 assert!(
112 raw.FrameCount > 0
113 && raw.FrameCountRendered == raw.FrameCount
114 && raw.FrameCountPlatformEnded == raw.FrameCount,
115 "Context::render_platform_windows_default() requires a rendered frame followed by Context::update_platform_windows()"
116 );
117 let platform_io = sys::igGetPlatformIO_Nil();
118 assert!(
119 !platform_io.is_null()
120 && ((*platform_io).Platform_RenderWindow.is_some()
121 || (*platform_io).Renderer_RenderWindow.is_some()),
122 "Context::render_platform_windows_default() requires Platform_RenderWindow or Renderer_RenderWindow; render snapshots directly when the renderer does not use default callbacks"
123 );
124 sys::igRenderPlatformWindowsDefault(std::ptr::null_mut(), std::ptr::null_mut());
125 });
126 }
127 }
128
129 #[cfg(feature = "multi-viewport")]
152 #[doc(alias = "DestroyPlatformWindows")]
153 pub fn destroy_platform_windows(&mut self) -> Result<(), ContextPlatformWindowTeardownError> {
154 let _guard = CTX_MUTEX.lock();
155 self.end_frame_for_teardown_unlocked();
156 let teardown = ContextPlatformWindowTeardown::new(&self.state);
157 let invocation = self.attachments.begin_platform_window_teardown(&teardown)?;
158 unsafe {
159 with_bound_context(self.raw, || {
160 sys::igDestroyPlatformWindows();
161 });
162 }
163 invocation.finish(&teardown)
164 }
165
166 #[cfg(feature = "multi-viewport")]
167 pub(super) fn prepare_multi_viewport_new_frame_contract_unlocked(&self, caller: &str) {
168 unsafe {
169 let config_flags = (*self.io_ptr(caller)).ConfigFlags;
170 let viewports_enabled = config_flags & sys::ImGuiConfigFlags_ViewportsEnable != 0;
171 if !viewports_enabled {
172 return;
173 }
174
175 let frame_count = (*self.raw).FrameCount;
176 let frame_count_ended = (*self.raw).FrameCountEnded;
177 let frame_count_platform_ended = (*self.raw).FrameCountPlatformEnded;
178 let config_flags_current_frame = (*self.raw).ConfigFlagsCurrFrame;
179
180 if frame_count == 1
181 && config_flags_current_frame & sys::ImGuiConfigFlags_ViewportsEnable == 0
182 {
183 panic!(
184 "{caller} cannot enable multi-viewport on the second frame; enable it before the first frame or after the second frame so Dear ImGui preserves its settings contract"
185 );
186 }
187 if !self.multi_viewport_backends_advertised_unlocked() {
188 return;
191 }
192 if frame_count > 0 && frame_count_platform_ended != frame_count {
193 if config_flags_current_frame & sys::ImGuiConfigFlags_ViewportsEnable == 0 {
194 assert_eq!(
195 frame_count_ended, frame_count,
196 "{caller} cannot enable multi-viewport while the previous frame is still open"
197 );
198 sys::igUpdatePlatformWindows();
201 } else {
202 panic!(
203 "{caller} cannot begin a new multi-viewport frame before Context::update_platform_windows() completes the previous frame"
204 );
205 }
206 }
207 self.assert_multi_viewport_backend_contract_unlocked(
208 caller,
209 config_flags | config_flags_current_frame,
210 );
211 }
212 }
213
214 #[cfg(feature = "multi-viewport")]
215 fn assert_can_update_platform_windows_unlocked(&self, caller: &str) {
216 unsafe {
217 let frame_count = (*self.raw).FrameCount;
218 let frame_count_ended = (*self.raw).FrameCountEnded;
219 let frame_count_platform_ended = (*self.raw).FrameCountPlatformEnded;
220 let config_flags_current_frame = (*self.raw).ConfigFlagsCurrFrame;
221 assert!(
222 frame_count_ended == frame_count,
223 "{caller} requires Context::render() or an ended frame first"
224 );
225 assert!(
226 frame_count_platform_ended < frame_count,
227 "{caller} was already called for frame {}",
228 frame_count
229 );
230 self.assert_multi_viewport_backend_contract_unlocked(
231 caller,
232 config_flags_current_frame,
233 );
234 }
235 }
236
237 #[cfg(feature = "multi-viewport")]
238 fn assert_multi_viewport_backend_contract_unlocked(&self, caller: &str, config_flags: i32) {
239 if config_flags & sys::ImGuiConfigFlags_ViewportsEnable == 0 {
240 return;
241 }
242
243 unsafe {
244 let io = &*self.io_ptr(caller);
245 assert!(
246 self.multi_viewport_backends_advertised_unlocked(),
247 "{caller} requires platform and renderer backends that advertise multi-viewport support"
248 );
249
250 let platform_io = &*self.platform_io_ptr(caller);
251 for (name, installed) in [
252 (
253 "Platform_CreateWindow",
254 platform_io.Platform_CreateWindow.is_some(),
255 ),
256 (
257 "Platform_DestroyWindow",
258 platform_io.Platform_DestroyWindow.is_some(),
259 ),
260 (
261 "Platform_ShowWindow",
262 platform_io.Platform_ShowWindow.is_some(),
263 ),
264 (
265 "Platform_GetWindowPos",
266 platform_io.Platform_GetWindowPos.is_some(),
267 ),
268 (
269 "Platform_SetWindowPos",
270 platform_io.Platform_SetWindowPos.is_some(),
271 ),
272 (
273 "Platform_GetWindowSize",
274 platform_io.Platform_GetWindowSize.is_some(),
275 ),
276 (
277 "Platform_SetWindowSize",
278 platform_io.Platform_SetWindowSize.is_some(),
279 ),
280 (
281 "Platform_SetWindowTitle",
282 platform_io.Platform_SetWindowTitle.is_some(),
283 ),
284 ] {
285 assert!(
286 installed,
287 "{caller} requires the {name} callback before multi-viewport can run"
288 );
289 }
290
291 let monitor_count = platform_io.Monitors.Size;
292 assert!(
293 monitor_count > 0 && !platform_io.Monitors.Data.is_null(),
294 "{caller} requires at least one valid PlatformIO monitor"
295 );
296 assert!(
297 platform_io.Monitors.Capacity >= monitor_count,
298 "{caller} rejected a corrupt PlatformIO monitor vector"
299 );
300 let monitors = std::slice::from_raw_parts(
301 platform_io.Monitors.Data,
302 usize::try_from(monitor_count)
303 .expect("positive PlatformIO monitor count must fit usize"),
304 );
305 crate::platform_io::assert_monitor_contract(monitors, caller);
306
307 let main_viewport = sys::igGetMainViewport();
308 assert!(
309 !main_viewport.is_null(),
310 "{caller} requires a valid main viewport"
311 );
312 assert!(
313 !(*main_viewport).PlatformUserData.is_null()
314 || !(*main_viewport).PlatformHandle.is_null(),
315 "{caller} requires the platform backend to initialize the main viewport"
316 );
317
318 let transparent_docking = io.ConfigDockingTransparentPayload
319 && config_flags & sys::ImGuiConfigFlags_DockingEnable != 0;
320 assert!(
321 !transparent_docking || platform_io.Platform_SetWindowAlpha.is_some(),
322 "{caller} requires Platform_SetWindowAlpha when transparent docking payloads are enabled"
323 );
324 }
325 }
326
327 #[cfg(feature = "multi-viewport")]
328 fn multi_viewport_backends_advertised_unlocked(&self) -> bool {
329 unsafe {
330 let backend_flags = (*self.io_ptr("multi-viewport backend validation")).BackendFlags;
331 backend_flags & sys::ImGuiBackendFlags_PlatformHasViewports != 0
332 && backend_flags & sys::ImGuiBackendFlags_RendererHasViewports != 0
333 }
334 }
335}