# dear-imgui-winit
Winit platform backend for the `dear-imgui-rs` Rust crate. It wires winit input/events,
cursor handling and DPI awareness into Dear ImGui. Inspired by
`imgui-rs/imgui-winit-support`.
## Compatibility
| Crate | 0.13.0 |
| dear-imgui-rs | 0.13.0 |
| winit | 0.30.13 |
See also: [docs/COMPATIBILITY.md](https://github.com/Latias94/dear-imgui-rs/blob/main/docs/COMPATIBILITY.md) for the full workspace matrix.
## Quick Start
Minimal flow with winit 0.30 ApplicationHandler-style loops:
```rust,no_run
use dear_imgui_rs::{Context, Condition};
use dear_imgui_winit::{WinitPlatform, HiDpiMode};
use winit::{event::WindowEvent, event_loop::{ActiveEventLoop, EventLoop}, window::WindowId};
struct App { /* ... */ }
impl winit::application::ApplicationHandler for App {
fn resumed(&mut self, el: &ActiveEventLoop) { /* create window + ImGui + WinitPlatform */ }
fn window_event(&mut self, el: &ActiveEventLoop, _id: WindowId, event: WindowEvent) {
let window = /* get your window */;
// 1) forward the window-local event to ImGui
self.imgui
.platform
.handle_window_event(&mut self.imgui.context, &window, &event);
match event {
WindowEvent::RedrawRequested => {
// 2) per-frame prep
self.imgui.platform.prepare_frame(&window, &mut self.imgui.context);
let ui = self.imgui.context.frame();
// 3) build UI
ui.window("Hello").size([400.0, 300.0], Condition::FirstUseEver).build(|| {
ui.text("ImGui + winit");
});
// 4) update OS cursor from UI
self.imgui.platform.prepare_render_with_ui(&ui, &window);
// 5) render via your renderer backend
let draw_data = self.imgui.context.render();
/* renderer.render(&draw_data) */
}
_ => {}
}
}
}
```
APIs of interest:
- `WinitPlatform::new(&mut Context)`
- `WinitPlatform::attach_window(&Window, HiDpiMode, &mut Context)`
- `WinitPlatform::handle_window_event(&mut Context, &Window, &WindowEvent)` — for `ApplicationHandler::window_event`
- `WinitPlatform::handle_event(&mut Context, &Window, &Event<T>)` — for closure-style `EventLoop::run`
- `WinitPlatform::prepare_frame(&Window, &mut Context)`
- `WinitPlatform::prepare_render_with_ui(&Ui, &Window)` — updates OS cursor from ImGui
- `WinitPlatform::detach_window(&Window, &mut Context)` — clears winit-owned IME hooks before a window is destroyed while the context remains alive
## DPI / HiDPI
`HiDpiMode` controls how the backend derives the framebuffer scale:
- `Default`: use winit’s `window.scale_factor()` directly.
- `Rounded`: round the winit factor to the nearest integer to avoid blurry scaling.
- `Locked(f64)`: force a custom factor (e.g. 1.0).
When DPI changes (`ScaleFactorChanged`), the backend adjusts:
- `io.display_size`, `io.display_framebuffer_scale`
- mouse position (keeping pointer location consistent across scales)
Helpers are provided if you pass winit logical values around and need the same
coordinates ImGui uses:
- `scale_size_from_winit(&Window, LogicalSize<f64>) -> LogicalSize<f64>`
- `scale_pos_from_winit(&Window, LogicalPosition<f64>) -> LogicalPosition<f64>`
- `scale_pos_for_winit(&Window, LogicalPosition<f64>) -> LogicalPosition<f64>`
## Input & IME
- Keyboard: press/release is mapped to `dear-imgui::Key`. When `event.text`
is present on key press, characters are injected via `io.add_input_character`.
Coverage includes letters/digits, punctuation (',.-/;=[]\\`), function and lock keys,
and numpad (0-9, decimal/divide/multiply/subtract/add/equal/enter).
- Mouse: buttons, position, wheel. `PixelDelta` wheel is mapped to ±1.0 steps
(consistent with most ImGui backends); `LineDelta` uses the provided values.
- Modifiers: tracked via `ModifiersChanged` and mirrored into left/right variants.
- IME: preedit is ignored (no transient injection); committed text is appended.
### Touch
Basic touch-to-mouse translation is provided:
- First active finger controls the pointer and Left mouse button.
- Started -> set position + press LMB; Moved -> update position; End/Cancelled -> release LMB.
### IME integration
- IME is **auto-managed** by default: `prepare_render_with_ui` inspects
`ui.io().want_text_input()` and toggles `Window::set_ime_allowed(...)`
accordingly. This means IME (and soft keyboards on mobile) are only enabled
while text widgets are active.
- You can temporarily override the state with
`WinitPlatform::set_ime_allowed(&window, bool)`. Auto-management may adjust
it again on subsequent frames unless you disable it.
- To fully opt out and manage IME yourself, call
`WinitPlatform::set_ime_auto_management(false)`.
- The backend tracks IME enabled/disabled state internally and exposes it
through `WinitPlatform::ime_enabled()`.
- If the window is destroyed before the ImGui context, call
`WinitPlatform::detach_window(&window, &mut context)` first so Dear ImGui no
longer holds a raw pointer to that window for IME cursor placement.
## Cursor Handling
`prepare_render_with_ui(&Ui, &Window)` updates the OS cursor from `ui.mouse_cursor()`.
Changes are cached to avoid redundant OS calls. If `ConfigFlags::NO_MOUSE_CURSOR_CHANGE`
is set, OS cursor updates are skipped. The software-drawn cursor flag is currently not
exposed via our `Io` wrapper (defaults to OS cursor).
If Dear ImGui requests repositioning (`io.want_set_mouse_pos()`), `prepare_frame`
will set the OS cursor position accordingly.
### Software Cursor
You can force Dear ImGui to draw the cursor by enabling the software cursor:
```rust
// Option 1: via Io directly
imgui_ctx.io_mut().set_mouse_draw_cursor(true);
// Option 2: helper on the platform
platform.set_software_cursor_enabled(&mut imgui_ctx, true);
```
When software cursor is enabled:
- The platform hides the OS cursor.
- Dear ImGui emits cursor geometry in draw data; ensure your renderer renders the draw lists every frame.
## Backend Flags
This backend sets (when appropriate):
- `BackendFlags::HAS_MOUSE_CURSORS`
- `BackendFlags::HAS_SET_MOUSE_POS`
For diagnostics, the backend also sets `BackendPlatformName` to `"dear-imgui-winit {version}"`.
## Multi-Viewport (Experimental)
Multi-viewport support is available behind the `multi-viewport` feature and is
**experimental**. It follows the upstream backend split:
- The winit platform layer (`dear-imgui-winit/multi-viewport`) owns OS windows and
routes events for secondary viewports.
- A renderer backend must also opt into viewports (e.g. `dear-imgui-wgpu/multi-viewport-winit`)
to create per-viewport render targets and draw them.
Current support matrix:
- **winit + WGPU**: experimental native multi-viewport, exercised by the
`multi_viewport_wgpu` example.
- Enabled on Windows/macOS/Linux; tested on Windows/macOS, Linux untested.
- Example:
`cargo run -p dear-imgui-examples --bin multi_viewport_wgpu --features multi-viewport`
- **winit + OpenGL (glow/glutin)**: no official multi-viewport stack yet.
If you need multi-viewport OpenGL today, use the SDL3 routes
(`sdl3_opengl_multi_viewport` or `sdl3_glow_multi_viewport`).
## Notes & Differences vs imgui-rs
This crate targets the `dear-imgui-rs` bindings in this repository and its API
surface. It’s intentionally separate from `imgui-rs/imgui-winit-support`, though
many behaviors are aligned for familiarity.
Known limitations:
- Key mapping covers digits, letters, navigation, modifiers, and function keys.
Some punctuation/numpad-specific variants are not mapped yet.