From 3a9cf40de3b9c6ae8ddd139415d3812b791cec1a Mon Sep 17 00:00:00 2001 From: Robin Quint Date: Mon, 28 Feb 2022 16:45:52 +0100 Subject: [PATCH] Implemented basic viewport support --- imgui-examples/examples/hello_world.rs | 4 +- imgui-examples/examples/support/mod.rs | 8 +- imgui-glium-renderer/src/lib.rs | 3 + imgui-winit-support/src/lib.rs | 134 +++++++- imgui/src/context.rs | 66 +++- imgui/src/internal.rs | 15 +- imgui/src/io.rs | 73 +---- imgui/src/lib.rs | 2 + imgui/src/platform_io.rs | 418 +++++++++++++++++++++++++ 9 files changed, 646 insertions(+), 77 deletions(-) create mode 100644 imgui/src/platform_io.rs diff --git a/imgui-examples/examples/hello_world.rs b/imgui-examples/examples/hello_world.rs index 29f74ed..4a70e86 100644 --- a/imgui-examples/examples/hello_world.rs +++ b/imgui-examples/examples/hello_world.rs @@ -3,7 +3,9 @@ use imgui::*; mod support; fn main() { - let system = support::init(file!()); + let mut system = support::init(file!()); + system.enable_viewports(); + let mut value = 0; let choices = ["test test this is 1", "test test this is 2"]; system.main_loop(move |_, ui| { diff --git a/imgui-examples/examples/support/mod.rs b/imgui-examples/examples/support/mod.rs index 44e8825..52c4bcd 100644 --- a/imgui-examples/examples/support/mod.rs +++ b/imgui-examples/examples/support/mod.rs @@ -3,10 +3,11 @@ use glium::glutin::event::{Event, WindowEvent}; use glium::glutin::event_loop::{ControlFlow, EventLoop}; use glium::glutin::window::WindowBuilder; use glium::{Display, Surface}; -use imgui::{Context, FontConfig, FontGlyphRanges, FontSource, Ui}; +use imgui::{Context, FontConfig, FontGlyphRanges, FontSource, Ui, ConfigFlags}; use imgui_glium_renderer::Renderer; use imgui_winit_support::{HiDpiMode, WinitPlatform}; use std::path::Path; +use std::rc::Rc; use std::time::Instant; mod clipboard; @@ -112,6 +113,11 @@ pub fn init(title: &str) -> System { } impl System { + pub fn enable_viewports(&mut self) { + WinitPlatform::init_viewports(&mut self.imgui, &self.event_loop, self.display.gl_window().window()); + self.imgui.io_mut().config_flags.insert(ConfigFlags::VIEWPORTS_ENABLE); + } + pub fn main_loop(self, mut run_ui: F) { let System { event_loop, diff --git a/imgui-glium-renderer/src/lib.rs b/imgui-glium-renderer/src/lib.rs index eddc659..b1ea68b 100644 --- a/imgui-glium-renderer/src/lib.rs +++ b/imgui-glium-renderer/src/lib.rs @@ -150,6 +150,9 @@ impl Renderer { ctx.io_mut() .backend_flags .insert(BackendFlags::RENDERER_HAS_VTX_OFFSET); + ctx.io_mut() + .backend_flags + .insert(BackendFlags::RENDERER_HAS_VIEWPORTS); Ok(Renderer { ctx: Rc::clone(facade.get_context()), program, diff --git a/imgui-winit-support/src/lib.rs b/imgui-winit-support/src/lib.rs index 3c2c65d..0b79529 100644 --- a/imgui-winit-support/src/lib.rs +++ b/imgui-winit-support/src/lib.rs @@ -188,8 +188,8 @@ use winit_20 as winit; ))] use winit_19 as winit; -use imgui::{self, BackendFlags, ConfigFlags, Context, Io, Key, Ui}; -use std::cell::Cell; +use imgui::{self, BackendFlags, ConfigFlags, Context, Io, Key, Ui, PlatformViewportBackend, ViewportFlags, PlatformMonitor, Viewport}; +use std::{cell::Cell, rc::Rc}; use std::cmp::Ordering; use winit::dpi::{LogicalPosition, LogicalSize}; @@ -444,6 +444,110 @@ impl HiDpiMode { } } +struct ViewportBackend { + event_loop: *const winit::event_loop::EventLoopWindowTarget<()>, +} + +enum PlatformHandle { + MainWindow(*const winit::window::Window), + SecondaryWindow(winit::window::Window), +} + +impl PlatformHandle { + fn get(&self) -> &winit::window::Window { + match self { + PlatformHandle::MainWindow(ptr) => unsafe { &**ptr }, + PlatformHandle::SecondaryWindow(wnd) => wnd, + } + } +} + +impl PlatformViewportBackend for ViewportBackend { + fn create_window(&mut self, viewport: &mut imgui::Viewport) { + let window = winit::window::WindowBuilder::new() + .with_always_on_top(viewport.flags.contains(ViewportFlags::TOP_MOST)) + .with_decorations(!viewport.flags.contains(ViewportFlags::NO_DECORATION)) + .with_resizable(true) + .with_visible(false) + .build(unsafe{&*self.event_loop}) + .unwrap(); + + viewport.platform_handle = Box::into_raw(Box::new(PlatformHandle::SecondaryWindow(window))) as *mut _; + } + + fn destroy_window(&mut self, viewport: &mut imgui::Viewport) { + unsafe { + // drop window + Box::from_raw(viewport.platform_handle as *mut PlatformHandle); + } + } + + fn show_window(&mut self, viewport: &mut imgui::Viewport) { + let window = unsafe { (*(viewport.platform_handle as *const PlatformHandle)).get() }; + window.set_visible(true); + } + + fn set_window_pos(&mut self, viewport: &mut imgui::Viewport, pos: [f32; 2]) { + let window = unsafe { (*(viewport.platform_handle as *const PlatformHandle)).get() }; + window.set_outer_position(winit::dpi::LogicalPosition::new(pos[0], pos[1])); + } + + fn get_window_pos(&mut self, viewport: &mut imgui::Viewport) -> [f32; 2] { + let window = unsafe { (*(viewport.platform_handle as *const PlatformHandle)).get() }; + let pos = window.outer_position().unwrap(); + [pos.x as f32, pos.y as f32] + } + + fn set_window_size(&mut self, viewport: &mut imgui::Viewport, size: [f32; 2]) { + let window = unsafe { (*(viewport.platform_handle as *const PlatformHandle)).get() }; + window.set_inner_size(winit::dpi::LogicalSize::new(size[0], size[1])); + } + + fn get_window_size(&mut self, viewport: &mut imgui::Viewport) -> [f32; 2] { + let window = unsafe { (*(viewport.platform_handle as *const PlatformHandle)).get() }; + let size = window.inner_size(); + [size.width as f32, size.width as f32] + } + + fn set_window_focus(&mut self, viewport: &mut imgui::Viewport) { + let window = unsafe { (*(viewport.platform_handle as *const PlatformHandle)).get() }; + window.focus_window(); + } + + fn get_window_focus(&mut self, viewport: &mut imgui::Viewport) -> bool { + true + } + + fn get_window_minimized(&mut self, viewport: &mut imgui::Viewport) -> bool { + false + } + + fn set_window_title(&mut self, viewport: &mut imgui::Viewport, title: &str) { + let window = unsafe { (*(viewport.platform_handle as *const PlatformHandle)).get() }; + window.set_title(title); + } + + fn set_window_alpha(&mut self, viewport: &mut imgui::Viewport, alpha: f32) { + + } + + fn update_window(&mut self, viewport: &mut imgui::Viewport) { + + } + + fn render_window(&mut self, viewport: &mut imgui::Viewport) { + + } + + fn swap_buffers(&mut self, viewport: &mut imgui::Viewport) { + + } + + fn create_vk_surface(&mut self, viewport: &mut imgui::Viewport, instance: u64, out_surface: &mut u64) -> i32 { + 0 + } +} + impl WinitPlatform { /// Initializes a winit platform instance and configures imgui. /// @@ -491,6 +595,32 @@ impl WinitPlatform { mouse_buttons: [Button::INIT; 5], } } + + pub fn init_viewports(imgui: &mut Context, event_loop: &winit::event_loop::EventLoopWindowTarget<()>, main_window: &winit::window::Window) { + let io = imgui.io_mut(); + io.backend_flags.insert(BackendFlags::PLATFORM_HAS_VIEWPORTS); + + imgui.set_platform_backend(ViewportBackend{ + event_loop: event_loop as *const _, + }); + + let pio = imgui.platform_io_mut(); + let mut monitors = Vec::new(); + for monitor in main_window.available_monitors() { + monitors.push(PlatformMonitor { + main_pos: [monitor.position().x as f32, monitor.position().y as f32], + main_size: [monitor.size().width as f32, monitor.size().height as f32], + work_pos: [monitor.position().x as f32, monitor.position().y as f32], + work_size: [monitor.size().width as f32, monitor.size().height as f32], + dpi_scale: 1.0, + }); + } + pio.monitors.replace_from_slice(&monitors); + + let main_viewport = imgui.main_viewport_mut(); + main_viewport.platform_handle = Box::into_raw(Box::new(PlatformHandle::MainWindow(main_window as *const _))) as *mut _; + } + /// Attaches the platform instance to a winit window. /// /// This function configures imgui-rs in the following ways: diff --git a/imgui/src/context.rs b/imgui/src/context.rs index 8964f56..93ea312 100644 --- a/imgui/src/context.rs +++ b/imgui/src/context.rs @@ -9,7 +9,7 @@ use crate::clipboard::{ClipboardBackend, ClipboardContext}; use crate::fonts::atlas::{FontAtlas, FontId, SharedFontAtlas}; use crate::io::Io; use crate::style::Style; -use crate::{sys, DrawData, PlatformIo}; +use crate::{sys, DrawData, PlatformIo, PlatformViewportContext, RendererViewportContext, PlatformViewportBackend, RendererViewportBackend, Viewport}; use crate::{MouseCursor, Ui}; /// An imgui-rs context. @@ -60,6 +60,9 @@ pub struct Context { // imgui a mutable pointer to it. clipboard_ctx: Box>, + platform_viewport_ctx: Box>, + renderer_viewport_ctx: Box>, + ui: Ui, } @@ -216,6 +219,51 @@ impl Context { io.clipboard_user_data = clipboard_ctx.get() as *mut _; self.clipboard_ctx = clipboard_ctx; } + pub fn set_platform_backend(&mut self, backend: T) { + let ctx = Box::new(UnsafeCell::new(PlatformViewportContext { + backend: Box::new(backend), + })); + + let io = self.io_mut(); + io.backend_platform_user_data = ctx.get() as *mut _; + + let pio = self.platform_io_mut(); + pio.platform_create_window = Some(crate::platform_io::platform_create_window); + pio.platform_destroy_window = Some(crate::platform_io::platform_destroy_window); + pio.platform_show_window = Some(crate::platform_io::platform_show_window); + pio.platform_set_window_pos = Some(crate::platform_io::platform_set_window_pos); + pio.platform_get_window_pos = Some(crate::platform_io::platform_get_window_pos); + pio.platform_set_window_size = Some(crate::platform_io::platform_set_window_size); + pio.platform_get_window_size = Some(crate::platform_io::platform_get_window_size); + pio.platform_set_window_focus = Some(crate::platform_io::platform_set_window_focus); + pio.platform_get_window_focus = Some(crate::platform_io::platform_get_window_focus); + pio.platform_get_window_minimized = Some(crate::platform_io::platform_get_window_minimized); + pio.platform_set_window_title = Some(crate::platform_io::platform_set_window_title); + pio.platform_set_window_alpha = Some(crate::platform_io::platform_set_window_alpha); + pio.platform_update_window = Some(crate::platform_io::platform_update_window); + pio.platform_render_window = Some(crate::platform_io::platform_render_window); + pio.platform_swap_buffers = Some(crate::platform_io::platform_swap_buffers); + pio.platform_create_vk_surface = Some(crate::platform_io::platform_create_vk_surface); + + self.platform_viewport_ctx = ctx; + } + pub fn set_renderer_backend(&mut self, backend: T) { + let ctx = Box::new(UnsafeCell::new(RendererViewportContext { + backend: Box::new(backend), + })); + + let io = self.io_mut(); + io.backend_renderer_user_data = ctx.get() as *mut _; + + let pio = self.platform_io_mut(); + pio.renderer_create_window = Some(crate::platform_io::renderer_create_window); + pio.renderer_destroy_window = Some(crate::platform_io::renderer_destroy_window); + pio.renderer_set_window_size = Some(crate::platform_io::renderer_set_window_size); + pio.renderer_render_window = Some(crate::platform_io::renderer_render_window); + pio.renderer_swap_buffers = Some(crate::platform_io::renderer_swap_buffers); + + self.renderer_viewport_ctx = ctx; + } fn create_internal(mut shared_font_atlas: Option) -> Self { let _guard = CTX_MUTEX.lock(); assert!( @@ -239,6 +287,8 @@ impl Context { platform_name: None, renderer_name: None, clipboard_ctx: Box::new(ClipboardContext::dummy().into()), + platform_viewport_ctx: Box::new(UnsafeCell::new(PlatformViewportContext::dummy())), + renderer_viewport_ctx: Box::new(UnsafeCell::new(RendererViewportContext::dummy())), ui: Ui { buffer: UnsafeCell::new(crate::string::UiBuffer::new(1024)), }, @@ -328,6 +378,8 @@ impl SuspendedContext { platform_name: None, renderer_name: None, clipboard_ctx: Box::new(ClipboardContext::dummy().into()), + platform_viewport_ctx: Box::new(UnsafeCell::new(PlatformViewportContext::dummy())), + renderer_viewport_ctx: Box::new(UnsafeCell::new(RendererViewportContext::dummy())), ui: Ui { buffer: UnsafeCell::new(crate::string::UiBuffer::new(1024)), }, @@ -485,12 +537,22 @@ impl Context { &*(sys::igGetPlatformIO() as *const PlatformIo) } } - pub fn platform_io_mut(&mut self) -> &PlatformIo { + pub fn platform_io_mut(&mut self) -> &mut PlatformIo { unsafe { // safe because PlatformIo is a transparent wrapper around sys::ImGuiPlatformIO &mut *(sys::igGetPlatformIO() as *mut PlatformIo) } } + pub fn main_viewport(&self) -> &Viewport { + unsafe { + &*(sys::igGetMainViewport() as *mut Viewport) + } + } + pub fn main_viewport_mut(&mut self) -> &mut Viewport { + unsafe { + &mut *(sys::igGetMainViewport() as *mut Viewport) + } + } /// Returns an immutable reference to the user interface style #[doc(alias = "GetStyle")] pub fn style(&self) -> &Style { diff --git a/imgui/src/internal.rs b/imgui/src/internal.rs index 0af90a1..af24cca 100644 --- a/imgui/src/internal.rs +++ b/imgui/src/internal.rs @@ -1,6 +1,6 @@ //! Internal raw utilities (don't use unless you know what you're doing!) -use std::slice; +use std::{slice, mem::size_of}; /// A generic version of the raw imgui-sys ImVector struct types #[repr(C)] @@ -15,6 +15,19 @@ impl ImVector { pub fn as_slice(&self) -> &[T] { unsafe { slice::from_raw_parts(self.data, self.size as usize) } } + + pub fn replace_from_slice(&mut self, data: &[T]) { + unsafe { + sys::igMemFree(self.data as *mut _); + + let buffer_ptr = sys::igMemAlloc(size_of::() * data.len()) as *mut T; + buffer_ptr.copy_from_nonoverlapping(data.as_ptr(), data.len()); + + self.size = data.len() as i32; + self.capacity = data.len() as i32; + self.data = buffer_ptr; + } + } } #[test] diff --git a/imgui/src/io.rs b/imgui/src/io.rs index d4c4e92..7681ed8 100644 --- a/imgui/src/io.rs +++ b/imgui/src/io.rs @@ -1,7 +1,7 @@ use bitflags::bitflags; use std::f32; use std::ops::{Index, IndexMut}; -use std::os::raw::{c_char, c_void, c_int}; +use std::os::raw::{c_char, c_void}; use std::time::Duration; use crate::fonts::atlas::FontAtlas; @@ -158,73 +158,6 @@ fn test_nav_input_variants() { } } -#[cfg(feature = "docking")] -#[repr(C)] -pub struct PlatformIo { - pub(crate) platform_create_window: Option, - pub(crate) platform_destroy_window: Option, - pub(crate) platform_show_window: Option, - pub(crate) platform_set_window_pos: Option, - pub(crate) platform_get_window_pos: Option sys::ImVec2>, - pub(crate) platform_set_window_size: Option, - pub(crate) platform_get_window_size: Option sys::ImVec2>, - pub(crate) platform_set_window_focus: Option, - pub(crate) platform_get_window_focus: Option bool>, - pub(crate) platform_get_window_minimized: Option bool>, - pub(crate) platform_set_window_title: Option, - pub(crate) platform_set_window_alpha: Option, - pub(crate) platform_update_window: Option, - pub(crate) platform_render_window: Option, - pub(crate) platform_swap_buffers: Option, - pub(crate) platform_get_window_dpi_scale: Option f32>, - pub(crate) platform_on_changed_viewport: Option, - pub(crate) platform_create_vk_surface: Option c_int>, - - pub(crate) renderer_create_window: Option, - pub(crate) renderer_destroy_window: Option, - pub(crate) renderer_set_window_size: Option, - pub(crate) renderer_render_window: Option, - pub(crate) renderer_swap_buffers: Option, - - pub(crate) monitors: ImVector, - - pub(crate) viewports: ImVector<*mut Viewport>, -} - -unsafe impl RawCast for PlatformIo {} - -#[cfg(feature = "docking")] -#[repr(C)] -pub struct Viewport { - pub(crate) id: crate::Id, - pub(crate) flags: ViewportFlags, - pub(crate) pos: [f32; 2], - pub(crate) size: [f32; 2], - pub(crate) work_pos: [f32; 2], - pub(crate) work_size: [f32; 2], - pub(crate) dpi_scale: f32, - pub(crate) parent_viewport_id: crate::Id, - pub(crate) draw_data: *mut crate::DrawData, - - pub(crate) renderer_user_data: *mut c_void, - pub(crate) platform_user_data: *mut c_void, - pub(crate) platform_handle: *mut c_void, - pub(crate) platform_handle_raw: *mut c_void, - pub(crate) platform_request_move: bool, - pub(crate) platform_request_resize: bool, - pub(crate) platform_request_close: bool, -} - -#[cfg(feature = "docking")] -#[repr(C)] -pub struct PlatformMonitor { - main_pos: [f32; 2], - main_size: [f32; 2], - work_pos: [f32; 2], - work_size: [f32; 2], - dpi_scale: f32, -} - /// Settings and inputs/outputs for imgui-rs #[repr(C)] pub struct Io { @@ -320,8 +253,8 @@ pub struct Io { pub(crate) backend_platform_name: *const c_char, pub(crate) backend_renderer_name: *const c_char, - backend_platform_user_data: *mut c_void, - backend_renderer_user_data: *mut c_void, + pub(crate) backend_platform_user_data: *mut c_void, + pub(crate) backend_renderer_user_data: *mut c_void, backend_language_user_data: *mut c_void, pub(crate) get_clipboard_text_fn: Option *const c_char>, diff --git a/imgui/src/lib.rs b/imgui/src/lib.rs index 7eb289f..b80a2ca 100644 --- a/imgui/src/lib.rs +++ b/imgui/src/lib.rs @@ -20,6 +20,7 @@ pub use self::input::keyboard::*; pub use self::input::mouse::*; pub use self::input_widget::*; pub use self::io::*; +pub use self::platform_io::*; pub use self::layout::*; pub use self::list_clipper::ListClipper; pub use self::plothistogram::PlotHistogram; @@ -68,6 +69,7 @@ mod input; mod input_widget; pub mod internal; mod io; +mod platform_io; mod layout; mod list_clipper; mod math; diff --git a/imgui/src/platform_io.rs b/imgui/src/platform_io.rs new file mode 100644 index 0000000..79a7d78 --- /dev/null +++ b/imgui/src/platform_io.rs @@ -0,0 +1,418 @@ +use std::{os::raw::{c_char, c_int}, ffi::{c_void, CStr}}; + +use crate::{internal::{ImVector, RawCast}, ViewportFlags}; + +#[cfg(feature = "docking")] +#[repr(C)] +pub struct PlatformIo { + pub(crate) platform_create_window: Option, + pub(crate) platform_destroy_window: Option, + pub(crate) platform_show_window: Option, + pub(crate) platform_set_window_pos: Option, + pub(crate) platform_get_window_pos: Option sys::ImVec2>, + pub(crate) platform_set_window_size: Option, + pub(crate) platform_get_window_size: Option sys::ImVec2>, + pub(crate) platform_set_window_focus: Option, + pub(crate) platform_get_window_focus: Option bool>, + pub(crate) platform_get_window_minimized: Option bool>, + pub(crate) platform_set_window_title: Option, + pub(crate) platform_set_window_alpha: Option, + pub(crate) platform_update_window: Option, + pub(crate) platform_render_window: Option, + pub(crate) platform_swap_buffers: Option, + pub(crate) platform_get_window_dpi_scale: Option f32>, + pub(crate) platform_on_changed_viewport: Option, + pub(crate) platform_set_ime_input_pos: Option, + pub(crate) platform_create_vk_surface: Option c_int>, + + pub(crate) renderer_create_window: Option, + pub(crate) renderer_destroy_window: Option, + pub(crate) renderer_set_window_size: Option, + pub(crate) renderer_render_window: Option, + pub(crate) renderer_swap_buffers: Option, + + pub monitors: ImVector, + + pub(crate) viewports: ImVector<*mut Viewport>, +} + +unsafe impl RawCast for PlatformIo {} + +#[test] +#[cfg(test)] +fn test_platform_io_memory_layout() { + use std::mem; + assert_eq!(mem::size_of::(), mem::size_of::()); + assert_eq!(mem::align_of::(), mem::align_of::()); + use sys::ImGuiPlatformIO; + macro_rules! assert_field_offset { + ($l:ident, $r:ident) => { + assert_eq!( + memoffset::offset_of!(PlatformIo, $l), + memoffset::offset_of!(ImGuiPlatformIO, $r) + ); + }; + } + + assert_field_offset!(platform_create_window, Platform_CreateWindow); + assert_field_offset!(platform_destroy_window, Platform_DestroyWindow); + assert_field_offset!(platform_show_window, Platform_ShowWindow); + assert_field_offset!(platform_set_window_pos, Platform_SetWindowPos); + assert_field_offset!(platform_get_window_pos, Platform_GetWindowPos); + assert_field_offset!(platform_set_window_size, Platform_SetWindowSize); + assert_field_offset!(platform_get_window_size, Platform_GetWindowSize); + assert_field_offset!(platform_set_window_focus, Platform_SetWindowFocus); + assert_field_offset!(platform_get_window_focus, Platform_GetWindowFocus); + assert_field_offset!(platform_get_window_minimized, Platform_GetWindowMinimized); + assert_field_offset!(platform_set_window_title, Platform_SetWindowTitle); + assert_field_offset!(platform_set_window_alpha, Platform_SetWindowAlpha); + assert_field_offset!(platform_update_window, Platform_UpdateWindow); + assert_field_offset!(platform_render_window, Platform_RenderWindow); + assert_field_offset!(platform_swap_buffers, Platform_SwapBuffers); + assert_field_offset!(platform_get_window_dpi_scale, Platform_GetWindowDpiScale); + assert_field_offset!(platform_on_changed_viewport, Platform_OnChangedViewport); + assert_field_offset!(platform_set_ime_input_pos, Platform_SetImeInputPos); + assert_field_offset!(platform_create_vk_surface, Platform_CreateVkSurface); + + assert_field_offset!(renderer_create_window, Renderer_CreateWindow); + assert_field_offset!(renderer_destroy_window, Renderer_DestroyWindow); + assert_field_offset!(renderer_set_window_size, Renderer_SetWindowSize); + assert_field_offset!(renderer_render_window, Renderer_RenderWindow); + assert_field_offset!(renderer_swap_buffers, Renderer_SwapBuffers); + + assert_field_offset!(monitors, Monitors); + assert_field_offset!(viewports, Viewports); +} + +pub trait PlatformViewportBackend: 'static { + fn create_window(&mut self, viewport: &mut Viewport); + fn destroy_window(&mut self, viewport: &mut Viewport); + fn show_window(&mut self, viewport: &mut Viewport); + fn set_window_pos(&mut self, viewport: &mut Viewport, pos: [f32; 2]); + fn get_window_pos(&mut self, viewport: &mut Viewport) -> [f32; 2]; + fn set_window_size(&mut self, viewport: &mut Viewport, size: [f32; 2]); + fn get_window_size(&mut self, viewport: &mut Viewport) -> [f32; 2]; + fn set_window_focus(&mut self, viewport: &mut Viewport); + fn get_window_focus(&mut self, viewport: &mut Viewport) -> bool; + fn get_window_minimized(&mut self, viewport: &mut Viewport) -> bool; + fn set_window_title(&mut self, viewport: &mut Viewport, title: &str); + fn set_window_alpha(&mut self, viewport: &mut Viewport, alpha: f32); + + fn update_window(&mut self, viewport: &mut Viewport); + fn render_window(&mut self, viewport: &mut Viewport); + fn swap_buffers(&mut self, viewport: &mut Viewport); + fn create_vk_surface(&mut self, viewport: &mut Viewport, instance: u64, out_surface: &mut u64) -> i32; +} + +fn get_platform_ctx() -> &'static mut PlatformViewportContext { + unsafe { + &mut *((*sys::igGetIO()).BackendPlatformUserData as *mut PlatformViewportContext) + } +} + +fn get_renderer_ctx() -> &'static mut RendererViewportContext { + unsafe { + &mut *((*sys::igGetIO()).BackendRendererUserData as *mut RendererViewportContext) + } +} + +pub(crate) extern "C" fn platform_create_window(viewport: *mut Viewport) { + let ctx = get_platform_ctx(); + ctx.backend.create_window(unsafe{&mut *viewport}); +} +pub(crate) extern "C" fn platform_destroy_window(viewport: *mut Viewport) { + let ctx = get_platform_ctx(); + ctx.backend.destroy_window(unsafe{&mut *viewport}); +} +pub(crate) extern "C" fn platform_show_window(viewport: *mut Viewport) { + let ctx = get_platform_ctx(); + ctx.backend.show_window(unsafe{&mut *viewport}); +} +pub(crate) extern "C" fn platform_set_window_pos(viewport: *mut Viewport, pos: sys::ImVec2) { + let ctx = get_platform_ctx(); + ctx.backend.set_window_pos(unsafe{&mut *viewport}, [pos.x, pos.y]); +} +pub(crate) extern "C" fn platform_get_window_pos(viewport: *mut Viewport) -> sys::ImVec2 { + let ctx = get_platform_ctx(); + let pos = ctx.backend.get_window_pos(unsafe{&mut *viewport}); + sys::ImVec2::new(pos[0], pos[1]) +} +pub(crate) extern "C" fn platform_set_window_size(viewport: *mut Viewport, size: sys::ImVec2) { + let ctx = get_platform_ctx(); + ctx.backend.set_window_size(unsafe{&mut *viewport}, [size.x, size.y]); +} +pub(crate) extern "C" fn platform_get_window_size(viewport: *mut Viewport) -> sys::ImVec2 { + let ctx = get_platform_ctx(); + let size = ctx.backend.get_window_size(unsafe{&mut *viewport}); + sys::ImVec2::new(size[0], size[1]) +} +pub(crate) extern "C" fn platform_set_window_focus(viewport: *mut Viewport) { + let ctx = get_platform_ctx(); + ctx.backend.set_window_focus(unsafe{&mut *viewport}); +} +pub(crate) extern "C" fn platform_get_window_focus(viewport: *mut Viewport) -> bool { + let ctx = get_platform_ctx(); + ctx.backend.get_window_focus(unsafe{&mut *viewport}) +} +pub(crate) extern "C" fn platform_get_window_minimized(viewport: *mut Viewport) -> bool { + let ctx = get_platform_ctx(); + ctx.backend.get_window_minimized(unsafe{&mut *viewport}) +} +pub(crate) extern "C" fn platform_set_window_title(viewport: *mut Viewport, title: *const c_char) { + let ctx = get_platform_ctx(); + let title = unsafe { CStr::from_ptr(title).to_str().unwrap() }; + ctx.backend.set_window_title(unsafe{&mut *viewport}, title); +} +pub(crate) extern "C" fn platform_set_window_alpha(viewport: *mut Viewport, alpha: f32) { + let ctx = get_platform_ctx(); + ctx.backend.set_window_alpha(unsafe{&mut *viewport}, alpha); +} +pub(crate) extern "C" fn platform_update_window(viewport: *mut Viewport) { + let ctx = get_platform_ctx(); + ctx.backend.update_window(unsafe{&mut *viewport}); +} +pub(crate) extern "C" fn platform_render_window(viewport: *mut Viewport, _arg: *mut c_void) { + let ctx = get_platform_ctx(); + ctx.backend.render_window(unsafe{&mut *viewport}); +} +pub(crate) extern "C" fn platform_swap_buffers(viewport: *mut Viewport, _arg: *mut c_void) { + let ctx = get_platform_ctx(); + ctx.backend.swap_buffers(unsafe{&mut *viewport}); +} +pub(crate) extern "C" fn platform_create_vk_surface(viewport: *mut Viewport, instance: u64, _arg: *const c_void, out_surface: *mut u64) -> c_int { + let ctx = get_platform_ctx(); + ctx.backend.create_vk_surface(unsafe{&mut *viewport}, instance, unsafe{&mut *out_surface}) +} + +pub(crate) struct DummyPlatformViewportBackend {} +impl PlatformViewportBackend for DummyPlatformViewportBackend { + fn create_window(&mut self, viewport: &mut Viewport) { + unimplemented!() + } + + fn destroy_window(&mut self, viewport: &mut Viewport) { + unimplemented!() + } + + fn show_window(&mut self, viewport: &mut Viewport) { + unimplemented!() + } + + fn set_window_pos(&mut self, viewport: &mut Viewport, pos: [f32; 2]) { + unimplemented!() + } + + fn get_window_pos(&mut self, viewport: &mut Viewport) -> [f32; 2] { + unimplemented!() + } + + fn set_window_size(&mut self, viewport: &mut Viewport, size: [f32; 2]) { + unimplemented!() + } + + fn get_window_size(&mut self, viewport: &mut Viewport) -> [f32; 2] { + unimplemented!() + } + + fn set_window_focus(&mut self, viewport: &mut Viewport) { + unimplemented!() + } + + fn get_window_focus(&mut self, viewport: &mut Viewport) -> bool { + unimplemented!() + } + + fn get_window_minimized(&mut self, viewport: &mut Viewport) -> bool { + unimplemented!() + } + + fn set_window_title(&mut self, viewport: &mut Viewport, title: &str) { + unimplemented!() + } + + fn set_window_alpha(&mut self, viewport: &mut Viewport, alpha: f32) { + unimplemented!() + } + + fn update_window(&mut self, viewport: &mut Viewport) { + unimplemented!() + } + + fn render_window(&mut self, viewport: &mut Viewport) { + unimplemented!() + } + + fn swap_buffers(&mut self, viewport: &mut Viewport) { + unimplemented!() + } + + fn create_vk_surface(&mut self, viewport: &mut Viewport, instance: u64, out_surface: &mut u64) -> i32 { + unimplemented!() + } +} + +pub(crate) struct PlatformViewportContext { + pub(crate) backend: Box, +} + +impl PlatformViewportContext { + pub(crate) fn dummy() -> Self { + Self { + backend: Box::new(DummyPlatformViewportBackend{}), + } + } +} + +pub trait RendererViewportBackend: 'static { + fn create_window(&mut self, viewport: &mut Viewport); + fn destroy_window(&mut self, viewport: &mut Viewport); + fn set_window_size(&mut self, viewport: &mut Viewport, size: [f32; 2]); + fn render_window(&mut self, viewport: &mut Viewport); + fn swap_buffers(&mut self, viewport: &mut Viewport); +} + +pub(crate) extern "C" fn renderer_create_window(viewport: *mut Viewport) { + let ctx = get_renderer_ctx(); + ctx.backend.create_window(unsafe{&mut *viewport}); +} +pub(crate) extern "C" fn renderer_destroy_window(viewport: *mut Viewport) { + let ctx = get_renderer_ctx(); + ctx.backend.destroy_window(unsafe{&mut *viewport}); +} +pub(crate) extern "C" fn renderer_set_window_size(viewport: *mut Viewport, size: sys::ImVec2) { + let ctx = get_renderer_ctx(); + ctx.backend.set_window_size(unsafe{&mut *viewport}, [size.x, size.y]); +} +pub(crate) extern "C" fn renderer_render_window(viewport: *mut Viewport, _arg: *mut c_void) { + let ctx = get_renderer_ctx(); + ctx.backend.render_window(unsafe{&mut *viewport}); +} +pub(crate) extern "C" fn renderer_swap_buffers(viewport: *mut Viewport, _arg: *mut c_void) { + let ctx = get_renderer_ctx(); + ctx.backend.swap_buffers(unsafe{&mut *viewport}); +} + +pub(crate) struct DummyRendererViewportBackend {} +impl RendererViewportBackend for DummyRendererViewportBackend { + fn create_window(&mut self, viewport: &mut Viewport) { + unimplemented!() + } + + fn destroy_window(&mut self, viewport: &mut Viewport) { + unimplemented!() + } + + fn set_window_size(&mut self, viewport: &mut Viewport, size: [f32; 2]) { + unimplemented!() + } + + fn render_window(&mut self, viewport: &mut Viewport) { + unimplemented!() + } + + fn swap_buffers(&mut self, viewport: &mut Viewport) { + unimplemented!() + } +} + +pub(crate) struct RendererViewportContext { + pub(crate) backend: Box, +} + +impl RendererViewportContext { + pub(crate) fn dummy() -> Self { + Self { + backend: Box::new(DummyRendererViewportBackend{}), + } + } +} + +#[cfg(feature = "docking")] +#[repr(C)] +pub struct Viewport { + pub(crate) id: crate::Id, + pub flags: ViewportFlags, + pub(crate) pos: [f32; 2], + pub(crate) size: [f32; 2], + pub(crate) work_pos: [f32; 2], + pub(crate) work_size: [f32; 2], + pub(crate) dpi_scale: f32, + pub(crate) parent_viewport_id: crate::Id, + pub(crate) draw_data: *mut crate::DrawData, + + pub renderer_user_data: *mut c_void, + pub platform_user_data: *mut c_void, + pub platform_handle: *mut c_void, + pub platform_handle_raw: *mut c_void, + pub platform_request_move: bool, + pub platform_request_resize: bool, + pub platform_request_close: bool, +} + +#[test] +#[cfg(test)] +fn test_viewport_memory_layout() { + use std::mem; + assert_eq!(mem::size_of::(), mem::size_of::()); + assert_eq!(mem::align_of::(), mem::align_of::()); + use sys::ImGuiViewport; + macro_rules! assert_field_offset { + ($l:ident, $r:ident) => { + assert_eq!( + memoffset::offset_of!(Viewport, $l), + memoffset::offset_of!(ImGuiViewport, $r) + ); + }; + } + + assert_field_offset!(id, ID); + assert_field_offset!(flags, Flags); + assert_field_offset!(pos, Pos); + assert_field_offset!(size, Size); + assert_field_offset!(work_pos, WorkPos); + assert_field_offset!(work_size, WorkSize); + assert_field_offset!(dpi_scale, DpiScale); + assert_field_offset!(parent_viewport_id, ParentViewportId); + assert_field_offset!(draw_data, DrawData); + + assert_field_offset!(renderer_user_data, RendererUserData); + assert_field_offset!(platform_user_data, PlatformUserData); + assert_field_offset!(platform_handle, PlatformHandle); + assert_field_offset!(platform_handle_raw, PlatformHandleRaw); + assert_field_offset!(platform_request_move, PlatformRequestMove); + assert_field_offset!(platform_request_resize, PlatformRequestResize); + assert_field_offset!(platform_request_close, PlatformRequestClose); +} + +#[cfg(feature = "docking")] +#[repr(C)] +pub struct PlatformMonitor { + pub main_pos: [f32; 2], + pub main_size: [f32; 2], + pub work_pos: [f32; 2], + pub work_size: [f32; 2], + pub dpi_scale: f32, +} + +#[test] +#[cfg(test)] +fn test_platform_monitor_memory_layout() { + use std::mem; + assert_eq!(mem::size_of::(), mem::size_of::()); + assert_eq!(mem::align_of::(), mem::align_of::()); + use sys::ImGuiPlatformMonitor; + macro_rules! assert_field_offset { + ($l:ident, $r:ident) => { + assert_eq!( + memoffset::offset_of!(PlatformMonitor, $l), + memoffset::offset_of!(ImGuiPlatformMonitor, $r) + ); + }; + } + + assert_field_offset!(main_pos, MainPos); + assert_field_offset!(main_size, MainSize); + assert_field_offset!(work_pos, WorkPos); + assert_field_offset!(work_size, WorkSize); + assert_field_offset!(dpi_scale, DpiScale); +}