Merge pull request #619 from Rob2309/main

Expose Viewport functionality
This commit is contained in:
dbr/Ben 2023-02-02 13:02:18 +10:30 committed by GitHub
commit a2ad8dd955
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 2162 additions and 8 deletions

View File

@ -22,7 +22,7 @@ jobs:
strategy:
matrix:
rust: ["1.57"]
rust: ["1.60"]
env:
RUSTFLAGS: -D warnings
@ -81,7 +81,7 @@ jobs:
matrix:
rust:
- stable
- "1.57"
- "1.60"
os:
- ubuntu-latest
- macos-latest

View File

@ -6,6 +6,7 @@ members = [
"imgui-glow-renderer",
"imgui-sdl2-support",
"imgui-winit-support",
"imgui-winit-glow-renderer-viewports",
"imgui-examples",
"xtask",
]

View File

@ -60,7 +60,7 @@ be applicable to usage with any backend/renderer.
## Minimum Support Rust Version (MSRV)
The MSRV for `imgui-rs` and all of the backend crates is **1.57**. We update our MSRV periodically, and issue a minor bump for it.
The MSRV for `imgui-rs` and all of the backend crates is **1.60**. We update our MSRV periodically, and issue a minor bump for it.
## Choosing a backend platform and a renderer

View File

@ -4,8 +4,10 @@ mod support;
fn main() {
let system = support::init(file!());
let mut value = 0;
let choices = ["test test this is 1", "test test this is 2"];
system.main_loop(move |_, ui| {
ui.window("Hello world")
.size([300.0, 110.0], Condition::FirstUseEver)

View File

@ -190,7 +190,7 @@ struct HumanData {
}
impl HumanData {
pub fn sort_humans(humans: &mut Vec<Self>, specs: Specs<'_>) {
pub fn sort_humans(humans: &mut [Self], specs: Specs<'_>) {
let spec = specs.iter().next().unwrap();
if let Some(kind) = spec.sort_direction() {
match kind {

View File

@ -0,0 +1,16 @@
[package]
name = "imgui-winit-glow-renderer-viewports"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
imgui = { version="0.10.0", path="../imgui", features=["docking"] }
glow = "0.11.2"
glutin = "0.30.3"
raw-window-handle = "0.5.0"
winit = "0.27.5"
thiserror = "1.0.38"
glutin-winit = "0.2.1"

View File

@ -0,0 +1,162 @@
use std::{ffi::CString, num::NonZeroU32, time::Instant};
use glow::{Context, HasContext};
use glutin::{
config::ConfigTemplateBuilder,
context::ContextAttributesBuilder,
display::GetGlDisplay,
prelude::{
GlDisplay, NotCurrentGlContextSurfaceAccessor, PossiblyCurrentContextGlSurfaceAccessor,
},
surface::{GlSurface, SurfaceAttributesBuilder, WindowSurface},
};
use glutin_winit::DisplayBuilder;
use imgui::ConfigFlags;
use imgui_winit_glow_renderer_viewports::Renderer;
use raw_window_handle::HasRawWindowHandle;
use winit::{dpi::LogicalSize, event::WindowEvent, event_loop::EventLoop, window::WindowBuilder};
fn main() {
let event_loop = EventLoop::new();
let window_builder = WindowBuilder::new()
.with_inner_size(LogicalSize::new(800.0, 600.0))
.with_visible(true)
.with_resizable(true)
.with_title("Viewports example");
let template_builder = ConfigTemplateBuilder::new();
let (window, gl_config) = DisplayBuilder::new()
.with_window_builder(Some(window_builder))
.build(&event_loop, template_builder, |mut configs| {
configs.next().unwrap()
})
.expect("Failed to create main window");
let window = window.unwrap();
let context_attribs = ContextAttributesBuilder::new().build(Some(window.raw_window_handle()));
let context = unsafe {
gl_config
.display()
.create_context(&gl_config, &context_attribs)
.expect("Failed to create main context")
};
let size = window.inner_size();
let surface_attribs = SurfaceAttributesBuilder::<WindowSurface>::new().build(
window.raw_window_handle(),
NonZeroU32::new(size.width).unwrap(),
NonZeroU32::new(size.height).unwrap(),
);
let surface = unsafe {
gl_config
.display()
.create_window_surface(&gl_config, &surface_attribs)
.expect("Failed to create main surface")
};
let context = context
.make_current(&surface)
.expect("Failed to make current");
let glow = unsafe {
Context::from_loader_function(|name| {
let name = CString::new(name).unwrap();
context.display().get_proc_address(&name)
})
};
let mut imgui = imgui::Context::create();
imgui
.io_mut()
.config_flags
.insert(ConfigFlags::DOCKING_ENABLE);
imgui
.io_mut()
.config_flags
.insert(ConfigFlags::VIEWPORTS_ENABLE);
imgui.set_ini_filename(None);
let mut renderer = Renderer::new(&mut imgui, &window, &glow).expect("Failed to init Renderer");
let mut last_frame = Instant::now();
event_loop.run(move |event, window_target, control_flow| {
control_flow.set_poll();
renderer.handle_event(&mut imgui, &window, &event);
match event {
winit::event::Event::NewEvents(_) => {
let now = Instant::now();
imgui.io_mut().update_delta_time(now - last_frame);
last_frame = now;
}
winit::event::Event::WindowEvent {
window_id,
event: WindowEvent::CloseRequested,
} if window_id == window.id() => {
control_flow.set_exit();
}
winit::event::Event::WindowEvent {
window_id,
event: WindowEvent::Resized(new_size),
} if window_id == window.id() => {
surface.resize(
&context,
NonZeroU32::new(new_size.width).unwrap(),
NonZeroU32::new(new_size.height).unwrap(),
);
}
winit::event::Event::MainEventsCleared => {
window.request_redraw();
}
winit::event::Event::RedrawRequested(_) => {
let ui = imgui.frame();
ui.dockspace_over_main_viewport();
ui.show_demo_window(&mut true);
ui.window("Style Editor").build(|| {
ui.show_default_style_editor();
});
ui.end_frame_early();
renderer.prepare_render(&mut imgui, &window);
imgui.update_platform_windows();
renderer
.update_viewports(&mut imgui, window_target, &glow)
.expect("Failed to update viewports");
let draw_data = imgui.render();
if let Err(e) = context.make_current(&surface) {
// For some reason make_current randomly throws errors on windows.
// Until the reason for this is found, we just print it out instead of panicing.
eprintln!("Failed to make current: {e}");
}
unsafe {
glow.disable(glow::SCISSOR_TEST);
glow.clear(glow::COLOR_BUFFER_BIT);
}
renderer
.render(&window, &glow, draw_data)
.expect("Failed to render main viewport");
surface
.swap_buffers(&context)
.expect("Failed to swap buffers");
renderer
.render_viewports(&glow, &mut imgui)
.expect("Failed to render viewports");
}
_ => {}
}
});
}

View File

@ -0,0 +1,13 @@
#version 330
in vec2 v2f_UV;
in vec4 v2f_Color;
uniform sampler2D u_FontTexture;
layout(location = 0) out vec4 out_Color;
void main() {
vec4 tex = texture(u_FontTexture, v2f_UV);
out_Color = v2f_Color * tex;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,16 @@
#version 330
layout(location = 0) in vec2 in_Position;
layout(location = 1) in vec2 in_UV;
layout(location = 2) in vec4 in_Color;
uniform mat4 u_Matrix;
out vec2 v2f_UV;
out vec4 v2f_Color;
void main() {
gl_Position = u_Matrix * vec4(in_Position, 0.0, 1.0);
v2f_UV = in_UV;
v2f_Color = in_Color;
}

View File

@ -60,6 +60,13 @@ pub struct Context {
// imgui a mutable pointer to it.
clipboard_ctx: Box<UnsafeCell<ClipboardContext>>,
// we need to store an owning reference to our PlatformViewportBackend and PlatformRendererBackend,
// so that it is ensured that PlatformIo::backend_platform_user_data and PlatformIo::backend_renderer_user_data remain valid
#[cfg(feature = "docking")]
platform_viewport_ctx: Box<UnsafeCell<crate::PlatformViewportContext>>,
#[cfg(feature = "docking")]
renderer_viewport_ctx: Box<UnsafeCell<crate::RendererViewportContext>>,
ui: Ui,
}
@ -239,6 +246,14 @@ impl Context {
platform_name: None,
renderer_name: None,
clipboard_ctx: Box::new(ClipboardContext::dummy().into()),
#[cfg(feature = "docking")]
platform_viewport_ctx: Box::new(UnsafeCell::new(
crate::PlatformViewportContext::dummy(),
)),
#[cfg(feature = "docking")]
renderer_viewport_ctx: Box::new(UnsafeCell::new(
crate::RendererViewportContext::dummy(),
)),
ui: Ui {
buffer: UnsafeCell::new(crate::string::UiBuffer::new(1024)),
},
@ -328,6 +343,14 @@ impl SuspendedContext {
platform_name: None,
renderer_name: None,
clipboard_ctx: Box::new(ClipboardContext::dummy().into()),
#[cfg(feature = "docking")]
platform_viewport_ctx: Box::new(UnsafeCell::new(
crate::PlatformViewportContext::dummy(),
)),
#[cfg(feature = "docking")]
renderer_viewport_ctx: Box::new(UnsafeCell::new(
crate::RendererViewportContext::dummy(),
)),
ui: Ui {
buffer: UnsafeCell::new(crate::string::UiBuffer::new(1024)),
},
@ -568,3 +591,151 @@ impl Context {
}
}
}
#[cfg(feature = "docking")]
impl Context {
/// Returns an immutable reference to the Context's [`PlatformIo`](crate::PlatformIo) object.
pub fn platform_io(&self) -> &crate::PlatformIo {
unsafe {
// safe because PlatformIo is a transparent wrapper around sys::ImGuiPlatformIO
// and &self ensures we have shared ownership of PlatformIo.
&*(sys::igGetPlatformIO() as *const crate::PlatformIo)
}
}
/// Returns a mutable reference to the Context's [`PlatformIo`](crate::PlatformIo) object.
pub fn platform_io_mut(&mut self) -> &mut crate::PlatformIo {
unsafe {
// safe because PlatformIo is a transparent wrapper around sys::ImGuiPlatformIO
// and &mut self ensures exclusive ownership of PlatformIo.
&mut *(sys::igGetPlatformIO() as *mut crate::PlatformIo)
}
}
/// Returns an immutable reference to the main [`Viewport`](crate::Viewport)
pub fn main_viewport(&self) -> &crate::Viewport {
unsafe {
// safe because Viewport is a transparent wrapper around sys::ImGuiViewport
// and &self ensures we have shared ownership.
&*(sys::igGetMainViewport() as *mut crate::Viewport)
}
}
/// Returns a mutable reference to the main [`Viewport`](crate::Viewport)
pub fn main_viewport_mut(&mut self) -> &mut crate::Viewport {
unsafe {
// safe because Viewport is a transparent wrapper around sys::ImGuiViewport
// and &mut self ensures we have exclusive ownership.
&mut *(sys::igGetMainViewport() as *mut crate::Viewport)
}
}
/// Tries to find and return a Viewport identified by `id`.
///
/// # Returns
/// A [`Viewport`](crate::Viewport) with the given `id`
/// or `None` if no [`Viewport`](crate::Viewport) exists.
pub fn viewport_by_id(&self, id: crate::Id) -> Option<&crate::Viewport> {
unsafe {
// safe because Viewport is a transparent wrapper around sys::ImGuiViewport
// and &self ensures shared ownership.
(sys::igFindViewportByID(id.0) as *const crate::Viewport).as_ref()
}
}
/// Tries to find and return a Viewport identified by `id`.
///
/// # Returns
/// A [`Viewport`](crate::Viewport) with the given `id`
/// or `None` if no [`Viewport`](crate::Viewport) exists.
pub fn viewport_by_id_mut(&mut self, id: crate::Id) -> Option<&mut crate::Viewport> {
unsafe {
// safe because Viewport is a transparent wrapper around sys::ImGuiViewport
// and &mut self ensures exclusive ownership.
(sys::igFindViewportByID(id.0) as *mut crate::Viewport).as_mut()
}
}
/// Returns an iterator containing every [`Viewport`](crate::Viewport) that currently exists.
pub fn viewports(&self) -> impl Iterator<Item = &crate::Viewport> {
let slice = self.platform_io().viewports.as_slice();
// safe because &self ensures shared ownership
unsafe { slice.iter().map(|ptr| &**ptr) }
}
/// Returns an iterator containing every [`Viewport`](crate::Viewport) that currently exists.
pub fn viewports_mut(&mut self) -> impl Iterator<Item = &mut crate::Viewport> {
let slice = self.platform_io_mut().viewports.as_slice();
// safe because &mut self ensures exclusive ownership
unsafe { slice.iter().map(|ptr| &mut **ptr) }
}
/// Installs a [`PlatformViewportBackend`](crate::PlatformViewportBackend) that is used to
/// create platform windows on demand if a window is dragged outside of the main viewport.
pub fn set_platform_backend<T: crate::PlatformViewportBackend>(&mut self, backend: T) {
let ctx = Box::new(UnsafeCell::new(crate::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);
// since pio.platform_get_window_pos is not a C compatible function, cimgui provides an extra function to set it.
unsafe {
crate::platform_io::ImGuiPlatformIO_Set_Platform_GetWindowPos(
pio,
crate::platform_io::platform_get_window_pos,
);
}
pio.platform_set_window_size = Some(crate::platform_io::platform_set_window_size);
// since pio.platform_get_window_size is not a C compatible function, cimgui provides an extra function to set it.
unsafe {
crate::platform_io::ImGuiPlatformIO_Set_Platform_GetWindowSize(
pio,
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;
}
/// Installs a [`RendererViewportBackend`](crate::RendererViewportBackend) that is used to
/// render extra viewports created by ImGui.
pub fn set_renderer_backend<T: crate::RendererViewportBackend>(&mut self, backend: T) {
let ctx = Box::new(UnsafeCell::new(crate::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;
}
/// Updates the extra Viewports created by ImGui.
/// Has to be called every frame if Viewports are enabled.
pub fn update_platform_windows(&mut self) {
unsafe {
sys::igUpdatePlatformWindows();
}
}
/// Basically just calls the [`PlatformViewportBackend`](crate::PlatformViewportBackend) and [`RendererViewportBackend`](crate::RendererViewportBackend)
/// functions. If you render your extra viewports manually this function is not needed at all.
pub fn render_platform_windows_default(&mut self) {
unsafe {
sys::igRenderPlatformWindowsDefault(std::ptr::null_mut(), std::ptr::null_mut());
}
}
}

15
imgui/src/dock_space.rs Normal file
View File

@ -0,0 +1,15 @@
use std::ptr::null;
use crate::Ui;
impl Ui {
pub fn dockspace_over_main_viewport(&self) {
unsafe {
sys::igDockSpaceOverViewport(
sys::igGetMainViewport(),
sys::ImGuiDockNodeFlags_PassthruCentralNode as i32,
null(),
);
}
}
}

View File

@ -1,6 +1,6 @@
//! Internal raw utilities (don't use unless you know what you're doing!)
use std::slice;
use std::{mem::size_of, slice};
/// A generic version of the raw imgui-sys ImVector struct types
#[repr(C)]
@ -15,6 +15,24 @@ impl<T> ImVector<T> {
pub fn as_slice(&self) -> &[T] {
unsafe { slice::from_raw_parts(self.data, self.size as usize) }
}
#[inline]
pub fn as_slice_mut(&mut self) -> &mut [T] {
unsafe { slice::from_raw_parts_mut(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::<T>() * 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]

View File

@ -55,6 +55,29 @@ bitflags! {
#[cfg(feature = "docking")]
const DOCKING_ENABLE = sys::ImGuiConfigFlags_DockingEnable;
#[cfg(feature = "docking")]
const VIEWPORTS_ENABLE = sys::ImGuiConfigFlags_ViewportsEnable;
}
}
#[cfg(feature = "docking")]
bitflags! {
#[repr(transparent)]
pub struct ViewportFlags: u32 {
const IS_PLATFORM_WINDOW = sys::ImGuiViewportFlags_IsPlatformWindow;
const IS_PLATFORM_MONITOR = sys::ImGuiViewportFlags_IsPlatformMonitor;
const OWNED_BY_APP = sys::ImGuiViewportFlags_OwnedByApp;
const NO_DECORATION = sys::ImGuiViewportFlags_NoDecoration;
const NO_TASK_BAR_ICON = sys::ImGuiViewportFlags_NoTaskBarIcon;
const NO_FOCUS_ON_APPEARING = sys::ImGuiViewportFlags_NoFocusOnAppearing;
const NO_FOCUS_ON_CLICK = sys::ImGuiViewportFlags_NoFocusOnClick;
const NO_INPUTS = sys::ImGuiViewportFlags_NoInputs;
const NO_RENDERER_CLEAR = sys::ImGuiViewportFlags_NoRendererClear;
const TOP_MOST = sys::ImGuiViewportFlags_TopMost;
const MINIMIZED = sys::ImGuiViewportFlags_Minimized;
const NO_AUTO_MERGE = sys::ImGuiViewportFlags_NoAutoMerge;
const CAN_HOST_OTHER_WINDOWS = sys::ImGuiViewportFlags_CanHostOtherWindows;
}
}
@ -74,6 +97,13 @@ bitflags! {
///
/// This enables output of large meshes (64K+ vertices) while still using 16-bits indices.
const RENDERER_HAS_VTX_OFFSET = sys::ImGuiBackendFlags_RendererHasVtxOffset;
#[cfg(feature = "docking")]
/// Set if the platform backend supports viewports.
const PLATFORM_HAS_VIEWPORTS = sys::ImGuiBackendFlags_PlatformHasViewports;
#[cfg(feature = "docking")]
/// Set if the renderer backend supports viewports.
const RENDERER_HAS_VIEWPORTS = sys::ImGuiBackendFlags_RendererHasViewports;
}
}
@ -234,8 +264,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<unsafe extern "C" fn(user_data: *mut c_void) -> *const c_char>,

View File

@ -10,6 +10,8 @@ use std::os::raw::c_char;
pub use self::clipboard::*;
pub use self::color::ImColor32;
pub use self::context::*;
#[cfg(feature = "docking")]
pub use self::dock_space::*;
pub use self::drag_drop::{DragDropFlags, DragDropSource, DragDropTarget};
pub use self::draw_list::{ChannelsSplit, DrawListMut};
pub use self::fonts::atlas::*;
@ -22,6 +24,8 @@ pub use self::input_widget::*;
pub use self::io::*;
pub use self::layout::*;
pub use self::list_clipper::ListClipper;
#[cfg(feature = "docking")]
pub use self::platform_io::*;
pub use self::plothistogram::PlotHistogram;
pub use self::plotlines::PlotLines;
pub use self::popups::*;
@ -62,6 +66,8 @@ mod clipboard;
pub mod color;
mod columns;
mod context;
#[cfg(feature = "docking")]
mod dock_space;
pub mod drag_drop;
pub mod draw_list;
mod fonts;
@ -72,6 +78,8 @@ mod io;
mod layout;
mod list_clipper;
mod math;
#[cfg(feature = "docking")]
mod platform_io;
mod plothistogram;
mod plotlines;
mod popups;
@ -284,7 +292,8 @@ impl Ui {
/// Previously, in v0.7, this was erroneously constructed with `From`
/// implementations. Now, however, it is made from the `Ui` object
/// directly, with a few deprecated helper methods here.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
#[repr(transparent)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default, Hash)]
pub struct Id(pub(crate) u32);
impl Id {

558
imgui/src/platform_io.rs Normal file
View File

@ -0,0 +1,558 @@
use std::{
ffi::{c_void, CStr},
os::raw::{c_char, c_int},
};
use crate::{
internal::{ImVector, RawCast},
Io, ViewportFlags,
};
/// Holds the information needed to enable multiple viewports.
#[repr(C)]
pub struct PlatformIo {
pub(crate) platform_create_window: Option<unsafe extern "C" fn(*mut Viewport)>,
pub(crate) platform_destroy_window: Option<unsafe extern "C" fn(*mut Viewport)>,
pub(crate) platform_show_window: Option<unsafe extern "C" fn(*mut Viewport)>,
pub(crate) platform_set_window_pos: Option<unsafe extern "C" fn(*mut Viewport, sys::ImVec2)>,
pub(crate) platform_get_window_pos: Option<unsafe extern "C" fn(*mut Viewport) -> sys::ImVec2>,
pub(crate) platform_set_window_size: Option<unsafe extern "C" fn(*mut Viewport, sys::ImVec2)>,
pub(crate) platform_get_window_size: Option<unsafe extern "C" fn(*mut Viewport) -> sys::ImVec2>,
pub(crate) platform_set_window_focus: Option<unsafe extern "C" fn(*mut Viewport)>,
pub(crate) platform_get_window_focus: Option<unsafe extern "C" fn(*mut Viewport) -> bool>,
pub(crate) platform_get_window_minimized: Option<unsafe extern "C" fn(*mut Viewport) -> bool>,
pub(crate) platform_set_window_title:
Option<unsafe extern "C" fn(*mut Viewport, *const c_char)>,
pub(crate) platform_set_window_alpha: Option<unsafe extern "C" fn(*mut Viewport, f32)>,
pub(crate) platform_update_window: Option<unsafe extern "C" fn(*mut Viewport)>,
pub(crate) platform_render_window: Option<unsafe extern "C" fn(*mut Viewport, *mut c_void)>,
pub(crate) platform_swap_buffers: Option<unsafe extern "C" fn(*mut Viewport, *mut c_void)>,
pub(crate) platform_get_window_dpi_scale: Option<unsafe extern "C" fn(*mut Viewport) -> f32>,
pub(crate) platform_on_changed_viewport: Option<unsafe extern "C" fn(*mut Viewport)>,
pub(crate) platform_create_vk_surface:
Option<unsafe extern "C" fn(*mut Viewport, u64, *const c_void, *mut u64) -> c_int>,
pub(crate) renderer_create_window: Option<unsafe extern "C" fn(*mut Viewport)>,
pub(crate) renderer_destroy_window: Option<unsafe extern "C" fn(*mut Viewport)>,
pub(crate) renderer_set_window_size: Option<unsafe extern "C" fn(*mut Viewport, sys::ImVec2)>,
pub(crate) renderer_render_window: Option<unsafe extern "C" fn(*mut Viewport, *mut c_void)>,
pub(crate) renderer_swap_buffers: Option<unsafe extern "C" fn(*mut Viewport, *mut c_void)>,
/// Holds information about the available monitors.
/// Should be initialized and updated by the [`PlatformViewportBackend`].
pub monitors: ImVector<PlatformMonitor>,
pub(crate) viewports: ImVector<*mut Viewport>,
}
unsafe impl RawCast<sys::ImGuiPlatformIO> for PlatformIo {}
#[test]
#[cfg(test)]
fn test_platform_io_memory_layout() {
use std::mem;
assert_eq!(
mem::size_of::<PlatformIo>(),
mem::size_of::<sys::ImGuiPlatformIO>()
);
assert_eq!(
mem::align_of::<PlatformIo>(),
mem::align_of::<sys::ImGuiPlatformIO>()
);
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_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);
}
/// Trait holding functions needed when the platform integration supports viewports.
///
/// Register it via [`Context::set_platform_backend()`](crate::context::Context::set_platform_backend())
pub trait PlatformViewportBackend: 'static {
/// Called by imgui when a new [`Viewport`] is created.
///
/// # Notes
/// This function should initiate the creation of a platform window.
/// The window should be invisible.
fn create_window(&mut self, viewport: &mut Viewport);
/// Called by imgui when a [`Viewport`] is about to be destroyed.
///
/// # Notes
/// This function should initiate the destruction of the platform window.
fn destroy_window(&mut self, viewport: &mut Viewport);
/// Called by imgui to make a [`Viewport`] visible.
fn show_window(&mut self, viewport: &mut Viewport);
/// Called by imgui to reposition a [`Viewport`].
///
/// # Notes
/// `pos` specifies the position of the windows content area (excluding title bar etc.)
fn set_window_pos(&mut self, viewport: &mut Viewport, pos: [f32; 2]);
/// Called by imgui to get the position of a [`Viewport`].
///
/// # Notes
/// You should return the position of the window's content area (excluding title bar etc.)
fn get_window_pos(&mut self, viewport: &mut Viewport) -> [f32; 2];
/// Called by imgui to set the size of a [`Viewport`].
///
/// # Notes
/// `size` specifies the size of the window's content area (excluding title bar etc.)
fn set_window_size(&mut self, viewport: &mut Viewport, size: [f32; 2]);
/// Called by imgui to get the size of a [`Viewport`].
///
/// # Notes
/// you should return the size of the window's content area (excluding title bar etc.)
fn get_window_size(&mut self, viewport: &mut Viewport) -> [f32; 2];
/// Called by imgui to make a [`Viewport`] steal the focus.
fn set_window_focus(&mut self, viewport: &mut Viewport);
/// Called by imgui to query whether a [`Viewport`] is in focus.
fn get_window_focus(&mut self, viewport: &mut Viewport) -> bool;
/// Called by imgui to query whether a [`Viewport`] is minimized.
fn get_window_minimized(&mut self, viewport: &mut Viewport) -> bool;
/// Called by imgui to set a [`Viewport`] title.
fn set_window_title(&mut self, viewport: &mut Viewport, title: &str);
/// Called by imgui to set the opacity of an entire [`Viewport`].
///
/// If your backend does not support opactiy, it is safe to just do nothing in this function.
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;
}
/// Used to get the current Contexts [`PlatformViewportContext`].
fn get_platform_ctx() -> &'static mut PlatformViewportContext {
unsafe {
// should be safe as it is impossible to call any imgui function on a non-active context.
&mut *((*(sys::igGetIO() as *const Io)).backend_platform_user_data
as *mut PlatformViewportContext)
}
}
/// Used to get the current Contexts [`RendererViewportContext`].
fn get_renderer_ctx() -> &'static mut RendererViewportContext {
unsafe {
// should be safe as it is impossible to call any imgui function on a non-active context.
&mut *((*(sys::igGetIO() as *const Io)).backend_renderer_user_data
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,
out_pos: *mut sys::ImVec2,
) {
let ctx = get_platform_ctx();
let pos = ctx.backend.get_window_pos(unsafe { &mut *viewport });
unsafe {
*out_pos = 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,
out_size: *mut sys::ImVec2,
) {
let ctx = get_platform_ctx();
let size = ctx.backend.get_window_size(unsafe { &mut *viewport });
unsafe {
*out_size = 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
})
}
/// The default [`PlatformViewportBackend`], does nothing.
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!()
}
}
/// Just holds a [`PlatformViewportBackend`].
pub(crate) struct PlatformViewportContext {
pub(crate) backend: Box<dyn PlatformViewportBackend>,
}
impl PlatformViewportContext {
pub(crate) fn dummy() -> Self {
Self {
backend: Box::new(DummyPlatformViewportBackend {}),
}
}
}
/// Trait that holds optional functions for a rendering backend to support multiple viewports.
///
/// It is completely fine to not use this Backend at all, as all functions are optional.
pub trait RendererViewportBackend: 'static {
/// Called after [`PlatformViewportBackend::create_window()`].
fn create_window(&mut self, viewport: &mut Viewport);
/// Called before [`PlatformViewportBackend::destroy_window()`].
fn destroy_window(&mut self, viewport: &mut Viewport);
/// Called after [`PlatformViewportBackend::set_window_size()`].
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 });
}
/// The default [`RendererViewportBackend`], does nothing.
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!()
}
}
/// Just holds a [`RendererViewportBackend`].
pub(crate) struct RendererViewportContext {
pub(crate) backend: Box<dyn RendererViewportBackend>,
}
impl RendererViewportContext {
pub(crate) fn dummy() -> Self {
Self {
backend: Box::new(DummyRendererViewportBackend {}),
}
}
}
/// Describes an ImGui Viewport.
#[repr(C)]
pub struct Viewport {
/// The unique ID of this Viewport.
pub id: crate::Id,
/// Flags that describe how the Viewport should behave.
pub flags: ViewportFlags,
pub pos: [f32; 2],
pub size: [f32; 2],
pub work_pos: [f32; 2],
pub work_size: [f32; 2],
pub 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_window_created: bool,
pub platform_request_move: bool,
pub platform_request_resize: bool,
pub platform_request_close: bool,
}
impl Viewport {
/// Returns the draw data of the respective Viewport.
pub fn draw_data(&self) -> &crate::DrawData {
unsafe { &*self.draw_data }
}
}
#[test]
#[cfg(test)]
fn test_viewport_memory_layout() {
use std::mem;
assert_eq!(
mem::size_of::<Viewport>(),
mem::size_of::<sys::ImGuiViewport>()
);
assert_eq!(
mem::align_of::<Viewport>(),
mem::align_of::<sys::ImGuiViewport>()
);
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_window_created, PlatformWindowCreated);
assert_field_offset!(platform_request_move, PlatformRequestMove);
assert_field_offset!(platform_request_resize, PlatformRequestResize);
assert_field_offset!(platform_request_close, PlatformRequestClose);
}
/// Describes a monitor that can be used by ImGui.
#[repr(C)]
pub struct PlatformMonitor {
/// Position of the monitor on the virtual desktop.
pub main_pos: [f32; 2],
/// Size of the monitor on the virtual desktop.
pub main_size: [f32; 2],
/// Working position of the monitor, should exclude task bar etc.
///
/// Set to `main_pos` if not known.
pub work_pos: [f32; 2],
/// Working size of the monitor, should exclude task bar etc.
///
/// Set to `work_size` if not known.
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::<PlatformMonitor>(),
mem::size_of::<sys::ImGuiPlatformMonitor>()
);
assert_eq!(
mem::align_of::<PlatformMonitor>(),
mem::align_of::<sys::ImGuiPlatformMonitor>()
);
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);
}
extern "C" {
pub(crate) fn ImGuiPlatformIO_Set_Platform_GetWindowPos(
pio: *mut PlatformIo,
func: extern "C" fn(*mut Viewport, *mut sys::ImVec2),
);
pub(crate) fn ImGuiPlatformIO_Set_Platform_GetWindowSize(
pio: *mut PlatformIo,
func: extern "C" fn(*mut Viewport, *mut sys::ImVec2),
);
}