Skip to main content

dear_imgui_winit/
platform.rs

1//! Main platform implementation for Dear ImGui winit backend
2//!
3//! This module contains the core `WinitPlatform` struct and its implementation
4//! for integrating Dear ImGui with winit windowing.
5
6mod frame;
7mod input_events;
8mod ownership;
9mod window_state;
10
11use dear_imgui_rs::{
12    ContextAttachmentError, ContextBindingError, ContextPlatformAttachmentReleaseError,
13    ContextPlatformWindowTeardownError,
14};
15use thiserror::Error;
16
17pub use frame::HiDpiMode;
18pub use ownership::WinitPlatform;
19#[cfg(feature = "multi-viewport")]
20pub(crate) use ownership::{WINIT_VIEWPORT_FLAGS, WinitPlatformControl};
21
22/// Failure to attach or operate the Winit platform backend.
23#[derive(Clone, Debug, Eq, Error, PartialEq)]
24#[non_exhaustive]
25pub enum WinitPlatformError {
26    /// The Dear ImGui Context rejected the platform attachment.
27    #[error(transparent)]
28    Attachment(#[from] ContextAttachmentError),
29    /// The Context rejected release of the active platform attachment generation.
30    #[error(transparent)]
31    PlatformAttachmentRelease(#[from] ContextPlatformAttachmentReleaseError),
32    /// The originating Dear ImGui Context can no longer be entered normally.
33    #[error(transparent)]
34    Context(#[from] ContextBindingError),
35    /// Dear ImGui rejected an explicit platform-window teardown transaction.
36    #[error(transparent)]
37    PlatformWindowTeardown(#[from] ContextPlatformWindowTeardownError),
38    /// The supplied Context is not the Context owned by this platform backend.
39    #[error("the Winit platform backend belongs to a different Dear ImGui context")]
40    ContextMismatch,
41    /// Another platform backend already owns a required global field.
42    #[error("Dear ImGui platform state `{field}` is already owned")]
43    PlatformStateOccupied { field: &'static str },
44    /// A field claimed by this platform backend changed while it remained attached.
45    #[error("Dear ImGui platform state `{field}` changed while Winit was attached")]
46    PlatformStateReplaced { field: &'static str },
47    /// No main window has been attached to the platform backend.
48    #[error("attach a main Winit window before using this operation")]
49    WindowNotAttached,
50    /// The supplied window is not the platform backend's attached main window.
51    #[error("the Winit window does not match the platform backend's attached main window")]
52    WindowMismatch,
53    /// Multi-viewport support is already attached to this platform owner.
54    #[error("Winit multi-viewport support is already attached")]
55    RuntimeAlreadyAttached,
56    /// A configuration mutation would invalidate the active multi-viewport coordinate contract.
57    #[error("Winit platform configuration is locked while multi-viewport support is attached")]
58    RuntimeConfigurationLocked,
59    /// The build artifact lacks the aggregate callback bridge required by this backend.
60    #[error("dear-imgui-sys was built without PlatformIO aggregate ABI hooks")]
61    AggregateCallbackHooksUnavailable,
62    /// Another platform backend already owns one of the required callback slots.
63    #[error("ImGuiPlatformIO callback `{callback}` is already owned by another platform backend")]
64    PlatformCallbackOccupied { callback: &'static str },
65    /// Another platform backend already advertises a capability owned by this runtime.
66    #[error("Dear ImGui backend capability `{flag}` is already owned by another platform backend")]
67    PlatformCapabilityOccupied { flag: &'static str },
68    /// A slot in the captured platform callback table changed while the runtime remained attached.
69    #[error(
70        "Winit platform callback table slot `{callback}` changed while the runtime was attached"
71    )]
72    PlatformCallbackReplaced { callback: &'static str },
73    /// Platform teardown was requested before the renderer released its viewport callback.
74    #[error("renderer state `{field}` is still installed; shut down the renderer before Winit")]
75    RendererShutdownRequired { field: &'static str },
76    /// A viewport already has platform data owned by another backend.
77    #[error("viewport platform data or handle is already owned by another platform backend")]
78    ForeignPlatformUserData,
79    /// A live viewport stopped matching the Winit platform data registered for it.
80    #[error("Winit lost ownership of viewport {viewport_id} field `{field}`")]
81    ViewportOwnershipLost {
82        /// Dear ImGui viewport identifier whose native platform state drifted.
83        viewport_id: u32,
84        /// Native platform field whose value no longer matches Winit's registration.
85        field: &'static str,
86    },
87    /// Winit did not expose any monitor geometry that can back Dear ImGui viewports.
88    #[error("Winit did not expose any monitor geometry")]
89    NoMonitors,
90    /// Winit could not form a complete native monitor publication.
91    #[cfg(feature = "multi-viewport")]
92    #[error("Winit native monitor collection is unavailable: {reason}")]
93    MonitorCollectionUnavailable {
94        reason: crate::multi_viewport::WinitMonitorCollectionFailure,
95    },
96    /// Winit exposed monitor geometry that violates Dear ImGui's platform contract.
97    #[error("Winit monitor {monitor} is invalid: {reason}")]
98    InvalidMonitorGeometry {
99        monitor: usize,
100        reason: &'static str,
101    },
102    /// Dear ImGui supplied viewport geometry that cannot be represented by Winit.
103    #[error("Dear ImGui viewport geometry is invalid during {operation}: {reason}")]
104    InvalidViewportGeometry {
105        operation: &'static str,
106        reason: &'static str,
107    },
108    /// Custom single-window coordinate scaling is not implemented for platform viewports.
109    #[error("Winit multi-viewport requires HiDpiMode::Default")]
110    CustomHiDpiModeUnsupported,
111    /// Wayland cannot provide the desktop-space positioning required by Dear ImGui viewports.
112    #[error("Wayland is unsupported by the Winit multi-viewport backend; use X11 on Linux")]
113    WaylandUnsupported,
114    /// The target has no supported native desktop window-system contract.
115    #[error("the Winit multi-viewport backend does not support target `{target}`")]
116    UnsupportedWindowSystem { target: &'static str },
117    /// A requested viewport flag cannot be implemented faithfully for this operation.
118    #[error("Winit cannot honor viewport flag `{flag}` during {operation}")]
119    UnsupportedViewportFlag {
120        flag: &'static str,
121        operation: &'static str,
122    },
123    /// The monitor count cannot be represented by Dear ImGui's native vector.
124    #[error("the Winit monitor count exceeds i32::MAX")]
125    MonitorCountOverflow,
126    /// Dear ImGui's allocator could not reserve monitor storage.
127    #[error("Dear ImGui failed to allocate Winit monitor storage")]
128    MonitorStorageAllocationFailed,
129    /// Dear ImGui requested a new viewport outside a scoped Winit event-loop entry.
130    #[error("Winit viewport creation requires WinitPlatform::with_event_loop")]
131    EventLoopUnavailable,
132    /// Winit failed to create a secondary viewport window.
133    #[error("Winit failed to create a secondary viewport window: {message}")]
134    WindowCreation { message: String },
135    /// A fallible operation on a secondary Winit window failed.
136    #[error("Winit viewport operation `{operation}` failed: {message}")]
137    WindowOperation {
138        operation: &'static str,
139        message: String,
140    },
141    /// A Rust platform callback panicked; the panic was contained at the C ABI boundary.
142    #[error("Winit platform callback `{callback}` panicked")]
143    CallbackPanicked { callback: &'static str },
144    /// The owning runtime has already shut down or entered a terminal fault.
145    #[error("the Winit platform runtime is no longer attached")]
146    RuntimeDetached,
147    #[cfg(test)]
148    #[error("injected Winit construction failure after {stage}")]
149    InjectedConstructionFailure { stage: &'static str },
150}
151
152#[cfg(test)]
153mod tests {
154    use std::ffi::{CStr, CString};
155    use std::rc::Rc;
156
157    use dear_imgui_rs::{
158        BackendFlags, Context, ContextAttachment, ContextAttachmentRole,
159        ContextPlatformAttachmentReleaseError,
160    };
161    use winit::event::{Event, WindowEvent};
162
163    use super::input_events::{event_targets_window, rescale_mouse_pos_for_hidpi_change};
164    use super::ownership::{
165        PlatformOwnerToken, PlatformState, WINIT_BASE_FLAGS, WINIT_RESERVED_FLAGS,
166        winit_backend_name_ptr,
167    };
168    use super::window_state::ime_callback_eq;
169    use super::{HiDpiMode, WinitPlatform, WinitPlatformError};
170    use crate::test_util::test_sync::lock_context;
171
172    struct ActiveRendererMarker;
173    struct ActiveRendererAttachment;
174
175    impl ContextAttachment for ActiveRendererAttachment {}
176
177    unsafe extern "C" fn foreign_ime_callback(
178        _context: *mut dear_imgui_rs::sys::ImGuiContext,
179        _viewport: *mut dear_imgui_rs::sys::ImGuiViewport,
180        _data: *mut dear_imgui_rs::sys::ImGuiPlatformImeData,
181    ) {
182    }
183
184    #[test]
185    fn test_hidpi_mode_default() {
186        assert_eq!(HiDpiMode::default(), HiDpiMode::Default);
187    }
188
189    #[test]
190    fn test_platform_creation() {
191        let _guard = lock_context();
192        let mut ctx = Context::create();
193        let platform = WinitPlatform::new(&mut ctx).unwrap();
194
195        assert_eq!(platform.hidpi_mode, HiDpiMode::Default);
196        assert_eq!(platform.hidpi_factor, 1.0);
197        assert_eq!(platform.cursor_cache, None);
198        assert!(!platform.ime_enabled);
199    }
200
201    #[test]
202    fn platform_shutdown_rejects_an_active_renderer_before_releasing_base_state() {
203        let _guard = lock_context();
204        let mut context = Context::create();
205        let mut platform = WinitPlatform::new(&mut context).unwrap();
206        let mut renderer = context
207            .register_attachment::<ActiveRendererMarker>(
208                ContextAttachmentRole::Renderer,
209                Rc::new(ActiveRendererAttachment),
210            )
211            .unwrap();
212        let io = unsafe { dear_imgui_rs::sys::igGetIO_ContextPtr(context.as_raw()) };
213
214        assert!(matches!(
215            platform.shutdown(&mut context),
216            Err(WinitPlatformError::PlatformAttachmentRelease(
217                ContextPlatformAttachmentReleaseError::RendererActive
218            ))
219        ));
220        assert_eq!(
221            unsafe { (*io).BackendPlatformUserData },
222            platform.control.token_ptr()
223        );
224        assert!(platform.control.attachment_handle().unwrap().is_attached());
225
226        assert_eq!(renderer.detach(), Ok(true));
227        platform.shutdown(&mut context).unwrap();
228    }
229
230    #[test]
231    fn platform_drop_defers_base_release_while_a_renderer_attachment_is_active() {
232        let _guard = lock_context();
233        let mut context = Context::create();
234        let platform = WinitPlatform::new(&mut context).unwrap();
235        let control = platform.control();
236        let mut renderer = context
237            .register_attachment::<ActiveRendererMarker>(
238                ContextAttachmentRole::Renderer,
239                Rc::new(ActiveRendererAttachment),
240            )
241            .unwrap();
242        let io = unsafe { dear_imgui_rs::sys::igGetIO_ContextPtr(context.as_raw()) };
243
244        drop(platform);
245
246        assert_eq!(
247            unsafe { (*io).BackendPlatformUserData },
248            control.token_ptr()
249        );
250        assert!(control.attachment_handle().unwrap().is_attached());
251        assert_eq!(renderer.detach(), Ok(true));
252        drop(context);
253        assert_eq!(control.state.get(), PlatformState::ContextDestroyed);
254    }
255
256    #[test]
257    fn platform_claim_publishes_stable_identity_and_cleans_up_exact_ownership() {
258        let _guard = lock_context();
259        let mut context = Context::create();
260        let platform_io =
261            unsafe { dear_imgui_rs::sys::igGetPlatformIO_ContextPtr(context.as_raw()) };
262        let baseline_ime_callback = unsafe { (*platform_io).Platform_SetImeDataFn };
263        let baseline_ime_user_data = unsafe { (*platform_io).Platform_ImeUserData };
264
265        let platform = WinitPlatform::new(&mut context).unwrap();
266        let control = platform.control();
267        let io = unsafe { dear_imgui_rs::sys::igGetIO_ContextPtr(context.as_raw()) };
268
269        assert_ne!(std::mem::size_of::<PlatformOwnerToken>(), 0);
270        assert_eq!(
271            unsafe { (*io).BackendPlatformName },
272            winit_backend_name_ptr()
273        );
274        assert_eq!(
275            unsafe { (*io).BackendPlatformUserData },
276            control.token_ptr()
277        );
278        assert_eq!(
279            unsafe { CStr::from_ptr((*io).BackendPlatformName) },
280            unsafe { CStr::from_ptr(winit_backend_name_ptr()) }
281        );
282        assert_eq!(
283            BackendFlags::from_bits_retain(unsafe { (*io).BackendFlags }) & WINIT_RESERVED_FLAGS,
284            WINIT_BASE_FLAGS
285        );
286        assert!(ime_callback_eq(
287            unsafe { (*platform_io).Platform_SetImeDataFn },
288            baseline_ime_callback
289        ));
290        assert_eq!(
291            unsafe { (*platform_io).Platform_ImeUserData },
292            baseline_ime_user_data
293        );
294
295        drop(platform);
296
297        assert!(unsafe { (*io).BackendPlatformName.is_null() });
298        assert!(unsafe { (*io).BackendPlatformUserData.is_null() });
299        assert!(
300            (BackendFlags::from_bits_retain(unsafe { (*io).BackendFlags }) & WINIT_RESERVED_FLAGS)
301                .is_empty()
302        );
303        assert!(ime_callback_eq(
304            unsafe { (*platform_io).Platform_SetImeDataFn },
305            baseline_ime_callback
306        ));
307        assert_eq!(
308            unsafe { (*platform_io).Platform_ImeUserData },
309            baseline_ime_user_data
310        );
311    }
312
313    #[test]
314    fn platform_attachment_is_unique_per_context_and_reusable_after_release() {
315        let _guard = lock_context();
316        let mut context = Context::create();
317        let platform = WinitPlatform::new(&mut context).unwrap();
318
319        let error = match WinitPlatform::new(&mut context) {
320            Ok(_) => panic!("a Context cannot have two Winit platform owners"),
321            Err(error) => error,
322        };
323        assert_eq!(
324            error,
325            WinitPlatformError::PlatformStateOccupied {
326                field: "BackendPlatformName"
327            }
328        );
329
330        drop(platform);
331        drop(WinitPlatform::new(&mut context).unwrap());
332    }
333
334    #[test]
335    fn base_contract_reports_each_replaced_owned_field() {
336        let _guard = lock_context();
337        let mut context = Context::create();
338        let mut platform = WinitPlatform::new(&mut context).unwrap();
339        let control = platform.control();
340        let io = unsafe { dear_imgui_rs::sys::igGetIO_ContextPtr(context.as_raw()) };
341        let platform_io =
342            unsafe { dear_imgui_rs::sys::igGetPlatformIO_ContextPtr(context.as_raw()) };
343        let baseline_ime_callback = unsafe { (*platform_io).Platform_SetImeDataFn };
344
345        let validate = || {
346            control
347                .binding()
348                .with_bound_context(|| control.validate_complete_contract_in_current_context())
349        };
350
351        unsafe { (*io).BackendPlatformName = std::ptr::null() };
352        assert_eq!(
353            validate(),
354            Err(WinitPlatformError::PlatformStateReplaced {
355                field: "BackendPlatformName"
356            })
357        );
358        unsafe { (*io).BackendPlatformName = winit_backend_name_ptr() };
359
360        unsafe { (*io).BackendPlatformUserData = std::ptr::null_mut() };
361        assert_eq!(
362            validate(),
363            Err(WinitPlatformError::PlatformStateReplaced {
364                field: "BackendPlatformUserData"
365            })
366        );
367        unsafe { (*io).BackendPlatformUserData = control.token_ptr() };
368
369        unsafe { (*io).BackendFlags &= !WINIT_BASE_FLAGS.bits() };
370        assert_eq!(
371            validate(),
372            Err(WinitPlatformError::PlatformStateReplaced {
373                field: "BackendFlags"
374            })
375        );
376        unsafe { (*io).BackendFlags |= WINIT_BASE_FLAGS.bits() };
377
378        unsafe { (*platform_io).Platform_SetImeDataFn = Some(foreign_ime_callback) };
379        assert_eq!(
380            validate(),
381            Err(WinitPlatformError::PlatformStateReplaced {
382                field: "Platform_SetImeDataFn"
383            })
384        );
385        unsafe { (*platform_io).Platform_SetImeDataFn = baseline_ime_callback };
386
387        let foreign_ime_user_data = std::ptr::dangling_mut::<u8>().cast();
388        unsafe { (*platform_io).Platform_ImeUserData = foreign_ime_user_data };
389        assert_eq!(
390            validate(),
391            Err(WinitPlatformError::PlatformStateReplaced {
392                field: "Platform_ImeUserData"
393            })
394        );
395        unsafe { (*platform_io).Platform_ImeUserData = std::ptr::null_mut() };
396
397        platform.shutdown(&mut context).unwrap();
398    }
399
400    #[test]
401    fn public_base_entry_latches_contract_drift_until_ordered_shutdown() {
402        let _guard = lock_context();
403        let mut context = Context::create();
404        let mut platform = WinitPlatform::new(&mut context).unwrap();
405        let io = unsafe { dear_imgui_rs::sys::igGetIO_ContextPtr(context.as_raw()) };
406        unsafe { (*io).BackendPlatformName = std::ptr::null() };
407
408        let expected = WinitPlatformError::PlatformStateReplaced {
409            field: "BackendPlatformName",
410        };
411        assert_eq!(
412            platform.set_software_cursor_enabled(&mut context, true),
413            Err(expected.clone())
414        );
415        assert!(
416            !BackendFlags::from_bits_retain(unsafe { (*io).BackendFlags })
417                .contains(WINIT_BASE_FLAGS)
418        );
419
420        unsafe { (*io).BackendPlatformName = winit_backend_name_ptr() };
421        assert_eq!(
422            platform.set_software_cursor_enabled(&mut context, false),
423            Err(expected.clone())
424        );
425        assert_eq!(platform.shutdown(&mut context), Err(expected));
426        assert!(unsafe { (*io).BackendPlatformName.is_null() });
427        assert!(unsafe { (*io).BackendPlatformUserData.is_null() });
428        assert_eq!(platform.shutdown(&mut context), Ok(()));
429    }
430
431    #[test]
432    fn shutdown_preserves_a_same_text_foreign_backend_name_pointer() {
433        let _guard = lock_context();
434        let mut context = Context::create();
435        let mut platform = WinitPlatform::new(&mut context).unwrap();
436        let io = unsafe { dear_imgui_rs::sys::igGetIO_ContextPtr(context.as_raw()) };
437        let foreign_name = CString::new(
438            unsafe { CStr::from_ptr(winit_backend_name_ptr()) }
439                .to_bytes()
440                .to_vec(),
441        )
442        .unwrap();
443        assert_ne!(foreign_name.as_ptr(), winit_backend_name_ptr());
444        unsafe { (*io).BackendPlatformName = foreign_name.as_ptr() };
445
446        assert_eq!(
447            platform.shutdown(&mut context),
448            Err(WinitPlatformError::PlatformStateReplaced {
449                field: "BackendPlatformName"
450            })
451        );
452        assert_eq!(unsafe { (*io).BackendPlatformName }, foreign_name.as_ptr());
453        assert_eq!(
454            unsafe { CStr::from_ptr((*io).BackendPlatformName) },
455            unsafe { CStr::from_ptr(winit_backend_name_ptr()) }
456        );
457        assert!(unsafe { (*io).BackendPlatformUserData.is_null() });
458
459        unsafe { (*io).BackendPlatformName = std::ptr::null() };
460        drop(WinitPlatform::new(&mut context).unwrap());
461    }
462
463    #[test]
464    fn explicit_shutdown_preserves_complete_foreign_base_takeover_and_flags() {
465        let _guard = lock_context();
466        let mut context = Context::create();
467        let mut platform = WinitPlatform::new(&mut context).unwrap();
468        let io = unsafe { dear_imgui_rs::sys::igGetIO_ContextPtr(context.as_raw()) };
469        let foreign_name = CString::new("foreign-platform").unwrap();
470        let foreign_token = std::ptr::dangling_mut::<u8>().cast();
471        unsafe {
472            (*io).BackendPlatformName = foreign_name.as_ptr();
473            (*io).BackendPlatformUserData = foreign_token;
474        }
475
476        assert_eq!(
477            platform.shutdown(&mut context),
478            Err(WinitPlatformError::PlatformStateReplaced {
479                field: "BackendPlatformUserData"
480            })
481        );
482        assert_eq!(unsafe { (*io).BackendPlatformName }, foreign_name.as_ptr());
483        assert_eq!(unsafe { (*io).BackendPlatformUserData }, foreign_token);
484        assert!(
485            BackendFlags::from_bits_retain(unsafe { (*io).BackendFlags })
486                .contains(WINIT_BASE_FLAGS)
487        );
488
489        unsafe {
490            (*io).BackendPlatformName = std::ptr::null();
491            (*io).BackendPlatformUserData = std::ptr::null_mut();
492            (*io).BackendFlags &= !WINIT_BASE_FLAGS.bits();
493        }
494    }
495
496    #[test]
497    fn drop_preserves_complete_foreign_base_takeover_and_flags() {
498        let _guard = lock_context();
499        let mut context = Context::create();
500        let io = unsafe { dear_imgui_rs::sys::igGetIO_ContextPtr(context.as_raw()) };
501        let foreign_name = CString::new("foreign-platform").unwrap();
502        let foreign_token = std::ptr::dangling_mut::<u8>().cast();
503        let platform = WinitPlatform::new(&mut context).unwrap();
504        unsafe {
505            (*io).BackendPlatformName = foreign_name.as_ptr();
506            (*io).BackendPlatformUserData = foreign_token;
507        }
508
509        drop(platform);
510
511        assert_eq!(unsafe { (*io).BackendPlatformName }, foreign_name.as_ptr());
512        assert_eq!(unsafe { (*io).BackendPlatformUserData }, foreign_token);
513        assert!(
514            BackendFlags::from_bits_retain(unsafe { (*io).BackendFlags })
515                .contains(WINIT_BASE_FLAGS)
516        );
517
518        unsafe {
519            (*io).BackendPlatformName = std::ptr::null();
520            (*io).BackendPlatformUserData = std::ptr::null_mut();
521            (*io).BackendFlags &= !WINIT_BASE_FLAGS.bits();
522        }
523    }
524
525    #[test]
526    fn complete_foreign_takeover_does_not_revoke_foreign_flags_on_contract_fault() {
527        let _guard = lock_context();
528        let mut context = Context::create();
529        let mut platform = WinitPlatform::new(&mut context).unwrap();
530        let io = unsafe { dear_imgui_rs::sys::igGetIO_ContextPtr(context.as_raw()) };
531        let foreign_name = CString::new("foreign-platform").unwrap();
532        let foreign_token = std::ptr::dangling_mut::<u8>().cast();
533        unsafe {
534            (*io).BackendPlatformName = foreign_name.as_ptr();
535            (*io).BackendPlatformUserData = foreign_token;
536        }
537
538        let expected = WinitPlatformError::PlatformStateReplaced {
539            field: "BackendPlatformName",
540        };
541        assert_eq!(
542            platform.set_software_cursor_enabled(&mut context, true),
543            Err(expected.clone())
544        );
545        assert!(
546            BackendFlags::from_bits_retain(unsafe { (*io).BackendFlags })
547                .contains(WINIT_BASE_FLAGS)
548        );
549        assert_eq!(platform.shutdown(&mut context), Err(expected));
550        assert_eq!(unsafe { (*io).BackendPlatformName }, foreign_name.as_ptr());
551        assert_eq!(unsafe { (*io).BackendPlatformUserData }, foreign_token);
552        assert!(
553            BackendFlags::from_bits_retain(unsafe { (*io).BackendFlags })
554                .contains(WINIT_BASE_FLAGS)
555        );
556
557        unsafe {
558            (*io).BackendPlatformName = std::ptr::null();
559            (*io).BackendPlatformUserData = std::ptr::null_mut();
560            (*io).BackendFlags &= !WINIT_BASE_FLAGS.bits();
561        }
562    }
563
564    #[test]
565    fn test_hidpi_mode_setting() {
566        let _guard = lock_context();
567        let mut ctx = Context::create();
568        let mut platform = WinitPlatform::new(&mut ctx).unwrap();
569
570        platform.set_hidpi_mode(HiDpiMode::Locked(2.0)).unwrap();
571        assert_eq!(platform.hidpi_mode, HiDpiMode::Locked(2.0));
572
573        platform.set_hidpi_mode(HiDpiMode::Rounded).unwrap();
574        assert_eq!(platform.hidpi_mode, HiDpiMode::Rounded);
575    }
576
577    #[test]
578    fn full_window_events_are_filtered_by_window_id_before_dispatch() {
579        let target = winit::window::WindowId::from(41_u64);
580        let foreign = winit::window::WindowId::from(42_u64);
581        let foreign_event = Event::<()>::WindowEvent {
582            window_id: foreign,
583            event: WindowEvent::Focused(true),
584        };
585        let target_event = Event::<()>::WindowEvent {
586            window_id: target,
587            event: WindowEvent::Focused(true),
588        };
589
590        assert!(!event_targets_window(target, &foreign_event));
591        assert!(event_targets_window(target, &target_event));
592        assert!(event_targets_window(target, &Event::<()>::AboutToWait));
593    }
594
595    #[test]
596    fn rescale_mouse_pos_for_hidpi_change_rejects_non_finite_results() {
597        assert_eq!(
598            rescale_mouse_pos_for_hidpi_change([10.0, 20.0], 1.0, 2.0),
599            Some([20.0, 40.0])
600        );
601        assert_eq!(
602            rescale_mouse_pos_for_hidpi_change([f32::NAN, 20.0], 1.0, 2.0),
603            None
604        );
605        assert_eq!(
606            rescale_mouse_pos_for_hidpi_change([10.0, 20.0], 0.0, 2.0),
607            Some([20.0, 40.0])
608        );
609        assert_eq!(
610            rescale_mouse_pos_for_hidpi_change([f32::MAX, 20.0], 1.0, f64::MAX),
611            None
612        );
613    }
614
615    #[test]
616    fn test_window_attributes_creation() {
617        let attrs = WinitPlatform::create_window_attributes();
618        // Just test that it doesn't panic - actual values depend on winit defaults
619        let _ = attrs;
620    }
621}