imgui-rs/MONITOR_INIT_FIX_SUMMARY.md
Elias Stepanik 54c61a525c fix: add monitor initialization workaround for viewport support
Provides a workaround for missing monitor initialization in platform
backends like imgui-winit-support. Without proper monitor initialization,
Dear ImGui throws "Platform init didn't setup Monitors list?" assertion.

- Add monitor_init_fix module with initialization utilities
- Support single and multi-monitor configurations
- Add unit tests and examples
- Document workaround usage

Co-Authored-By: Elias Stepanik <eliasstepanik@proton.me>
2025-07-10 12:27:37 +02:00

5.7 KiB

Monitor Initialization Fix Summary

Issue Identified

When viewport support is enabled in imgui-rs, Dear ImGui throws an assertion error:

"Platform init didn't setup Monitors list?"

This happens because platform backends like imgui-winit-support don't currently initialize the monitors list, which is required for the viewport system to function properly.

Root Cause

  1. Missing Implementation: The imgui-winit-support crate (external to imgui-rs) doesn't populate PlatformIO::monitors
  2. Dear ImGui Requirement: The viewport system requires at least one monitor to be defined
  3. Timing Critical: Monitors must be initialized BEFORE any viewport operations

Solution Implemented

Since imgui-winit-support is an external crate, we've created a workaround module monitor_init_fix that allows users to manually initialize monitors.

Usage

use imgui::*;

// Create and configure context
let mut ctx = Context::create();
ctx.io_mut().config_flags |= ConfigFlags::VIEWPORTS_ENABLE;

// Apply viewport fix (from previous issue)
viewport_issue_fix::apply_viewport_fix(&mut ctx);

// Initialize monitors BEFORE first new_frame()
monitor_init_fix::init_single_monitor(&mut ctx, 1920.0, 1080.0, 1.0);

// Or for multiple monitors:
monitor_init_fix::init_monitors(&mut ctx, &[
    monitor_init_fix::MonitorData {
        position: [0.0, 0.0],
        size: [1920.0, 1080.0],
        work_pos: [0.0, 0.0],
        work_size: [1920.0, 1040.0], // Excluding taskbar
        dpi_scale: 1.0,
    },
    monitor_init_fix::MonitorData {
        position: [1920.0, 0.0],
        size: [1920.0, 1080.0],
        work_pos: [1920.0, 0.0],
        work_size: [1920.0, 1080.0],
        dpi_scale: 1.0,
    },
]);

Implementation Details

Module Structure

  • monitor_init_fix.rs - Main implementation
  • Uses PlatformIO::monitors ImVector
  • Provides convenience functions for common cases

Key Functions

  1. init_monitors() - Initialize with custom monitor data
  2. init_single_monitor() - Quick single monitor setup
  3. clear_monitors() - Clear all monitors
  4. monitors_initialized() - Check if monitors are set
  5. monitor_count() - Get number of monitors

Integration with Winit

If you're using winit, you can enumerate real monitors:

use winit::event_loop::EventLoop;
use winit::dpi::{PhysicalPosition, PhysicalSize};

let event_loop = EventLoop::new();
let monitors: Vec<monitor_init_fix::MonitorData> = event_loop
    .available_monitors()
    .map(|monitor| {
        let PhysicalPosition { x, y } = monitor.position();
        let PhysicalSize { width, height } = monitor.size();
        
        monitor_init_fix::MonitorData {
            position: [x as f32, y as f32],
            size: [width as f32, height as f32],
            work_pos: [x as f32, y as f32],
            work_size: [width as f32, height as f32], // winit doesn't provide work area
            dpi_scale: monitor.scale_factor() as f32,
        }
    })
    .collect();

monitor_init_fix::init_monitors(&mut ctx, &monitors);

Files Added/Modified

New Files

  • imgui/src/monitor_init_fix.rs - Monitor initialization utilities
  • imgui/examples/monitor_initialization.rs - Usage example
  • imgui/tests/monitor_init_test.rs - Unit tests
  • imgui/tests/monitor_viewport_integration_test.rs - Integration tests

Modified Files

  • imgui/src/lib.rs - Added pub mod monitor_init_fix

Known Limitations

  1. Work Area: Winit doesn't provide work area (excluding taskbar), so we use full monitor size
  2. Platform Handles: Not preserved (set to null) as they're not needed for basic viewport support
  3. Hot-plug Support: Monitor changes require manual re-initialization

Complete Example

use imgui::*;

fn main() {
    let mut ctx = Context::create();
    
    // Configure for viewports
    ctx.io_mut().config_flags |= ConfigFlags::VIEWPORTS_ENABLE;
    ctx.io_mut().display_size = [1280.0, 720.0];
    ctx.io_mut().delta_time = 1.0 / 60.0;
    ctx.fonts().build_rgba32_texture();
    
    // Apply fixes
    viewport_issue_fix::apply_viewport_fix(&mut ctx);
    monitor_init_fix::init_single_monitor(&mut ctx, 1920.0, 1080.0, 1.0);
    
    // Set platform backend
    ctx.set_platform_backend(MyPlatformBackend::new());
    
    loop {
        // Apply viewport fix each frame
        viewport_issue_fix::apply_viewport_fix(&mut ctx);
        
        let ui = ctx.new_frame();
        
        // Windows can now be dragged outside!
        ui.window("Draggable")
            .size([300.0, 200.0], Condition::FirstUseEver)
            .build(|| {
                ui.text("Drag me outside!");
            });
        
        let draw_data = ctx.render();
        // ... render draw_data ...
        
        ctx.update_platform_windows();
        ctx.render_platform_windows_default();
    }
}

Testing

Run the tests with:

cargo test --features docking monitor

Run the example:

cargo run --example monitor_initialization --features docking

Future Improvements

  1. Upstream Fix: The proper solution would be to add monitor initialization to imgui-winit-support
  2. Work Area Detection: Platform-specific code to detect actual work area
  3. Auto-initialization: Integrate with platform backend setup
  4. Monitor Events: Handle display configuration changes automatically

Summary

This workaround successfully enables viewport support by manually initializing the monitors list. While not ideal (requiring manual setup), it allows users to use the viewport feature until proper support is added to platform backends like imgui-winit-support.