#![allow(unsafe_op_in_unsafe_fn)]
use std::cell::RefCell;
use std::ffi::{CStr, c_char, c_void};
use dear_imgui_rs::Context;
use winit::dpi::{LogicalPosition, LogicalSize};
use winit::event::{Event, WindowEvent};
use winit::event_loop::ActiveEventLoop;
use winit::window::{Window, WindowAttributes, WindowLevel};
thread_local! {
static EVENT_LOOP: RefCell<Option<*const ActiveEventLoop>> = const { RefCell::new(None) };
static VIEWPORT_DATA: RefCell<Vec<(*mut dear_imgui_rs::sys::ImGuiContext, *mut ViewportData)>> = const { RefCell::new(Vec::new()) };
}
#[allow(unused_macros)]
macro_rules! mvlog {
($($arg:tt)*) => {
if cfg!(feature = "mv-log") { eprintln!($($arg)*); }
}
}
fn abort_on_panic<R>(name: &str, f: impl FnOnce() -> R) -> R {
match std::panic::catch_unwind(std::panic::AssertUnwindSafe(f)) {
Ok(v) => v,
Err(_) => {
eprintln!("dear-imgui-winit: panic in {}", name);
std::process::abort();
}
}
}
#[repr(C)]
struct ViewportData {
pub window: *mut Window, pub window_owned: bool, pub ignore_window_pos_event_frame: i32,
pub ignore_window_size_event_frame: i32,
pub last_log_fb_scale: f32,
}
impl Default for ViewportData {
fn default() -> Self {
Self::new()
}
}
impl ViewportData {
pub fn new() -> Self {
Self {
window: std::ptr::null_mut(),
window_owned: false,
ignore_window_pos_event_frame: -1,
ignore_window_size_event_frame: -1,
last_log_fb_scale: 0.0,
}
}
}
pub(crate) fn client_to_screen_pos(window: &Window, logical: [f64; 2]) -> Option<[f32; 2]> {
let scale = window.scale_factor();
let base = window
.inner_position()
.ok()
.map(|p| p.to_logical::<f64>(scale))
.or_else(|| {
window
.outer_position()
.ok()
.map(|p| p.to_logical::<f64>(scale))
});
if let Some(base) = base {
Some([(base.x + logical[0]) as f32, (base.y + logical[1]) as f32])
} else {
Some([logical[0] as f32, logical[1] as f32])
}
}
fn decoration_offset_logical(window: &Window) -> Option<(f64, f64)> {
let scale = window.scale_factor();
let inner_phys = window.inner_position().ok()?;
let outer_phys = window.outer_position().ok()?;
let inner_log = inner_phys.to_logical::<f64>(scale);
let outer_log = outer_phys.to_logical::<f64>(scale);
Some((inner_log.x - outer_log.x, inner_log.y - outer_log.y))
}
pub fn init_multi_viewport_support(ctx: &mut Context, main_window: &Window) {
let _context_guard = unsafe { CurrentContextGuard::bind(ctx.as_raw()) };
install_platform_callbacks(ctx);
init_main_viewport(ctx, main_window);
unsafe {
setup_monitors_with_window(main_window, ctx);
}
}
fn install_platform_callbacks(ctx: &mut Context) {
let _context_guard = unsafe { CurrentContextGuard::bind(ctx.as_raw()) };
unsafe {
let pio = ctx.platform_io_mut();
let pio_sys = pio.as_raw_mut();
pio.set_platform_create_window_raw(Some(winit_create_window));
pio.set_platform_destroy_window_raw(Some(winit_destroy_window));
pio.set_platform_show_window_raw(Some(winit_show_window));
pio.set_platform_set_window_pos_raw(Some(winit_set_window_pos));
pio.set_platform_get_window_pos_raw(Some(winit_get_window_pos_out));
pio.set_platform_set_window_size_raw(Some(winit_set_window_size));
pio.set_platform_get_window_size_raw(Some(winit_get_window_size_out));
pio.set_platform_set_window_focus_raw(Some(winit_set_window_focus));
pio.set_platform_get_window_focus_raw(Some(winit_get_window_focus));
pio.set_platform_get_window_minimized_raw(Some(winit_get_window_minimized));
pio.set_platform_set_window_title_raw(Some(winit_set_window_title));
pio.set_platform_update_window_raw(Some(winit_update_window));
pio.set_platform_get_window_framebuffer_scale_raw(Some(
winit_get_window_framebuffer_scale_out,
));
(*pio_sys).Platform_GetWindowDpiScale = Some(winit_get_window_dpi_scale);
pio.set_platform_get_window_work_area_insets_raw(None);
(*pio_sys).Platform_OnChangedViewport = Some(winit_on_changed_viewport);
(*pio_sys).Platform_SetWindowAlpha = Some(winit_set_window_alpha);
(*pio_sys).Platform_RenderWindow = Some(winit_platform_render_window);
(*pio_sys).Platform_SwapBuffers = Some(winit_platform_swap_buffers);
(*pio_sys).Platform_CreateVkSurface = Some(winit_platform_create_vk_surface);
macro_rules! chk {
($name:expr, $ptr:expr) => {};
}
chk!("CreateWindow", (*pio_sys).Platform_CreateWindow);
chk!("DestroyWindow", (*pio_sys).Platform_DestroyWindow);
chk!("ShowWindow", (*pio_sys).Platform_ShowWindow);
chk!("SetWindowPos", (*pio_sys).Platform_SetWindowPos);
chk!("GetWindowPos", (*pio_sys).Platform_GetWindowPos);
chk!("SetWindowSize", (*pio_sys).Platform_SetWindowSize);
chk!("GetWindowSize", (*pio_sys).Platform_GetWindowSize);
chk!(
"GetWindowFramebufferScale",
(*pio_sys).Platform_GetWindowFramebufferScale
);
chk!("SetWindowFocus", (*pio_sys).Platform_SetWindowFocus);
chk!("GetWindowFocus", (*pio_sys).Platform_GetWindowFocus);
chk!("GetWindowMinimized", (*pio_sys).Platform_GetWindowMinimized);
chk!("SetWindowTitle", (*pio_sys).Platform_SetWindowTitle);
chk!("SetWindowAlpha", (*pio_sys).Platform_SetWindowAlpha);
chk!("UpdateWindow", (*pio_sys).Platform_UpdateWindow);
chk!("RenderWindow", (*pio_sys).Platform_RenderWindow);
chk!("SwapBuffers", (*pio_sys).Platform_SwapBuffers);
chk!("GetWindowDpiScale", (*pio_sys).Platform_GetWindowDpiScale);
chk!("OnChangedViewport", (*pio_sys).Platform_OnChangedViewport);
chk!(
"GetWindowWorkAreaInsets",
(*pio_sys).Platform_GetWindowWorkAreaInsets
);
chk!("CreateVkSurface", (*pio_sys).Platform_CreateVkSurface);
}
}
unsafe fn setup_monitors_with_window(window: &Window, ctx: &mut Context) {
let monitors: Vec<dear_imgui_rs::sys::ImGuiPlatformMonitor> = {
let mut out = Vec::new();
let mut iter = window.available_monitors();
while let Some(m) = iter.next() {
let scale_f64 = m.scale_factor();
let scale = scale_f64 as f32;
let pos_logical = m.position().to_logical::<f64>(scale_f64);
let size_logical = m.size().to_logical::<f64>(scale_f64);
let mut monitor = dear_imgui_rs::sys::ImGuiPlatformMonitor::default();
monitor.MainPos = dear_imgui_rs::sys::ImVec2 {
x: pos_logical.x as f32,
y: pos_logical.y as f32,
};
monitor.MainSize = dear_imgui_rs::sys::ImVec2 {
x: size_logical.width as f32,
y: size_logical.height as f32,
};
monitor.WorkPos = monitor.MainPos;
monitor.WorkSize = monitor.MainSize;
monitor.DpiScale = scale;
monitor.PlatformHandle = std::ptr::null_mut();
out.push(monitor);
}
if out.is_empty() {
let scale_f64 = window.scale_factor();
let scale = scale_f64 as f32;
let size_logical = window.inner_size().to_logical::<f64>(scale_f64);
let mut monitor = dear_imgui_rs::sys::ImGuiPlatformMonitor::default();
monitor.MainPos = dear_imgui_rs::sys::ImVec2 { x: 0.0, y: 0.0 };
monitor.MainSize = dear_imgui_rs::sys::ImVec2 {
x: size_logical.width as f32,
y: size_logical.height as f32,
};
monitor.WorkPos = monitor.MainPos;
monitor.WorkSize = monitor.MainSize;
monitor.DpiScale = scale;
out.push(monitor);
}
out
};
let pio = ctx.platform_io_mut().as_raw_mut();
let vec = unsafe { &mut (*pio).Monitors };
if vec.Capacity > 0 && !vec.Data.is_null() {
dear_imgui_rs::sys::igMemFree(vec.Data as *mut _);
vec.Data = std::ptr::null_mut();
vec.Size = 0;
vec.Capacity = 0;
}
let count = monitors.len();
let bytes = count * std::mem::size_of::<dear_imgui_rs::sys::ImGuiPlatformMonitor>();
let data_ptr = if bytes > 0 {
dear_imgui_rs::sys::igMemAlloc(bytes) as *mut dear_imgui_rs::sys::ImGuiPlatformMonitor
} else {
std::ptr::null_mut()
};
if !data_ptr.is_null() {
for (i, m) in monitors.iter().enumerate() {
*data_ptr.add(i) = *m;
}
vec.Data = data_ptr;
vec.Size = count as i32;
vec.Capacity = count as i32;
}
}
pub fn route_event_to_viewports<T>(imgui_ctx: &mut Context, event: &Event<T>) -> bool {
let _context_guard = unsafe { CurrentContextGuard::bind(imgui_ctx.as_raw()) };
match event {
Event::WindowEvent { window_id, event } => {
#[cfg(feature = "multi-viewport")]
{
unsafe {
let pio = dear_imgui_rs::sys::igGetPlatformIO_ContextPtr(imgui_ctx.as_raw());
let viewports = &(*pio).Viewports;
if viewports.Data.is_null() || viewports.Size <= 0 {
return false;
}
for i in 0..viewports.Size {
let vp = *viewports.Data.add(i as usize);
if vp.is_null() {
continue;
}
if let Some(vd) = viewport_data_ref(vp) {
if !vd.window_owned {
continue;
}
if let Some(window) = vd.window.as_ref() {
if &window.id() == window_id {
match event {
WindowEvent::Moved(_) => {
let cur = dear_imgui_rs::sys::igGetFrameCount();
if vd.ignore_window_pos_event_frame != cur {
(*vp).PlatformRequestMove = true;
}
}
WindowEvent::Resized(_) => {
let cur = dear_imgui_rs::sys::igGetFrameCount();
if vd.ignore_window_size_event_frame != cur {
(*vp).PlatformRequestResize = true;
}
}
WindowEvent::ScaleFactorChanged { .. } => {
let scale = window.scale_factor() as f32;
if scale.is_finite() && scale > 0.0 {
(*vp).DpiScale = scale;
(*vp).FramebufferScale.x = scale;
(*vp).FramebufferScale.y = scale;
}
}
WindowEvent::CloseRequested => {
(*vp).PlatformRequestClose = true;
}
_ => {}
}
match event {
WindowEvent::KeyboardInput { event, .. } => {
return crate::events::handle_keyboard_input(
event, imgui_ctx,
);
}
WindowEvent::ModifiersChanged(mods) => {
crate::events::handle_modifiers_changed(
mods, imgui_ctx,
);
return imgui_ctx.io().want_capture_keyboard();
}
WindowEvent::MouseWheel { delta, .. } => {
return crate::events::handle_mouse_wheel(
*delta, imgui_ctx,
);
}
WindowEvent::MouseInput { state, button, .. } => {
return crate::events::handle_mouse_button(
*button, *state, imgui_ctx,
);
}
WindowEvent::CursorMoved { position, .. } => {
imgui_ctx.io_mut().add_mouse_viewport_event(
dear_imgui_rs::Id::from((*vp).ID),
);
let pos_logical =
position.to_logical(window.scale_factor());
let logical = [pos_logical.x, pos_logical.y];
if let Some(screen) =
client_to_screen_pos(window, logical)
{
return crate::events::handle_cursor_moved(
[screen[0] as f64, screen[1] as f64],
imgui_ctx,
);
} else {
let pos =
position.to_logical(window.scale_factor());
return crate::events::handle_cursor_moved(
[pos.x, pos.y],
imgui_ctx,
);
}
}
WindowEvent::CursorLeft { .. } => {
{
let io = imgui_ctx.io_mut();
io.add_mouse_pos_event([-f32::MAX, -f32::MAX]);
io.add_mouse_viewport_event(
dear_imgui_rs::Id::default(),
);
}
return false;
}
WindowEvent::Focused(focused) => {
return crate::events::handle_focused(
*focused, imgui_ctx,
);
}
WindowEvent::Ime(ime) => {
crate::events::handle_ime_event(ime, imgui_ctx);
return imgui_ctx.io().want_capture_keyboard();
}
_ => {}
}
}
}
}
}
}
}
false
}
_ => false,
}
}
#[cfg(feature = "multi-viewport")]
pub fn handle_event_with_multi_viewport<T>(
platform: &mut crate::platform::WinitPlatform,
imgui_ctx: &mut Context,
main_window: &Window,
event: &Event<T>,
) -> bool {
let mut consumed = false;
if let Event::WindowEvent { window_id, .. } = event {
if *window_id == main_window.id() {
if platform.handle_event(imgui_ctx, main_window, event) {
consumed = true;
}
}
}
if route_event_to_viewports(imgui_ctx, event) {
consumed = true;
}
consumed
}
fn init_main_viewport(ctx: &mut Context, main_window: &Window) {
let _context_guard = unsafe { CurrentContextGuard::bind(ctx.as_raw()) };
unsafe {
let main_viewport = dear_imgui_rs::sys::igGetMainViewport();
if main_viewport.is_null() {
return;
}
let existing = (*main_viewport).PlatformUserData as *mut ViewportData;
let vd = if existing.is_null() {
let vd = Box::into_raw(Box::new(ViewportData::new()));
register_viewport_data(vd);
vd
} else if is_winit_viewport_data(existing) {
existing
} else {
panic!("main viewport PlatformUserData is already owned by another platform backend");
};
(*vd).window = main_window as *const Window as *mut Window;
(*vd).window_owned = false;
(*main_viewport).PlatformUserData = vd as *mut c_void;
(*main_viewport).PlatformHandle = main_window as *const Window as *mut c_void;
}
}
struct CurrentContextGuard {
previous: *mut dear_imgui_rs::sys::ImGuiContext,
target: *mut dear_imgui_rs::sys::ImGuiContext,
}
impl CurrentContextGuard {
unsafe fn bind(target: *mut dear_imgui_rs::sys::ImGuiContext) -> Self {
let previous = unsafe { dear_imgui_rs::sys::igGetCurrentContext() };
if previous != target {
unsafe { dear_imgui_rs::sys::igSetCurrentContext(target) };
}
Self { previous, target }
}
}
impl Drop for CurrentContextGuard {
fn drop(&mut self) {
if self.previous != self.target {
unsafe { dear_imgui_rs::sys::igSetCurrentContext(self.previous) };
}
}
}
unsafe fn clear_main_viewport_data_for_current_context() {
unsafe {
let main_viewport = dear_imgui_rs::sys::igGetMainViewport();
if !main_viewport.is_null() {
let vp = &mut *main_viewport;
let vd_ptr = vp.PlatformUserData as *mut ViewportData;
let owned_by_winit = is_winit_viewport_data(vd_ptr);
if owned_by_winit {
(*vd_ptr).window = std::ptr::null_mut();
drop_viewport_data(vd_ptr);
vp.PlatformUserData = std::ptr::null_mut();
}
if vd_ptr.is_null() || owned_by_winit {
vp.PlatformHandle = std::ptr::null_mut();
}
}
}
}
pub fn shutdown_multi_viewport_support(ctx: &mut Context) {
unsafe {
let _context_guard = CurrentContextGuard::bind(ctx.as_raw());
clear_main_viewport_data_for_current_context();
ctx.destroy_platform_windows();
clear_main_viewport_data_for_current_context();
ctx.platform_io_mut().clear_platform_handlers();
}
}
fn register_viewport_data(ptr: *mut ViewportData) {
if ptr.is_null() {
return;
}
let ctx = unsafe { dear_imgui_rs::sys::igGetCurrentContext() };
VIEWPORT_DATA.with(|items| {
let mut items = items.borrow_mut();
if !items
.iter()
.any(|(entry_ctx, entry_ptr)| *entry_ctx == ctx && *entry_ptr == ptr)
{
items.push((ctx, ptr));
}
});
}
fn unregister_viewport_data(ptr: *mut ViewportData) {
if ptr.is_null() {
return;
}
VIEWPORT_DATA.with(|items| {
items
.borrow_mut()
.retain(|(_, entry_ptr)| *entry_ptr != ptr);
});
}
fn is_winit_viewport_data(ptr: *mut ViewportData) -> bool {
if ptr.is_null() {
return false;
}
let ctx = unsafe { dear_imgui_rs::sys::igGetCurrentContext() };
VIEWPORT_DATA.with(|items| {
items
.borrow()
.iter()
.any(|(entry_ctx, entry_ptr)| *entry_ctx == ctx && *entry_ptr == ptr)
})
}
unsafe fn viewport_data_ref<'a>(
vp: *const dear_imgui_rs::sys::ImGuiViewport,
) -> Option<&'a ViewportData> {
if vp.is_null() {
return None;
}
let ptr = unsafe { (*vp).PlatformUserData as *mut ViewportData };
if is_winit_viewport_data(ptr) {
unsafe { ptr.as_ref() }
} else {
None
}
}
unsafe fn viewport_data_mut<'a>(
vp: *mut dear_imgui_rs::sys::ImGuiViewport,
) -> Option<&'a mut ViewportData> {
if vp.is_null() {
return None;
}
let ptr = unsafe { (*vp).PlatformUserData as *mut ViewportData };
if is_winit_viewport_data(ptr) {
unsafe { ptr.as_mut() }
} else {
None
}
}
unsafe fn drop_viewport_data(ptr: *mut ViewportData) {
if !is_winit_viewport_data(ptr) {
return;
}
unregister_viewport_data(ptr);
unsafe {
let _ = Box::from_raw(ptr);
}
}
pub fn set_event_loop(event_loop: &ActiveEventLoop) {
EVENT_LOOP.with(|el| {
*el.borrow_mut() = Some(event_loop as *const ActiveEventLoop);
});
}
pub fn clear_event_loop() {
EVENT_LOOP.with(|el| {
*el.borrow_mut() = None;
});
}
pub struct EventLoopFrameGuard;
impl Drop for EventLoopFrameGuard {
fn drop(&mut self) {
clear_event_loop();
}
}
pub fn set_event_loop_for_frame(event_loop: &ActiveEventLoop) -> EventLoopFrameGuard {
set_event_loop(event_loop);
EventLoopFrameGuard
}
unsafe extern "C" fn winit_create_window(vp: *mut dear_imgui_rs::sys::ImGuiViewport) {
abort_on_panic("Platform_CreateWindow", || {
if vp.is_null() {
return;
}
let event_loop = EVENT_LOOP.with(|el| el.borrow().map(|ptr| unsafe { &*ptr }));
let event_loop = match event_loop {
Some(el) => el,
None => return,
};
let vp_ref = unsafe { &mut *vp };
let existing = vp_ref.PlatformUserData as *mut ViewportData;
if is_winit_viewport_data(existing) {
return;
}
if !existing.is_null() {
panic!("viewport PlatformUserData is already owned by another platform backend");
}
let vd = Box::into_raw(Box::new(ViewportData::new()));
register_viewport_data(vd);
vp_ref.PlatformUserData = vd as *mut c_void;
let viewport_flags = vp_ref.Flags;
let mut pos_x = vp_ref.Pos.x as f64;
let mut pos_y = vp_ref.Pos.y as f64;
if !pos_x.is_finite() {
pos_x = 0.0;
}
if !pos_y.is_finite() {
pos_y = 0.0;
}
let mut size_x = vp_ref.Size.x as f64;
let mut size_y = vp_ref.Size.y as f64;
if !size_x.is_finite() || size_x <= 0.0 {
size_x = 128.0;
}
if !size_y.is_finite() || size_y <= 0.0 {
size_y = 128.0;
}
let pos_logical = LogicalPosition::new(pos_x, pos_y);
let size_logical = LogicalSize::new(size_x, size_y);
let mut window_attrs = WindowAttributes::default()
.with_title("ImGui Viewport")
.with_inner_size(size_logical)
.with_position(pos_logical)
.with_visible(false);
if viewport_flags & (dear_imgui_rs::sys::ImGuiViewportFlags_NoDecoration as i32) != 0 {
window_attrs = window_attrs.with_decorations(false);
}
if viewport_flags & (dear_imgui_rs::sys::ImGuiViewportFlags_TopMost as i32) != 0 {
window_attrs = window_attrs.with_window_level(WindowLevel::AlwaysOnTop);
}
match event_loop.create_window(window_attrs) {
Ok(window) => {
mvlog!(
"[winit-mv] Platform_CreateWindow id={} size=({}, {})",
vp_ref.ID,
vp_ref.Size.x,
vp_ref.Size.y
);
let cur_frame = unsafe { dear_imgui_rs::sys::igGetFrameCount() };
let outer_target = if let Some((dx, dy)) = decoration_offset_logical(&window) {
LogicalPosition::new(pos_logical.x - dx, pos_logical.y - dy)
} else {
pos_logical
};
window.set_outer_position(winit::dpi::Position::Logical(outer_target));
let window_ptr = Box::into_raw(Box::new(window));
unsafe {
(*vd).window = window_ptr;
(*vd).window_owned = true;
(*vd).ignore_window_pos_event_frame = cur_frame;
(*vd).ignore_window_size_event_frame = cur_frame;
}
vp_ref.PlatformHandle = window_ptr as *mut c_void;
let window_ref: &Window = unsafe { &*window_ptr };
let scale = window_ref.scale_factor() as f32;
vp_ref.DpiScale = scale;
vp_ref.FramebufferScale.x = scale;
vp_ref.FramebufferScale.y = scale;
}
Err(_) => {
unsafe {
drop_viewport_data(vd);
}
vp_ref.PlatformUserData = std::ptr::null_mut();
}
}
});
}
unsafe extern "C" fn winit_destroy_window(vp: *mut dear_imgui_rs::sys::ImGuiViewport) {
abort_on_panic("Platform_DestroyWindow", || {
if vp.is_null() {
return;
}
let vp_ref = unsafe { &mut *vp };
let vd_ptr = vp_ref.PlatformUserData as *mut ViewportData;
if vd_ptr.is_null() {
vp_ref.PlatformUserData = std::ptr::null_mut();
vp_ref.PlatformHandle = std::ptr::null_mut();
return;
}
if !is_winit_viewport_data(vd_ptr) {
return;
}
unsafe {
if (*vd_ptr).window_owned && !(*vd_ptr).window.is_null() {
let _ = Box::from_raw((*vd_ptr).window);
}
(*vd_ptr).window = std::ptr::null_mut();
drop_viewport_data(vd_ptr);
}
vp_ref.PlatformUserData = std::ptr::null_mut();
vp_ref.PlatformHandle = std::ptr::null_mut();
});
}
unsafe extern "C" fn winit_show_window(vp: *mut dear_imgui_rs::sys::ImGuiViewport) {
abort_on_panic("Platform_ShowWindow", || {
if vp.is_null() {
return;
}
let vp_ref = unsafe { &*vp };
if let Some(vd) = unsafe { viewport_data_ref(vp_ref) } {
if let Some(window) = unsafe { vd.window.as_ref() } {
window.set_visible(true);
}
}
});
}
unsafe extern "C" fn winit_get_window_pos_out(
vp: *mut dear_imgui_rs::sys::ImGuiViewport,
out_pos: *mut dear_imgui_rs::sys::ImVec2,
) {
abort_on_panic("winit_get_window_pos_out", || {
let mut r = dear_imgui_rs::sys::ImVec2 { x: 0.0, y: 0.0 };
if !vp.is_null() {
let vp_ref = &*vp;
if let Some(vd) = viewport_data_ref(vp) {
if let Some(window) = vd.window.as_ref() {
let scale = window.scale_factor();
if let Ok(pos_phys) = window.inner_position() {
let pos_logical = pos_phys.to_logical::<f64>(scale);
r.x = pos_logical.x as f32;
r.y = pos_logical.y as f32;
} else if let Ok(pos_phys) = window.outer_position() {
let pos_logical = pos_phys.to_logical::<f64>(scale);
r.x = pos_logical.x as f32;
r.y = pos_logical.y as f32;
} else {
r.x = vp_ref.Pos.x;
r.y = vp_ref.Pos.y;
}
} else {
r.x = vp_ref.Pos.x;
r.y = vp_ref.Pos.y;
}
} else {
r.x = vp_ref.Pos.x;
r.y = vp_ref.Pos.y;
}
}
if !out_pos.is_null() {
*out_pos = r;
}
});
}
unsafe extern "C" fn winit_set_window_pos(
vp: *mut dear_imgui_rs::sys::ImGuiViewport,
pos: dear_imgui_rs::sys::ImVec2,
) {
abort_on_panic("winit_set_window_pos", || {
if vp.is_null() {
return;
}
if let Some(vd) = unsafe { viewport_data_mut(vp) } {
if let Some(window) = unsafe { vd.window.as_ref() } {
let desired_client = LogicalPosition::new(pos.x as f64, pos.y as f64);
let outer_target = if let Some((dx, dy)) = decoration_offset_logical(window) {
LogicalPosition::new(desired_client.x - dx, desired_client.y - dy)
} else {
desired_client
};
window.set_outer_position(winit::dpi::Position::Logical(outer_target));
vd.ignore_window_pos_event_frame = unsafe { dear_imgui_rs::sys::igGetFrameCount() };
}
}
});
}
unsafe extern "C" fn winit_get_window_size_out(
vp: *mut dear_imgui_rs::sys::ImGuiViewport,
out_size: *mut dear_imgui_rs::sys::ImVec2,
) {
abort_on_panic("winit_get_window_size_out", || {
let mut r = dear_imgui_rs::sys::ImVec2 { x: 0.0, y: 0.0 };
if !vp.is_null() {
let vp_ref = &*vp;
if let Some(vd) = viewport_data_ref(vp) {
if let Some(window) = vd.window.as_ref() {
let size_phys = window.inner_size();
let size_logical: LogicalSize<f64> =
size_phys.to_logical(window.scale_factor());
r.x = size_logical.width as f32;
r.y = size_logical.height as f32;
} else {
r.x = vp_ref.Size.x;
r.y = vp_ref.Size.y;
}
} else {
r.x = vp_ref.Size.x;
r.y = vp_ref.Size.y;
}
}
if !out_size.is_null() {
*out_size = r;
}
});
}
unsafe extern "C" fn winit_set_window_size(
vp: *mut dear_imgui_rs::sys::ImGuiViewport,
size: dear_imgui_rs::sys::ImVec2,
) {
abort_on_panic("winit_set_window_size", || {
if vp.is_null() {
return;
}
if let Some(vd) = unsafe { viewport_data_mut(vp) } {
if let Some(window) = unsafe { vd.window.as_ref() } {
let logical: LogicalSize<f64> = LogicalSize::new(size.x as f64, size.y as f64);
let _ = window.request_inner_size(winit::dpi::Size::Logical(logical));
vd.ignore_window_size_event_frame =
unsafe { dear_imgui_rs::sys::igGetFrameCount() };
}
}
});
}
unsafe extern "C" fn winit_set_window_focus(vp: *mut dear_imgui_rs::sys::ImGuiViewport) {
abort_on_panic("winit_set_window_focus", || {
if vp.is_null() {
return;
}
let vp_ref = unsafe { &*vp };
if let Some(vd) = unsafe { viewport_data_ref(vp_ref) } {
if let Some(window) = unsafe { vd.window.as_ref() } {
window.focus_window();
}
}
});
}
unsafe extern "C" fn winit_get_window_focus(vp: *mut dear_imgui_rs::sys::ImGuiViewport) -> bool {
abort_on_panic("winit_get_window_focus", || {
if vp.is_null() {
return false;
}
let vp_ref = unsafe { &*vp };
if let Some(vd) = unsafe { viewport_data_ref(vp_ref) } {
if let Some(window) = unsafe { vd.window.as_ref() } {
return window.has_focus();
}
}
false
})
}
unsafe extern "C" fn winit_get_window_minimized(
vp: *mut dear_imgui_rs::sys::ImGuiViewport,
) -> bool {
abort_on_panic("Platform_GetWindowMinimized", || {
if vp.is_null() {
return false;
}
let vp_ref = unsafe { &*vp };
if let Some(vd) = unsafe { viewport_data_ref(vp_ref) } {
if let Some(window) = unsafe { vd.window.as_ref() } {
return window.is_minimized().unwrap_or(false);
}
}
false
})
}
unsafe extern "C" fn winit_set_window_title(
vp: *mut dear_imgui_rs::sys::ImGuiViewport,
title: *const c_char,
) {
abort_on_panic("Platform_SetWindowTitle", || {
if vp.is_null() || title.is_null() {
return;
}
let vp_ref = unsafe { &*vp };
if let Some(vd) = unsafe { viewport_data_ref(vp_ref) } {
if let Some(window) = unsafe { vd.window.as_ref() } {
let title = unsafe { CStr::from_ptr(title) }.to_string_lossy();
window.set_title(title.as_ref());
}
}
});
}
unsafe extern "C" fn winit_get_window_framebuffer_scale_out(
vp: *mut dear_imgui_rs::sys::ImGuiViewport,
out_scale: *mut dear_imgui_rs::sys::ImVec2,
) {
abort_on_panic("Platform_GetWindowFramebufferScale", || {
if out_scale.is_null() {
return;
}
let mut result = dear_imgui_rs::sys::ImVec2 { x: 1.0, y: 1.0 };
if vp.is_null() {
unsafe { *out_scale = result };
return;
}
let vp_ref = unsafe { &*vp };
if let Some(vd) = unsafe { viewport_data_mut(vp) } {
if vd.window.is_null() {
unsafe { *out_scale = result };
return;
}
if let Some(window) = unsafe { vd.window.as_ref() } {
let scale = window.scale_factor() as f32;
if cfg!(feature = "mv-log") && (scale - vd.last_log_fb_scale).abs() > 0.01 {
mvlog!(
"[winit-mv] fb_scale changed id={} -> {:.2}",
vp_ref.ID,
scale
);
vd.last_log_fb_scale = scale;
}
result = dear_imgui_rs::sys::ImVec2 { x: scale, y: scale };
}
}
unsafe { *out_scale = result };
})
}
unsafe extern "C" fn winit_get_window_dpi_scale(vp: *mut dear_imgui_rs::sys::ImGuiViewport) -> f32 {
abort_on_panic("Platform_GetWindowDpiScale", || {
if vp.is_null() {
return 1.0;
}
let vp_ref = &mut *vp;
let mut scale = 1.0f32;
if let Some(vd) = viewport_data_ref(vp_ref) {
if let Some(window) = vd.window.as_ref() {
scale = window.scale_factor() as f32;
}
}
if !scale.is_finite() || scale <= 0.0 {
scale = 1.0;
}
scale
})
}
unsafe extern "C" fn winit_on_changed_viewport(vp: *mut dear_imgui_rs::sys::ImGuiViewport) {
abort_on_panic("Platform_OnChangedViewport", || {
if vp.is_null() {
return;
}
let vp_ref = &*vp;
mvlog!(
"[winit-mv] OnChangedViewport id={} pos=({:.1},{:.1}) size=({:.1},{:.1}) dpi_scale={:.2} fb_scale=({:.2},{:.2})",
vp_ref.ID,
vp_ref.Pos.x,
vp_ref.Pos.y,
vp_ref.Size.x,
vp_ref.Size.y,
vp_ref.DpiScale,
vp_ref.FramebufferScale.x,
vp_ref.FramebufferScale.y
);
});
}
unsafe extern "C" fn winit_set_window_alpha(
vp: *mut dear_imgui_rs::sys::ImGuiViewport,
_alpha: f32,
) {
abort_on_panic("Platform_SetWindowAlpha", || {
if vp.is_null() {
return;
}
});
}
unsafe extern "C" fn winit_platform_render_window(
vp: *mut dear_imgui_rs::sys::ImGuiViewport,
_render_arg: *mut c_void,
) {
abort_on_panic("Platform_RenderWindow", || {
if vp.is_null() {
return;
}
});
}
unsafe extern "C" fn winit_platform_swap_buffers(
vp: *mut dear_imgui_rs::sys::ImGuiViewport,
_render_arg: *mut c_void,
) {
abort_on_panic("Platform_SwapBuffers", || {
if vp.is_null() {
return;
}
});
}
unsafe extern "C" fn winit_platform_create_vk_surface(
_vp: *mut dear_imgui_rs::sys::ImGuiViewport,
_vk_inst: u64,
_vk_allocators: *const c_void,
out_vk_surface: *mut u64,
) -> ::std::os::raw::c_int {
abort_on_panic("Platform_CreateVkSurface", || {
if !out_vk_surface.is_null() {
*out_vk_surface = 0;
}
-1 })
}
unsafe extern "C" fn winit_update_window(vp: *mut dear_imgui_rs::sys::ImGuiViewport) {
abort_on_panic("Platform_UpdateWindow", || {
if vp.is_null() {
return;
}
});
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_util::test_sync::lock_context;
#[test]
fn install_platform_callbacks_targets_passed_context() {
let _guard = lock_context();
let mut ctx_a = Context::create();
let raw_a = ctx_a.as_raw();
let pio_a = unsafe { dear_imgui_rs::sys::igGetPlatformIO_ContextPtr(raw_a) };
unsafe {
dear_imgui_rs::sys::igSetCurrentContext(std::ptr::null_mut());
}
let ctx_b = Context::create();
let raw_b = ctx_b.as_raw();
let pio_b = unsafe { dear_imgui_rs::sys::igGetPlatformIO_ContextPtr(raw_b) };
install_platform_callbacks(&mut ctx_a);
unsafe {
assert_eq!(dear_imgui_rs::sys::igGetCurrentContext(), raw_b);
assert!((*pio_a).Platform_CreateWindow.is_some());
assert!((*pio_a).Platform_GetWindowPos.is_some());
assert!((*pio_a).Platform_GetWindowSize.is_some());
assert!((*pio_a).Platform_GetWindowFramebufferScale.is_some());
assert!((*pio_a).Platform_GetWindowDpiScale.is_some());
assert!((*pio_a).Platform_OnChangedViewport.is_some());
assert!((*pio_b).Platform_CreateWindow.is_none());
assert!((*pio_b).Platform_GetWindowPos.is_none());
assert!((*pio_b).Platform_GetWindowSize.is_none());
assert!((*pio_b).Platform_GetWindowFramebufferScale.is_none());
assert!((*pio_b).Platform_GetWindowDpiScale.is_none());
assert!((*pio_b).Platform_OnChangedViewport.is_none());
dear_imgui_rs::sys::igSetCurrentContext(raw_a);
}
drop(ctx_a);
unsafe {
dear_imgui_rs::sys::igSetCurrentContext(raw_b);
}
drop(ctx_b);
}
#[test]
fn shutdown_multi_viewport_support_targets_passed_context() {
let _guard = lock_context();
let mut ctx_a = Context::create();
let raw_a = ctx_a.as_raw();
let main_viewport_a = unsafe { dear_imgui_rs::sys::igGetMainViewport() };
let vd_a = unsafe {
let vd = Box::into_raw(Box::new(ViewportData::new()));
register_viewport_data(vd);
(*main_viewport_a).PlatformUserData = vd.cast();
vd
};
unsafe {
dear_imgui_rs::sys::igSetCurrentContext(std::ptr::null_mut());
}
let mut ctx_b = Context::create();
let raw_b = ctx_b.as_raw();
let main_viewport_b = unsafe { dear_imgui_rs::sys::igGetMainViewport() };
let vd_b = unsafe {
let vd = Box::into_raw(Box::new(ViewportData::new()));
register_viewport_data(vd);
(*main_viewport_b).PlatformUserData = vd.cast();
vd
};
shutdown_multi_viewport_support(&mut ctx_a);
unsafe {
assert_eq!(dear_imgui_rs::sys::igGetCurrentContext(), raw_b);
assert!((*main_viewport_a).PlatformUserData.is_null());
assert!(!(*main_viewport_b).PlatformUserData.is_null());
assert!(is_winit_viewport_data(vd_b));
assert!(!std::ptr::eq(
(*main_viewport_b).PlatformUserData.cast::<ViewportData>(),
vd_a
));
}
shutdown_multi_viewport_support(&mut ctx_b);
unsafe {
assert_eq!(dear_imgui_rs::sys::igGetCurrentContext(), raw_b);
assert!((*main_viewport_b).PlatformUserData.is_null());
assert!(!is_winit_viewport_data(vd_b));
dear_imgui_rs::sys::igSetCurrentContext(raw_a);
}
drop(ctx_a);
unsafe {
dear_imgui_rs::sys::igSetCurrentContext(raw_b);
}
drop(ctx_b);
}
}