1use parking_lot::ReentrantMutex;
9use std::cell::UnsafeCell;
10use std::ffi::CString;
11use std::ops::Drop;
12use std::path::PathBuf;
13use std::ptr;
14
15use crate::clipboard::{ClipboardBackend, ClipboardContext};
16use crate::fonts::{Font, FontAtlas, SharedFontAtlas};
17use crate::io::Io;
18
19use crate::sys;
20
21#[derive(Debug)]
47pub struct Context {
48 raw: *mut sys::ImGuiContext,
49 shared_font_atlas: Option<SharedFontAtlas>,
50 ini_filename: Option<CString>,
51 log_filename: Option<CString>,
52 platform_name: Option<CString>,
53 renderer_name: Option<CString>,
54 clipboard_ctx: Box<UnsafeCell<ClipboardContext>>,
59 ui: crate::ui::Ui,
60}
61
62static CTX_MUTEX: ReentrantMutex<()> = parking_lot::const_reentrant_mutex(());
65
66fn clear_current_context() {
67 unsafe {
68 sys::igSetCurrentContext(ptr::null_mut());
69 }
70}
71
72fn no_current_context() -> bool {
73 let ctx = unsafe { sys::igGetCurrentContext() };
74 ctx.is_null()
75}
76
77impl Context {
78 pub fn try_create() -> crate::error::ImGuiResult<Context> {
82 Self::try_create_internal(None)
83 }
84
85 pub fn try_create_with_shared_font_atlas(
87 shared_font_atlas: SharedFontAtlas,
88 ) -> crate::error::ImGuiResult<Context> {
89 Self::try_create_internal(Some(shared_font_atlas))
90 }
91
92 pub fn create() -> Context {
96 Self::try_create().expect("Failed to create Dear ImGui context")
97 }
98
99 pub fn create_with_shared_font_atlas(shared_font_atlas: SharedFontAtlas) -> Context {
101 Self::try_create_with_shared_font_atlas(shared_font_atlas)
102 .expect("Failed to create Dear ImGui context")
103 }
104
105 fn try_create_internal(
108 mut shared_font_atlas: Option<SharedFontAtlas>,
109 ) -> crate::error::ImGuiResult<Context> {
110 let _guard = CTX_MUTEX.lock();
111
112 if !no_current_context() {
113 return Err(crate::error::ImGuiError::ContextAlreadyActive);
114 }
115
116 let shared_font_atlas_ptr = match &mut shared_font_atlas {
117 Some(atlas) => atlas.as_ptr_mut(),
118 None => ptr::null_mut(),
119 };
120
121 let raw = unsafe { sys::igCreateContext(shared_font_atlas_ptr) };
123 if raw.is_null() {
124 return Err(crate::error::ImGuiError::ContextCreation {
125 reason: "ImGui_CreateContext returned null".to_string(),
126 });
127 }
128
129 unsafe {
131 sys::igSetCurrentContext(raw);
132 }
133
134 Ok(Context {
135 raw,
136 shared_font_atlas,
137 ini_filename: None,
138 log_filename: None,
139 platform_name: None,
140 renderer_name: None,
141 clipboard_ctx: Box::new(UnsafeCell::new(ClipboardContext::dummy())),
142 ui: crate::ui::Ui::new(),
143 })
144 }
145
146 pub fn io_mut(&mut self) -> &mut Io {
148 let _guard = CTX_MUTEX.lock();
149 unsafe {
150 let io_ptr = sys::igGetIO_Nil();
152 &mut *(io_ptr as *mut Io)
153 }
154 }
155
156 pub fn io(&self) -> &crate::io::Io {
158 let _guard = CTX_MUTEX.lock();
159 unsafe {
160 let io_ptr = sys::igGetIO_Nil();
162 &*(io_ptr as *const crate::io::Io)
163 }
164 }
165
166 pub fn style(&self) -> &crate::style::Style {
168 let _guard = CTX_MUTEX.lock();
169 unsafe {
170 let style_ptr = sys::igGetStyle();
171 &*(style_ptr as *const crate::style::Style)
172 }
173 }
174
175 pub fn style_mut(&mut self) -> &mut crate::style::Style {
177 let _guard = CTX_MUTEX.lock();
178 unsafe {
179 let style_ptr = sys::igGetStyle();
180 &mut *(style_ptr as *mut crate::style::Style)
181 }
182 }
183
184 pub fn frame(&mut self) -> &mut crate::ui::Ui {
186 let _guard = CTX_MUTEX.lock();
187
188 unsafe {
189 sys::igNewFrame();
190 }
191 &mut self.ui
192 }
193
194 pub fn frame_with<F, R>(&mut self, f: F) -> R
196 where
197 F: FnOnce(&crate::ui::Ui) -> R,
198 {
199 let ui = self.frame();
200 f(ui)
201 }
202
203 pub fn render(&mut self) -> &crate::render::DrawData {
208 let _guard = CTX_MUTEX.lock();
209 unsafe {
210 sys::igRender();
211 &*(sys::igGetDrawData() as *const crate::render::DrawData)
212 }
213 }
214
215 pub fn draw_data(&self) -> Option<&crate::render::DrawData> {
220 let _guard = CTX_MUTEX.lock();
221 unsafe {
222 let draw_data = sys::igGetDrawData();
223 if draw_data.is_null() {
224 None
225 } else {
226 let data = &*(draw_data as *const crate::render::DrawData);
227 if data.valid() { Some(data) } else { None }
228 }
229 }
230 }
231
232 pub fn set_ini_filename<P: Into<PathBuf>>(
238 &mut self,
239 filename: Option<P>,
240 ) -> crate::error::ImGuiResult<()> {
241 use crate::error::SafeStringConversion;
242 let _guard = CTX_MUTEX.lock();
243
244 self.ini_filename = match filename {
245 Some(f) => Some(f.into().to_string_lossy().to_cstring_safe()?),
246 None => None,
247 };
248
249 unsafe {
250 let io = sys::igGetIO_Nil();
251 let ptr = self
252 .ini_filename
253 .as_ref()
254 .map(|s| s.as_ptr())
255 .unwrap_or(ptr::null());
256 (*io).IniFilename = ptr;
257 }
258 Ok(())
259 }
260
261 pub fn set_log_filename<P: Into<PathBuf>>(
269 &mut self,
270 filename: Option<P>,
271 ) -> crate::error::ImGuiResult<()> {
272 use crate::error::SafeStringConversion;
273 let _guard = CTX_MUTEX.lock();
274
275 self.log_filename = match filename {
276 Some(f) => Some(f.into().to_string_lossy().to_cstring_safe()?),
277 None => None,
278 };
279
280 unsafe {
281 let io = sys::igGetIO_Nil();
282 let ptr = self
283 .log_filename
284 .as_ref()
285 .map(|s| s.as_ptr())
286 .unwrap_or(ptr::null());
287 (*io).LogFilename = ptr;
288 }
289 Ok(())
290 }
291
292 pub fn set_platform_name<S: Into<String>>(
300 &mut self,
301 name: Option<S>,
302 ) -> crate::error::ImGuiResult<()> {
303 use crate::error::SafeStringConversion;
304 let _guard = CTX_MUTEX.lock();
305
306 self.platform_name = match name {
307 Some(n) => Some(n.into().to_cstring_safe()?),
308 None => None,
309 };
310
311 unsafe {
312 let io = sys::igGetIO_Nil();
313 let ptr = self
314 .platform_name
315 .as_ref()
316 .map(|s| s.as_ptr())
317 .unwrap_or(ptr::null());
318 (*io).BackendPlatformName = ptr;
319 }
320 Ok(())
321 }
322
323 pub fn set_renderer_name<S: Into<String>>(
331 &mut self,
332 name: Option<S>,
333 ) -> crate::error::ImGuiResult<()> {
334 use crate::error::SafeStringConversion;
335 let _guard = CTX_MUTEX.lock();
336
337 self.renderer_name = match name {
338 Some(n) => Some(n.into().to_cstring_safe()?),
339 None => None,
340 };
341
342 unsafe {
343 let io = sys::igGetIO_Nil();
344 let ptr = self
345 .renderer_name
346 .as_ref()
347 .map(|s| s.as_ptr())
348 .unwrap_or(ptr::null());
349 (*io).BackendRendererName = ptr;
350 }
351 Ok(())
352 }
353
354 #[cfg(feature = "multi-viewport")]
358 pub fn platform_io_mut(&mut self) -> &mut crate::platform_io::PlatformIo {
359 let _guard = CTX_MUTEX.lock();
360 unsafe {
361 let pio = sys::igGetPlatformIO_Nil();
362 crate::platform_io::PlatformIo::from_raw_mut(pio)
363 }
364 }
365
366 #[cfg(feature = "multi-viewport")]
368 pub fn enable_multi_viewport(&mut self) {
369 crate::viewport_backend::utils::enable_viewport_flags(self.io_mut());
371 }
372
373 #[cfg(feature = "multi-viewport")]
378 pub fn update_platform_windows(&mut self) {
379 let _guard = CTX_MUTEX.lock();
380 unsafe {
381 let main_viewport = sys::igGetMainViewport();
383 if !main_viewport.is_null() && (*main_viewport).PlatformHandle.is_null() {
384 eprintln!("update_platform_windows: main viewport not set up, setting it up now");
385 }
388
389 sys::igUpdatePlatformWindows();
390 }
391 }
392
393 #[cfg(feature = "multi-viewport")]
398 pub fn render_platform_windows_default(&mut self) {
399 let _guard = CTX_MUTEX.lock();
400 unsafe {
401 sys::igRenderPlatformWindowsDefault(std::ptr::null_mut(), std::ptr::null_mut());
402 }
403 }
404
405 #[cfg(feature = "multi-viewport")]
410 pub fn destroy_platform_windows(&mut self) {
411 let _guard = CTX_MUTEX.lock();
412 unsafe {
413 sys::igDestroyPlatformWindows();
414 }
415 }
416
417 pub fn suspend(self) -> SuspendedContext {
419 let _guard = CTX_MUTEX.lock();
420 assert!(
421 self.is_current_context(),
422 "context to be suspended is not the active context"
423 );
424 clear_current_context();
425 SuspendedContext(self)
426 }
427
428 fn is_current_context(&self) -> bool {
429 let ctx = unsafe { sys::igGetCurrentContext() };
430 self.raw == ctx
431 }
432
433 pub fn push_font(&mut self, font: &Font) {
435 let _guard = CTX_MUTEX.lock();
436 unsafe {
437 sys::igPushFont(font.raw(), 0.0);
438 }
439 }
440
441 #[doc(alias = "PopFont")]
445 pub fn pop_font(&mut self) {
446 let _guard = CTX_MUTEX.lock();
447 unsafe {
448 sys::igPopFont();
449 }
450 }
451
452 #[doc(alias = "GetFont")]
454 pub fn current_font(&self) -> &Font {
455 let _guard = CTX_MUTEX.lock();
456 unsafe { Font::from_raw(sys::igGetFont() as *const _) }
457 }
458
459 #[doc(alias = "GetFontSize")]
461 pub fn current_font_size(&self) -> f32 {
462 let _guard = CTX_MUTEX.lock();
463 unsafe { sys::igGetFontSize() }
464 }
465
466 pub fn font_atlas(&self) -> FontAtlas {
468 let _guard = CTX_MUTEX.lock();
469 unsafe {
470 let io = sys::igGetIO_Nil();
471 let atlas_ptr = (*io).Fonts;
472 FontAtlas::from_raw(atlas_ptr)
473 }
474 }
475
476 pub fn font_atlas_mut(&mut self) -> FontAtlas {
478 let _guard = CTX_MUTEX.lock();
479
480 #[cfg(target_arch = "wasm32")]
482 {
483 return unsafe { FontAtlas::from_raw(ptr::null_mut()) };
484 }
485
486 #[cfg(not(target_arch = "wasm32"))]
487 unsafe {
488 let io = sys::igGetIO_Nil();
489 let atlas_ptr = (*io).Fonts;
490 FontAtlas::from_raw(atlas_ptr)
491 }
492 }
493
494 pub fn fonts(&mut self) -> FontAtlas {
498 self.font_atlas_mut()
499 }
500
501 pub fn clone_shared_font_atlas(&mut self) -> Option<SharedFontAtlas> {
503 self.shared_font_atlas.clone()
504 }
505
506 #[doc(alias = "LoadIniSettingsFromMemory")]
508 pub fn load_ini_settings(&mut self, data: &str) {
509 let _guard = CTX_MUTEX.lock();
510 unsafe {
511 sys::igLoadIniSettingsFromMemory(data.as_ptr() as *const _, data.len());
512 }
513 }
514
515 #[doc(alias = "SaveIniSettingsToMemory")]
517 pub fn save_ini_settings(&mut self, buf: &mut String) {
518 let _guard = CTX_MUTEX.lock();
519 unsafe {
520 let data_ptr = sys::igSaveIniSettingsToMemory(ptr::null_mut());
521 if !data_ptr.is_null() {
522 let data = std::ffi::CStr::from_ptr(data_ptr);
523 buf.push_str(&data.to_string_lossy());
524 }
525 }
526 }
527
528 pub fn set_clipboard_backend<T: ClipboardBackend>(&mut self, backend: T) {
530 let clipboard_ctx: Box<UnsafeCell<_>> =
531 Box::new(UnsafeCell::new(ClipboardContext::new(backend)));
532
533 unsafe {
535 let platform_io = sys::igGetPlatformIO_Nil();
536 (*platform_io).Platform_SetClipboardTextFn = Some(crate::clipboard::set_clipboard_text);
537 (*platform_io).Platform_GetClipboardTextFn = Some(crate::clipboard::get_clipboard_text);
538 (*platform_io).Platform_ClipboardUserData = clipboard_ctx.get() as *mut _;
539 }
540
541 self.clipboard_ctx = clipboard_ctx;
542 }
543}
544
545impl Drop for Context {
546 fn drop(&mut self) {
547 let _guard = CTX_MUTEX.lock();
548 unsafe {
549 if !self.raw.is_null() {
550 if sys::igGetCurrentContext() == self.raw {
551 clear_current_context();
552 }
553 sys::igDestroyContext(self.raw);
554 }
555 }
556 }
557}
558
559#[derive(Debug)]
563pub struct SuspendedContext(Context);
564
565impl SuspendedContext {
566 pub fn try_create() -> crate::error::ImGuiResult<Self> {
568 Self::try_create_internal(None)
569 }
570
571 pub fn try_create_with_shared_font_atlas(
573 shared_font_atlas: SharedFontAtlas,
574 ) -> crate::error::ImGuiResult<Self> {
575 Self::try_create_internal(Some(shared_font_atlas))
576 }
577
578 pub fn create() -> Self {
580 Self::try_create().expect("Failed to create Dear ImGui context")
581 }
582
583 pub fn create_with_shared_font_atlas(shared_font_atlas: SharedFontAtlas) -> Self {
585 Self::try_create_with_shared_font_atlas(shared_font_atlas)
586 .expect("Failed to create Dear ImGui context")
587 }
588
589 fn try_create_internal(
592 mut shared_font_atlas: Option<SharedFontAtlas>,
593 ) -> crate::error::ImGuiResult<Self> {
594 let _guard = CTX_MUTEX.lock();
595
596 let shared_font_atlas_ptr = match &mut shared_font_atlas {
597 Some(atlas) => atlas.as_ptr_mut(),
598 None => ptr::null_mut(),
599 };
600
601 let raw = unsafe { sys::igCreateContext(shared_font_atlas_ptr) };
602 if raw.is_null() {
603 return Err(crate::error::ImGuiError::ContextCreation {
604 reason: "ImGui_CreateContext returned null".to_string(),
605 });
606 }
607
608 let ctx = Context {
609 raw,
610 shared_font_atlas,
611 ini_filename: None,
612 log_filename: None,
613 platform_name: None,
614 renderer_name: None,
615 clipboard_ctx: Box::new(UnsafeCell::new(ClipboardContext::dummy())),
616 ui: crate::ui::Ui::new(),
617 };
618
619 if ctx.is_current_context() {
621 clear_current_context();
622 }
623
624 Ok(SuspendedContext(ctx))
625 }
626
627 pub fn activate(self) -> Result<Context, SuspendedContext> {
632 let _guard = CTX_MUTEX.lock();
633 if no_current_context() {
634 unsafe {
635 sys::igSetCurrentContext(self.0.raw);
636 }
637 Ok(self.0)
638 } else {
639 Err(self)
640 }
641 }
642}
643
644