1use parking_lot::ReentrantMutex;
2use std::cell::UnsafeCell;
3use std::ffi::CString;
4use std::ops::Drop;
5use std::path::PathBuf;
6use std::ptr;
7
8use crate::clipboard::{ClipboardBackend, ClipboardContext};
9use crate::fonts::{Font, FontAtlas, SharedFontAtlas};
10use crate::io::Io;
11
12use crate::sys;
13
14#[derive(Debug)]
40pub struct Context {
41 raw: *mut sys::ImGuiContext,
42 shared_font_atlas: Option<SharedFontAtlas>,
43 ini_filename: Option<CString>,
44 log_filename: Option<CString>,
45 platform_name: Option<CString>,
46 renderer_name: Option<CString>,
47 clipboard_ctx: Box<UnsafeCell<ClipboardContext>>,
52 ui: crate::ui::Ui,
53}
54
55static CTX_MUTEX: ReentrantMutex<()> = parking_lot::const_reentrant_mutex(());
58
59fn clear_current_context() {
60 unsafe {
61 sys::igSetCurrentContext(ptr::null_mut());
62 }
63}
64
65fn no_current_context() -> bool {
66 let ctx = unsafe { sys::igGetCurrentContext() };
67 ctx.is_null()
68}
69
70impl Context {
71 pub fn try_create() -> crate::error::ImGuiResult<Context> {
75 Self::try_create_internal(None)
76 }
77
78 pub fn try_create_with_shared_font_atlas(
80 shared_font_atlas: SharedFontAtlas,
81 ) -> crate::error::ImGuiResult<Context> {
82 Self::try_create_internal(Some(shared_font_atlas))
83 }
84
85 pub fn create() -> Context {
89 Self::try_create().expect("Failed to create Dear ImGui context")
90 }
91
92 pub fn create_with_shared_font_atlas(shared_font_atlas: SharedFontAtlas) -> Context {
94 Self::try_create_with_shared_font_atlas(shared_font_atlas)
95 .expect("Failed to create Dear ImGui context")
96 }
97
98 fn try_create_internal(
101 mut shared_font_atlas: Option<SharedFontAtlas>,
102 ) -> crate::error::ImGuiResult<Context> {
103 let _guard = CTX_MUTEX.lock();
104
105 if !no_current_context() {
106 return Err(crate::error::ImGuiError::ContextAlreadyActive);
107 }
108
109 let shared_font_atlas_ptr = match &mut shared_font_atlas {
110 Some(atlas) => atlas.as_ptr_mut(),
111 None => ptr::null_mut(),
112 };
113
114 let raw = unsafe { sys::igCreateContext(shared_font_atlas_ptr) };
116 if raw.is_null() {
117 return Err(crate::error::ImGuiError::ContextCreation {
118 reason: "ImGui_CreateContext returned null".to_string(),
119 });
120 }
121
122 unsafe {
124 sys::igSetCurrentContext(raw);
125 }
126
127 Ok(Context {
128 raw,
129 shared_font_atlas,
130 ini_filename: None,
131 log_filename: None,
132 platform_name: None,
133 renderer_name: None,
134 clipboard_ctx: Box::new(UnsafeCell::new(ClipboardContext::dummy())),
135 ui: crate::ui::Ui::new(),
136 })
137 }
138
139 pub fn io_mut(&mut self) -> &mut Io {
141 let _guard = CTX_MUTEX.lock();
142 unsafe {
143 let io_ptr = sys::igGetIO_Nil();
145 &mut *(io_ptr as *mut Io)
146 }
147 }
148
149 pub fn io(&self) -> &crate::io::Io {
151 let _guard = CTX_MUTEX.lock();
152 unsafe {
153 let io_ptr = sys::igGetIO_Nil();
155 &*(io_ptr as *const crate::io::Io)
156 }
157 }
158
159 pub fn style(&self) -> &crate::style::Style {
161 let _guard = CTX_MUTEX.lock();
162 unsafe {
163 let style_ptr = sys::igGetStyle();
164 &*(style_ptr as *const crate::style::Style)
165 }
166 }
167
168 pub fn style_mut(&mut self) -> &mut crate::style::Style {
170 let _guard = CTX_MUTEX.lock();
171 unsafe {
172 let style_ptr = sys::igGetStyle();
173 &mut *(style_ptr as *mut crate::style::Style)
174 }
175 }
176
177 pub fn frame(&mut self) -> &mut crate::ui::Ui {
179 let _guard = CTX_MUTEX.lock();
180
181 unsafe {
182 sys::igNewFrame();
183 }
184 &mut self.ui
185 }
186
187 pub fn frame_with<F, R>(&mut self, f: F) -> R
189 where
190 F: FnOnce(&crate::ui::Ui) -> R,
191 {
192 let ui = self.frame();
193 f(ui)
194 }
195
196 pub fn render(&mut self) -> &crate::render::DrawData {
201 let _guard = CTX_MUTEX.lock();
202 unsafe {
203 sys::igRender();
204 &*(sys::igGetDrawData() as *const crate::render::DrawData)
205 }
206 }
207
208 pub fn draw_data(&self) -> Option<&crate::render::DrawData> {
213 let _guard = CTX_MUTEX.lock();
214 unsafe {
215 let draw_data = sys::igGetDrawData();
216 if draw_data.is_null() {
217 None
218 } else {
219 let data = &*(draw_data as *const crate::render::DrawData);
220 if data.valid() { Some(data) } else { None }
221 }
222 }
223 }
224
225 pub fn set_ini_filename<P: Into<PathBuf>>(
231 &mut self,
232 filename: Option<P>,
233 ) -> crate::error::ImGuiResult<()> {
234 use crate::error::SafeStringConversion;
235 let _guard = CTX_MUTEX.lock();
236
237 self.ini_filename = match filename {
238 Some(f) => Some(f.into().to_string_lossy().to_cstring_safe()?),
239 None => None,
240 };
241
242 unsafe {
243 let io = sys::igGetIO_Nil();
244 let ptr = self
245 .ini_filename
246 .as_ref()
247 .map(|s| s.as_ptr())
248 .unwrap_or(ptr::null());
249 (*io).IniFilename = ptr;
250 }
251 Ok(())
252 }
253
254 pub fn set_log_filename<P: Into<PathBuf>>(
262 &mut self,
263 filename: Option<P>,
264 ) -> crate::error::ImGuiResult<()> {
265 use crate::error::SafeStringConversion;
266 let _guard = CTX_MUTEX.lock();
267
268 self.log_filename = match filename {
269 Some(f) => Some(f.into().to_string_lossy().to_cstring_safe()?),
270 None => None,
271 };
272
273 unsafe {
274 let io = sys::igGetIO_Nil();
275 let ptr = self
276 .log_filename
277 .as_ref()
278 .map(|s| s.as_ptr())
279 .unwrap_or(ptr::null());
280 (*io).LogFilename = ptr;
281 }
282 Ok(())
283 }
284
285 pub fn set_platform_name<S: Into<String>>(
293 &mut self,
294 name: Option<S>,
295 ) -> crate::error::ImGuiResult<()> {
296 use crate::error::SafeStringConversion;
297 let _guard = CTX_MUTEX.lock();
298
299 self.platform_name = match name {
300 Some(n) => Some(n.into().to_cstring_safe()?),
301 None => None,
302 };
303
304 unsafe {
305 let io = sys::igGetIO_Nil();
306 let ptr = self
307 .platform_name
308 .as_ref()
309 .map(|s| s.as_ptr())
310 .unwrap_or(ptr::null());
311 (*io).BackendPlatformName = ptr;
312 }
313 Ok(())
314 }
315
316 pub fn set_renderer_name<S: Into<String>>(
324 &mut self,
325 name: Option<S>,
326 ) -> crate::error::ImGuiResult<()> {
327 use crate::error::SafeStringConversion;
328 let _guard = CTX_MUTEX.lock();
329
330 self.renderer_name = match name {
331 Some(n) => Some(n.into().to_cstring_safe()?),
332 None => None,
333 };
334
335 unsafe {
336 let io = sys::igGetIO_Nil();
337 let ptr = self
338 .renderer_name
339 .as_ref()
340 .map(|s| s.as_ptr())
341 .unwrap_or(ptr::null());
342 (*io).BackendRendererName = ptr;
343 }
344 Ok(())
345 }
346
347 #[cfg(feature = "multi-viewport")]
351 pub fn platform_io_mut(&mut self) -> &mut crate::platform_io::PlatformIo {
352 let _guard = CTX_MUTEX.lock();
353 unsafe {
354 let pio = sys::igGetPlatformIO_Nil();
355 crate::platform_io::PlatformIo::from_raw_mut(pio)
356 }
357 }
358
359 #[cfg(feature = "multi-viewport")]
361 pub fn enable_multi_viewport(&mut self) {
362 crate::viewport_backend::utils::enable_viewport_flags(self.io_mut());
364 }
365
366 #[cfg(feature = "multi-viewport")]
371 pub fn update_platform_windows(&mut self) {
372 let _guard = CTX_MUTEX.lock();
373 unsafe {
374 let main_viewport = sys::igGetMainViewport();
376 if !main_viewport.is_null() && (*main_viewport).PlatformHandle.is_null() {
377 eprintln!("update_platform_windows: main viewport not set up, setting it up now");
378 }
381
382 sys::igUpdatePlatformWindows();
383 }
384 }
385
386 #[cfg(feature = "multi-viewport")]
391 pub fn render_platform_windows_default(&mut self) {
392 let _guard = CTX_MUTEX.lock();
393 unsafe {
394 sys::igRenderPlatformWindowsDefault(std::ptr::null_mut(), std::ptr::null_mut());
395 }
396 }
397
398 #[cfg(feature = "multi-viewport")]
403 pub fn destroy_platform_windows(&mut self) {
404 let _guard = CTX_MUTEX.lock();
405 unsafe {
406 sys::igDestroyPlatformWindows();
407 }
408 }
409
410 pub fn suspend(self) -> SuspendedContext {
412 let _guard = CTX_MUTEX.lock();
413 assert!(
414 self.is_current_context(),
415 "context to be suspended is not the active context"
416 );
417 clear_current_context();
418 SuspendedContext(self)
419 }
420
421 fn is_current_context(&self) -> bool {
422 let ctx = unsafe { sys::igGetCurrentContext() };
423 self.raw == ctx
424 }
425
426 pub fn push_font(&mut self, font: &Font) {
428 let _guard = CTX_MUTEX.lock();
429 unsafe {
430 sys::igPushFont(font.raw(), 0.0);
431 }
432 }
433
434 #[doc(alias = "PopFont")]
438 pub fn pop_font(&mut self) {
439 let _guard = CTX_MUTEX.lock();
440 unsafe {
441 sys::igPopFont();
442 }
443 }
444
445 #[doc(alias = "GetFont")]
447 pub fn current_font(&self) -> &Font {
448 let _guard = CTX_MUTEX.lock();
449 unsafe { Font::from_raw(sys::igGetFont() as *const _) }
450 }
451
452 #[doc(alias = "GetFontSize")]
454 pub fn current_font_size(&self) -> f32 {
455 let _guard = CTX_MUTEX.lock();
456 unsafe { sys::igGetFontSize() }
457 }
458
459 pub fn font_atlas(&self) -> FontAtlas {
461 let _guard = CTX_MUTEX.lock();
462 unsafe {
463 let io = sys::igGetIO_Nil();
464 let atlas_ptr = (*io).Fonts;
465 FontAtlas::from_raw(atlas_ptr)
466 }
467 }
468
469 pub fn font_atlas_mut(&mut self) -> FontAtlas {
471 let _guard = CTX_MUTEX.lock();
472
473 #[cfg(target_arch = "wasm32")]
475 {
476 return unsafe { FontAtlas::from_raw(ptr::null_mut()) };
477 }
478
479 #[cfg(not(target_arch = "wasm32"))]
480 unsafe {
481 let io = sys::igGetIO_Nil();
482 let atlas_ptr = (*io).Fonts;
483 FontAtlas::from_raw(atlas_ptr)
484 }
485 }
486
487 pub fn fonts(&mut self) -> FontAtlas {
491 self.font_atlas_mut()
492 }
493
494 pub fn clone_shared_font_atlas(&mut self) -> Option<SharedFontAtlas> {
496 self.shared_font_atlas.clone()
497 }
498
499 #[doc(alias = "LoadIniSettingsFromMemory")]
501 pub fn load_ini_settings(&mut self, data: &str) {
502 let _guard = CTX_MUTEX.lock();
503 unsafe {
504 sys::igLoadIniSettingsFromMemory(data.as_ptr() as *const _, data.len());
505 }
506 }
507
508 #[doc(alias = "SaveIniSettingsToMemory")]
510 pub fn save_ini_settings(&mut self, buf: &mut String) {
511 let _guard = CTX_MUTEX.lock();
512 unsafe {
513 let data_ptr = sys::igSaveIniSettingsToMemory(ptr::null_mut());
514 if !data_ptr.is_null() {
515 let data = std::ffi::CStr::from_ptr(data_ptr);
516 buf.push_str(&data.to_string_lossy());
517 }
518 }
519 }
520
521 pub fn set_clipboard_backend<T: ClipboardBackend>(&mut self, backend: T) {
523 let clipboard_ctx: Box<UnsafeCell<_>> =
524 Box::new(UnsafeCell::new(ClipboardContext::new(backend)));
525
526 unsafe {
528 let platform_io = sys::igGetPlatformIO_Nil();
529 (*platform_io).Platform_SetClipboardTextFn = Some(crate::clipboard::set_clipboard_text);
530 (*platform_io).Platform_GetClipboardTextFn = Some(crate::clipboard::get_clipboard_text);
531 (*platform_io).Platform_ClipboardUserData = clipboard_ctx.get() as *mut _;
532 }
533
534 self.clipboard_ctx = clipboard_ctx;
535 }
536}
537
538impl Drop for Context {
539 fn drop(&mut self) {
540 let _guard = CTX_MUTEX.lock();
541 unsafe {
542 if !self.raw.is_null() {
543 if sys::igGetCurrentContext() == self.raw {
544 clear_current_context();
545 }
546 sys::igDestroyContext(self.raw);
547 }
548 }
549 }
550}
551
552#[derive(Debug)]
556pub struct SuspendedContext(Context);
557
558impl SuspendedContext {
559 pub fn try_create() -> crate::error::ImGuiResult<Self> {
561 Self::try_create_internal(None)
562 }
563
564 pub fn try_create_with_shared_font_atlas(
566 shared_font_atlas: SharedFontAtlas,
567 ) -> crate::error::ImGuiResult<Self> {
568 Self::try_create_internal(Some(shared_font_atlas))
569 }
570
571 pub fn create() -> Self {
573 Self::try_create().expect("Failed to create Dear ImGui context")
574 }
575
576 pub fn create_with_shared_font_atlas(shared_font_atlas: SharedFontAtlas) -> Self {
578 Self::try_create_with_shared_font_atlas(shared_font_atlas)
579 .expect("Failed to create Dear ImGui context")
580 }
581
582 fn try_create_internal(
585 mut shared_font_atlas: Option<SharedFontAtlas>,
586 ) -> crate::error::ImGuiResult<Self> {
587 let _guard = CTX_MUTEX.lock();
588
589 let shared_font_atlas_ptr = match &mut shared_font_atlas {
590 Some(atlas) => atlas.as_ptr_mut(),
591 None => ptr::null_mut(),
592 };
593
594 let raw = unsafe { sys::igCreateContext(shared_font_atlas_ptr) };
595 if raw.is_null() {
596 return Err(crate::error::ImGuiError::ContextCreation {
597 reason: "ImGui_CreateContext returned null".to_string(),
598 });
599 }
600
601 let ctx = Context {
602 raw,
603 shared_font_atlas,
604 ini_filename: None,
605 log_filename: None,
606 platform_name: None,
607 renderer_name: None,
608 clipboard_ctx: Box::new(UnsafeCell::new(ClipboardContext::dummy())),
609 ui: crate::ui::Ui::new(),
610 };
611
612 if ctx.is_current_context() {
614 clear_current_context();
615 }
616
617 Ok(SuspendedContext(ctx))
618 }
619
620 pub fn activate(self) -> Result<Context, SuspendedContext> {
625 let _guard = CTX_MUTEX.lock();
626 if no_current_context() {
627 unsafe {
628 sys::igSetCurrentContext(self.0.raw);
629 }
630 Ok(self.0)
631 } else {
632 Err(self)
633 }
634 }
635}
636
637