extended_winit_imgui_support.rs
1    //! This crate provides a winit-based backend platform for imgui_supp-rs.
2    //!
3    //! A backend platform handles window/input device events and manages their
4    //! state.
5    //!
6    //! # Using the library
7    //!
8    //! There are five things you need to do to use this library correctly:
9    //!
10   //! 1. Initialize a `WinitPlatform` instance
11   //! 2. Attach it to a winit `Window`
12   //! 3. Pass events to the platform (every frame)
13   //! 4. Call frame preparation callback (every frame)
14   //! 5. Call render preparation callback (every frame)
15   //!
16   //! ## Complete example for winit 0.20+ (without a renderer)
17   //!
18   //! ```rust,no_run,ignore
19   //! # // TODO: Remove ignore when only one winit version is used
20   //! use imgui_supp::Context;
21   //! use imgui_winit_support::{HiDpiMode, WinitPlatform};
22   //! use std::time::Instant;
23   //! use winit::event::{Event, WindowEvent};
24   //! use winit::event_loop::{ControlFlow, EventLoop};
25   //! use winit::window::{Window};
26   //!
27   //! let mut event_loop = EventLoop::new();
28   //! let mut window = Window::new(&event_loop).unwrap();
29   //!
30   //! let mut imgui_supp = Context::create();
31   //! // configure imgui_supp-rs Context if necessary
32   //!
33   //! let mut platform = WinitPlatform::init(&mut imgui_supp); // step 1
34   //! platform.attach_window(imgui_supp.io_mut(), &window, HiDpiMode::Default); // step 2
35   //!
36   //! let mut last_frame = Instant::now();
37   //! let mut run = true;
38   //! event_loop.run(move |event, _, control_flow| {
39   //!     match event {
40   //!         Event::NewEvents(_) => {
41   //!             // other application-specific logic
42   //!             last_frame = imgui_supp.io_mut().update_delta_time(last_frame);
43   //!         },
44   //!         Event::MainEventsCleared => {
45   //!             // other application-specific logic
46   //!             platform.prepare_frame(imgui_supp.io_mut(), &window) // step 4
47   //!                 .expect("Failed to prepare frame");
48   //!             window.request_redraw();
49   //!         }
50   //!         Event::RedrawRequested(_) => {
51   //!             let ui = imgui_supp.frame();
52   //!             // application-specific rendering *under the UI*
53   //!
54   //!             // construct the UI
55   //!
56   //!             platform.prepare_render(&ui, &window); // step 5
57   //!             // render the UI with a renderer
58   //!             let draw_data = ui.render();
59   //!             // renderer.render(..., draw_data).expect("UI rendering failed");
60   //!
61   //!             // application-specific rendering *over the UI*
62   //!         },
63   //!         Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => {
64   //!             *control_flow = ControlFlow::Exit;
65   //!         }
66   //!         // other application-specific event handling
67   //!         event => {
68   //!             platform.handle_event(imgui_supp.io_mut(), &window, &event); // step 3
69   //!             // other application-specific event handling
70   //!         }
71   //!     }
72   //! })
73   //! ```
74   //!
75   //! ## `winit` versions and features.
76   //!
77   //! This crate has several features which control the version of winit which is
78   //! used.
79   //!
80   //! The following versions are supported, controlled by the listed feature.
81   //!
82   //! - The `winit-24` feature supports winit versions `0.24`. This is
83   //!   on by default, so to use any other version you need to disable this crates
84   //!   default features.
85   //! - The `winit-23` feature uses winit versions compatible with `0.23`.
86   //! - The `winit-22` feature uses winit versions compatible with `0.22`.
87   //! - The `winit-20` feature should support winit either `0.20` or winit `0.21`.
88   //! - The `winit-19` feature should support winits older than `0.19` (possibly
89   //!   back to winit 0.16.*, but this isn't regularly tested and may not work).
90   //!
91   //! If multiple `winit-*` features are enabled, and it is a debug build (as
92   //! determined by `debug_assertions`), we will log a warning to stderr during
93   //! init. This can be disabled by either turning on the `no-warn-on-multiple`
94   //! feature, fixing the configuration, or disabling `debug_assertions`.
95   //!
96   //! Conversely, if no `winit-*` features are enabled, we will fail to compile.
97   //! This is not an issue generally, as by default we turn on `winit-24`.
98   //!
99   //! All of this is in attempt to preserve the additive nature of features (while
100  //! still helping users notice project configuration issues), however it's done
101  //! fairly weakly as our this crate's API isn't 100% identical across winit
102  //! versions.
103  //!
104  //! ### Using an older `winit` version
105  //!
106  //! To use an older version, you must configure `default-features = false` in
107  //! your `Cargo.toml`:
108  //!
109  //! ```toml
110  //! [dependencies.imgui_supp-winit-support]
111  //! version = "0.6"
112  //! features = ["winit-$YOUR_VERSION_HERE"]
113  //! default-features = false
114  //! ```
115  //!
116  //! ### Old `winit` compatibility
117  //!
118  //! No guarantee is made on how long this crate will support legacy versions of
119  //! `winit`, but we'll try to follow these rules:
120  //!
121  //! - Versions which are still in widespread use in the ecosystem will be
122  //!   supported while that is true (for example, 0.19 at the time of writing is
123  //!   quite old, but used by the most recent version of several popular crates).
124  //!
125  //! - Versions which are not a significant maintenance burden will be supported
126  //!   (for example, supporting versions older than winit 0.19 given that we
127  //!   support 0.19).
128  //!
129  //! - Explicitly removing support for a feature-indicated version will be
130  //!   considered a breaking change.
131  //!
132  //! - Changing the default feature to the new latest `winit` version is *not* a
133  //!   breaking change.
134  
135  #[cfg(feature = "winit-24")]
136  use winit_24 as winit;
137  
138  #[cfg(all(not(feature = "winit-24"), feature = "winit-23"))]
139  use winit_23 as winit;
140  
141  #[cfg(all(
142  not(any(feature = "winit-24", feature = "winit-23")),
143  feature = "winit-22",
144  ))]
145  use winit_22 as winit;
146  
147  #[cfg(all(
148  not(any(feature = "winit-24", feature = "winit-23", feature = "winit-22")),
149  feature = "winit-20",
150  ))]
151  use winit_20 as winit;
152  
153  #[cfg(all(
154  not(any(
155  feature = "winit-24",
156  feature = "winit-23",
157  feature = "winit-22",
158  feature = "winit-20"
159  )),
160  feature = "winit-19",
161  ))]
162  use winit_19 as winit;
163  
164  use imgui::{self, BackendFlags, ConfigFlags, Context, ImString, Io, Key, Ui};
165  use std::cell::Cell;
166  use std::cmp::Ordering;
167  use winit::dpi::{LogicalPosition, LogicalSize};
168  
169  #[cfg(all(
170  not(any(
171  feature = "winit-24",
172  feature = "winit-23",
173  feature = "winit-22",
174  feature = "winit-20"
175  )),
176  feature = "winit-19",
177  ))]
178  use winit::{
179      DeviceEvent, ElementState, Event, KeyboardInput, MouseButton, MouseCursor, MouseScrollDelta,
180      TouchPhase, VirtualKeyCode, Window, WindowEvent,
181  };
182  
183  #[cfg(any(
184  feature = "winit-20",
185  feature = "winit-22",
186  feature = "winit-23",
187  feature = "winit-24"
188  ))]
189  use winit::{
190      error::ExternalError,
191      event::{
192          DeviceEvent, ElementState, Event, KeyboardInput, MouseButton, MouseScrollDelta, TouchPhase,
193          VirtualKeyCode, WindowEvent,
194      },
195      window::{CursorIcon as MouseCursor, Window},
196  };
197  use crate::owned_event::{OwnedEvent, OwnedWindowEvent};
198  
199  // Ensure at least one is enabled
200  #[cfg(not(any(
201  feature = "winit-19",
202  feature = "winit-20",
203  feature = "winit-22",
204  feature = "winit-23",
205  feature = "winit-24",
206  )))]
207  compile_error!("Please enable at least one version of `winit` (see documentation for details).");
208  
209  // FIXME(thom): make updading winit and adding a new feature less of a hassle here.
210  fn check_multiple_winits() {
211      use std::io::Write as _;
212      use std::sync::atomic::{AtomicBool, Ordering};
213      // bail out for release builds or if we've been explicitly disabled.
214      if cfg!(any(not(debug_assertions), feature = "no-warn-on-multiple")) {
215          return;
216      }
217      let winits_enabled = cfg!(feature = "winit-24") as usize
218          + cfg!(feature = "winit-23") as usize
219          + cfg!(feature = "winit-22") as usize
220          + cfg!(feature = "winit-20") as usize
221          + cfg!(feature = "winit-19") as usize;
222  
223      // Only complain once even if we're called multiple times.
224      static COMPLAINED: AtomicBool = AtomicBool::new(false);
225      // Note that the `Ordering` basically doesn't matter here, but even if it
226      // did, `Relaxed` is still correct because we're only interested in the
227      // effects on a single atomic variable.
228      if winits_enabled <= 1
229          || COMPLAINED
230          .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
231          .is_err()
232      {
233          return;
234      }
235      let mut err = Vec::with_capacity(512);
236  
237      // Log the complaint into a buffer first — in practice this is enough to
238      // ensure atomicity.
239      let _ = writeln!(
240          err,
241          "Warning (imgui_supp-winit-support): More than one `winit-*` version feature is enabled \ 
242          (this likely indicates misconfiguration, see documentation for details)."
243      );
244      let feats = [
245          ("winit-24", cfg!(feature = "winit-24"), " (default)"),
246          ("winit-23", cfg!(feature = "winit-23"), ""),
247          ("winit-22", cfg!(feature = "winit-22"), ""),
248          ("winit-20", cfg!(feature = "winit-20"), ""),
249          ("winit-19", cfg!(feature = "winit-19"), ""),
250      ];
251      for &(name, enabled, extra) in &feats {
252          if enabled {
253              let _ = writeln!(err, "    `feature = {:?}` is enabled{}", name, extra);
254          }
255      }
256      if cfg!(feature = "winit-24") && winits_enabled == 2 {
257          let _ = writeln!(
258              err,
259              "    Perhaps you are missing a `default-features = false`?",
260          );
261      }
262      let _ = writeln!(
263          err,
264          "    (Note: This warning is only present in debug builds, and \ 
265          can be disabled using the \"no-warn-on-multiple\" feature)"
266      );
267      let _ = std::io::stderr().write_all(&err);
268  }
269  
270  /// State of a single mouse button. Used so that we can detect cases where mouse
271  /// press and release occur on the same frame (seems surprisingly frequent on
272  /// macOS now...)
273  #[derive(Debug, Clone, Default)]
274  struct Button {
275      pressed_this_frame: Cell<bool>,
276      state: Cell<bool>,
277  }
278  
279  impl Button {
280      // we can use this in an array initializer, unlike `Default::default()` or a
281      // `const fn new()`.
282      #[allow(clippy::declare_interior_mutable_const)]
283      const INIT: Button = Self {
284          pressed_this_frame: Cell::new(false),
285          state: Cell::new(false),
286      };
287      fn set(&self, pressed: bool) {
288          self.state.set(pressed);
289          if pressed {
290              self.pressed_this_frame.set(true);
291          }
292      }
293      fn get(&self) -> bool {
294          // If we got a press this frame, record it even if we got a release
295          // too — this way we don't drop mouse clicks where the release comes
296          // in on the same frame as the press. (This mirrors what Dear ImGUI
297          // seems to do in the `imgui_impl_*`)
298          self.pressed_this_frame.replace(false) || self.state.get()
299      }
300  }
301  
302  /// winit backend platform state
303  #[derive(Debug)]
304  pub struct WinitPlatform {
305      hidpi_mode: ActiveHiDpiMode,
306      hidpi_factor: f64,
307      cursor_cache: Option<CursorSettings>,
308      mouse_buttons: [Button; 5],
309  }
310  
311  #[derive(Debug, Copy, Clone, Eq, PartialEq)]
312  struct CursorSettings {
313      cursor: Option<imgui::MouseCursor>,
314      draw_cursor: bool,
315  }
316  
317  fn to_winit_cursor(cursor: imgui::MouseCursor) -> MouseCursor {
318      match cursor {
319          imgui::MouseCursor::Arrow => MouseCursor::Default,
320          imgui::MouseCursor::TextInput => MouseCursor::Text,
321          imgui::MouseCursor::ResizeAll => MouseCursor::Move,
322          imgui::MouseCursor::ResizeNS => MouseCursor::NsResize,
323          imgui::MouseCursor::ResizeEW => MouseCursor::EwResize,
324          imgui::MouseCursor::ResizeNESW => MouseCursor::NeswResize,
325          imgui::MouseCursor::ResizeNWSE => MouseCursor::NwseResize,
326          imgui::MouseCursor::Hand => MouseCursor::Hand,
327          imgui::MouseCursor::NotAllowed => MouseCursor::NotAllowed,
328      }
329  }
330  
331  impl CursorSettings {
332      #[cfg(all(
333      not(any(
334      feature = "winit-24",
335      feature = "winit-23",
336      feature = "winit-22",
337      feature = "winit-20"
338      )),
339      feature = "winit-19",
340      ))]
341      fn apply(&self, window: &Window) {
342          match self.cursor {
343              Some(mouse_cursor) if !self.draw_cursor => {
344                  window.hide_cursor(false);
345                  window.set_cursor(to_winit_cursor(mouse_cursor));
346              }
347              _ => window.hide_cursor(true),
348          }
349      }
350      #[cfg(any(
351      feature = "winit-20",
352      feature = "winit-22",
353      feature = "winit-23",
354      feature = "winit-24"
355      ))]
356      fn apply(&self, window: &Window) {
357          match self.cursor {
358              Some(mouse_cursor) if !self.draw_cursor => {
359                  window.set_cursor_visible(true);
360                  window.set_cursor_icon(to_winit_cursor(mouse_cursor));
361              }
362              _ => window.set_cursor_visible(false),
363          }
364      }
365  }
366  
367  #[derive(Copy, Clone, Debug, PartialEq)]
368  enum ActiveHiDpiMode {
369      Default,
370      Rounded,
371      Locked,
372  }
373  
374  /// DPI factor handling mode.
375  ///
376  /// Applications that use imgui_supp-rs might want to customize the used DPI factor and not use
377  /// directly the value coming from winit.
378  ///
379  /// **Note: if you use a mode other than default and the DPI factor is adjusted, winit and imgui_supp-rs
380  /// will use different logical coordinates, so be careful if you pass around logical size or
381  /// position values.**
382  #[derive(Copy, Clone, Debug, PartialEq)]
383  pub enum HiDpiMode {
384      /// The DPI factor from winit is used directly without adjustment
385      Default,
386      /// The DPI factor from winit is rounded to an integer value.
387      ///
388      /// This prevents the user interface from becoming blurry with non-integer scaling.
389      Rounded,
390      /// The DPI factor from winit is ignored, and the included value is used instead.
391      ///
392      /// This is useful if you want to force some DPI factor (e.g. 1.0) and not care about the value
393      /// coming from winit.
394      Locked(f64),
395  }
396  
397  impl HiDpiMode {
398      fn apply(&self, hidpi_factor: f64) -> (ActiveHiDpiMode, f64) {
399          match *self {
400              HiDpiMode::Default => (ActiveHiDpiMode::Default, hidpi_factor),
401              HiDpiMode::Rounded => (ActiveHiDpiMode::Rounded, hidpi_factor.round()),
402              HiDpiMode::Locked(value) => (ActiveHiDpiMode::Locked, value),
403          }
404      }
405  }
406  
407  impl WinitPlatform {
408      /// Initializes a winit platform instance and configures imgui_supp.
409      ///
410      /// This function configures imgui_supp-rs in the following ways:
411      ///
412      /// * backend flags are updated
413      /// * keys are configured
414      /// * platform name is set
415      pub fn init(imgui: &mut Context) -> WinitPlatform {
416          // noop in non-debug builds, if disabled, or if called a second time.
417          check_multiple_winits();
418          let io = imgui.io_mut();
419          io.backend_flags.insert(BackendFlags::HAS_MOUSE_CURSORS);
420          io.backend_flags.insert(BackendFlags::HAS_SET_MOUSE_POS);
421          io[Key::Tab] = VirtualKeyCode::Tab as _;
422          io[Key::LeftArrow] = VirtualKeyCode::Left as _;
423          io[Key::RightArrow] = VirtualKeyCode::Right as _;
424          io[Key::UpArrow] = VirtualKeyCode::Up as _;
425          io[Key::DownArrow] = VirtualKeyCode::Down as _;
426          io[Key::PageUp] = VirtualKeyCode::PageUp as _;
427          io[Key::PageDown] = VirtualKeyCode::PageDown as _;
428          io[Key::Home] = VirtualKeyCode::Home as _;
429          io[Key::End] = VirtualKeyCode::End as _;
430          io[Key::Insert] = VirtualKeyCode::Insert as _;
431          io[Key::Delete] = VirtualKeyCode::Delete as _;
432          io[Key::Backspace] = VirtualKeyCode::Back as _;
433          io[Key::Space] = VirtualKeyCode::Space as _;
434          io[Key::Enter] = VirtualKeyCode::Return as _;
435          io[Key::Escape] = VirtualKeyCode::Escape as _;
436          io[Key::KeyPadEnter] = VirtualKeyCode::NumpadEnter as _;
437          io[Key::A] = VirtualKeyCode::A as _;
438          io[Key::C] = VirtualKeyCode::C as _;
439          io[Key::V] = VirtualKeyCode::V as _;
440          io[Key::X] = VirtualKeyCode::X as _;
441          io[Key::Y] = VirtualKeyCode::Y as _;
442          io[Key::Z] = VirtualKeyCode::Z as _;
443          imgui.set_platform_name(Some(ImString::from(format!(
444              "imgui_supp-winit-support {}",
445              env!("CARGO_PKG_VERSION")
446          ))));
447          WinitPlatform {
448              hidpi_mode: ActiveHiDpiMode::Default,
449              hidpi_factor: 1.0,
450              cursor_cache: None,
451              mouse_buttons: [Button::INIT; 5],
452          }
453      }
454      /// Attaches the platform instance to a winit window.
455      ///
456      /// This function configures imgui_supp-rs in the following ways:
457      ///
458      /// * framebuffer scale (= DPI factor) is set
459      /// * display size is set
460      #[cfg(all(
461      not(any(
462      feature = "winit-24",
463      feature = "winit-23",
464      feature = "winit-22",
465      feature = "winit-20"
466      )),
467      feature = "winit-19",
468      ))]
469      pub fn attach_window(&mut self, io: &mut Io, window: &Window, hidpi_mode: HiDpiMode) {
470          let (hidpi_mode, hidpi_factor) = hidpi_mode.apply(window.get_hidpi_factor());
471          self.hidpi_mode = hidpi_mode;
472          self.hidpi_factor = hidpi_factor;
473          io.display_framebuffer_scale = [hidpi_factor as f32, hidpi_factor as f32];
474          if let Some(logical_size) = window.get_inner_size() {
475              let logical_size = self.scale_size_from_winit(window, logical_size);
476              io.display_size = [logical_size.width as f32, logical_size.height as f32];
477          }
478      }
479      /// Attaches the platform instance to a winit window.
480      ///
481      /// This function configures imgui_supp-rs in the following ways:
482      ///
483      /// * framebuffer scale (= DPI factor) is set
484      /// * display size is set
485      #[cfg(any(
486      feature = "winit-20",
487      feature = "winit-22",
488      feature = "winit-23",
489      feature = "winit-24"
490      ))]
491      pub fn attach_window(&mut self, io: &mut Io, window: &Window, hidpi_mode: HiDpiMode) {
492          let (hidpi_mode, hidpi_factor) = hidpi_mode.apply(window.scale_factor());
493          self.hidpi_mode = hidpi_mode;
494          self.hidpi_factor = hidpi_factor;
495          io.display_framebuffer_scale = [hidpi_factor as f32, hidpi_factor as f32];
496          let logical_size = window.inner_size().to_logical(hidpi_factor);
497          let logical_size = self.scale_size_from_winit(window, logical_size);
498          io.display_size = [logical_size.width as f32, logical_size.height as f32];
499      }
500      /// Returns the current DPI factor.
501      ///
502      /// The value might not be the same as the winit DPI factor (depends on the used DPI mode)
503      pub fn hidpi_factor(&self) -> f64 {
504          self.hidpi_factor
505      }
506      /// Scales a logical size coming from winit using the current DPI mode.
507      ///
508      /// This utility function is useful if you are using a DPI mode other than default, and want
509      /// your application to use the same logical coordinates as imgui_supp-rs.
510      #[cfg(all(
511      not(any(
512      feature = "winit-24",
513      feature = "winit-23",
514      feature = "winit-22",
515      feature = "winit-20"
516      )),
517      feature = "winit-19",
518      ))]
519      pub fn scale_size_from_winit(&self, window: &Window, logical_size: LogicalSize) -> LogicalSize {
520          match self.hidpi_mode {
521              ActiveHiDpiMode::Default => logical_size,
522              _ => logical_size
523                  .to_physical(window.get_hidpi_factor())
524                  .to_logical(self.hidpi_factor),
525          }
526      }
527      /// Scales a logical size coming from winit using the current DPI mode.
528      ///
529      /// This utility function is useful if you are using a DPI mode other than default, and want
530      /// your application to use the same logical coordinates as imgui_supp-rs.
531      #[cfg(any(
532      feature = "winit-20",
533      feature = "winit-22",
534      feature = "winit-23",
535      feature = "winit-24"
536      ))]
537      pub fn scale_size_from_winit(
538          &self,
539          window: &Window,
540          logical_size: LogicalSize<f64>,
541      ) -> LogicalSize<f64> {
542          match self.hidpi_mode {
543              ActiveHiDpiMode::Default => logical_size,
544              _ => logical_size
545                  .to_physical::<f64>(window.scale_factor())
546                  .to_logical(self.hidpi_factor),
547          }
548      }
549      /// Scales a logical position coming from winit using the current DPI mode.
550      ///
551      /// This utility function is useful if you are using a DPI mode other than default, and want
552      /// your application to use the same logical coordinates as imgui_supp-rs.
553      #[cfg(all(
554      not(any(
555      feature = "winit-24",
556      feature = "winit-23",
557      feature = "winit-22",
558      feature = "winit-20"
559      )),
560      feature = "winit-19",
561      ))]
562      pub fn scale_pos_from_winit(
563          &self,
564          window: &Window,
565          logical_pos: LogicalPosition,
566      ) -> LogicalPosition {
567          match self.hidpi_mode {
568              ActiveHiDpiMode::Default => logical_pos,
569              _ => logical_pos
570                  .to_physical(window.get_hidpi_factor())
571                  .to_logical(self.hidpi_factor),
572          }
573      }
574      /// Scales a logical position coming from winit using the current DPI mode.
575      ///
576      /// This utility function is useful if you are using a DPI mode other than default, and want
577      /// your application to use the same logical coordinates as imgui_supp-rs.
578      #[cfg(any(
579      feature = "winit-20",
580      feature = "winit-22",
581      feature = "winit-23",
582      feature = "winit-24"
583      ))]
584      pub fn scale_pos_from_winit(
585          &self,
586          window: &Window,
587          logical_pos: LogicalPosition<f64>,
588      ) -> LogicalPosition<f64> {
589          match self.hidpi_mode {
590              ActiveHiDpiMode::Default => logical_pos,
591              _ => logical_pos
592                  .to_physical::<f64>(window.scale_factor())
593                  .to_logical(self.hidpi_factor),
594          }
595      }
596      /// Scales a logical position for winit using the current DPI mode.
597      ///
598      /// This utility function is useful if you are using a DPI mode other than default, and want
599      /// your application to use the same logical coordinates as imgui_supp-rs.
600      #[cfg(all(
601      not(any(
602      feature = "winit-24",
603      feature = "winit-23",
604      feature = "winit-22",
605      feature = "winit-20"
606      )),
607      feature = "winit-19",
608      ))]
609      pub fn scale_pos_for_winit(
610          &self,
611          window: &Window,
612          logical_pos: LogicalPosition,
613      ) -> LogicalPosition {
614          match self.hidpi_mode {
615              ActiveHiDpiMode::Default => logical_pos,
616              _ => logical_pos
617                  .to_physical(self.hidpi_factor)
618                  .to_logical(window.get_hidpi_factor()),
619          }
620      }
621      /// Scales a logical position for winit using the current DPI mode.
622      ///
623      /// This utility function is useful if you are using a DPI mode other than default, and want
624      /// your application to use the same logical coordinates as imgui_supp-rs.
625      #[cfg(any(
626      feature = "winit-20",
627      feature = "winit-22",
628      feature = "winit-23",
629      feature = "winit-24"
630      ))]
631      pub fn scale_pos_for_winit(
632          &self,
633          window: &Window,
634          logical_pos: LogicalPosition<f64>,
635      ) -> LogicalPosition<f64> {
636          match self.hidpi_mode {
637              ActiveHiDpiMode::Default => logical_pos,
638              _ => logical_pos
639                  .to_physical::<f64>(self.hidpi_factor)
640                  .to_logical(window.scale_factor()),
641          }
642      }
643      /// Handles a winit event.
644      ///
645      /// This function performs the following actions (depends on the event):
646      ///
647      /// * window size / dpi factor changes are applied
648      /// * keyboard state is updated
649      /// * mouse state is updated
650      #[cfg(all(
651      not(any(
652      feature = "winit-24",
653      feature = "winit-23",
654      feature = "winit-22",
655      feature = "winit-20"
656      )),
657      feature = "winit-19",
658      ))]
659      pub fn handle_event(&mut self, io: &mut Io, window: &Window, event: &Event) {
660          match *event {
661              Event::WindowEvent {
662                  window_id,
663                  ref event,
664              } if window_id == window.id() => {
665                  self.handle_window_event(io, window, event);
666              }
667              // Track key release events outside our window. If we don't do this,
668              // we might never see the release event if some other window gets focus.
669              Event::DeviceEvent {
670                  event:
671                  DeviceEvent::Key(KeyboardInput {
672                                       state: ElementState::Released,
673                                       virtual_keycode: Some(key),
674                                       ..
675                                   }),
676                  ..
677              } => {
678                  io.keys_down[key as usize] = false;
679                  match key {
680                      VirtualKeyCode::LShift | VirtualKeyCode::RShift => io.key_shift = false,
681                      VirtualKeyCode::LControl | VirtualKeyCode::RControl => io.key_ctrl = false,
682                      VirtualKeyCode::LAlt | VirtualKeyCode::RAlt => io.key_alt = false,
683                      VirtualKeyCode::LWin | VirtualKeyCode::RWin => io.key_super = false,
684                      _ => (),
685                  }
686              }
687              _ => (),
688          }
689      }
690      /// Handles a winit event.
691      ///
692      /// This function performs the following actions (depends on the event):
693      ///
694      /// * window size / dpi factor changes are applied
695      /// * keyboard state is updated
696      /// * mouse state is updated
697      #[cfg(all(
698      not(any(feature = "winit-24", feature = "winit-23", feature = "winit-22")),
699      feature = "winit-20",
700      ))]
701      pub fn handle_event<T>(&mut self, io: &mut Io, window: &Window, event: &Event<T>) {
702          match *event {
703              Event::WindowEvent {
704                  window_id,
705                  ref event,
706              } if window_id == window.id() => {
707                  self.handle_window_event(io, window, event);
708              }
709              // Track key release events outside our window. If we don't do this,
710              // we might never see the release event if some other window gets focus.
711              Event::DeviceEvent {
712                  event:
713                  DeviceEvent::Key(KeyboardInput {
714                                       state: ElementState::Released,
715                                       virtual_keycode: Some(key),
716                                       ..
717                                   }),
718                  ..
719              } => {
720                  io.keys_down[key as usize] = false;
721              }
722  
723              // We need to track modifiers separately because some system like macOS, will
724              // not reliably send modifier states during certain events like ScreenCapture.
725              // Gotta let the people show off their pretty imgui_supp widgets!
726              Event::DeviceEvent {
727                  event: DeviceEvent::ModifiersChanged(modifiers),
728                  ..
729              } => {
730                  io.key_shift = modifiers.shift();
731                  io.key_ctrl = modifiers.ctrl();
732                  io.key_alt = modifiers.alt();
733                  io.key_super = modifiers.logo();
734              }
735              _ => (),
736          }
737      }
738      /// Handles a winit event.
739      ///
740      /// This function performs the following actions (depends on the event):
741      ///
742      /// * window size / dpi factor changes are applied
743      /// * keyboard state is updated
744      /// * mouse state is updated
745      #[cfg(any(feature = "winit-22", feature = "winit-23", feature = "winit-24"))]
746      pub fn handle_event<T>(&mut self, io: &mut Io, window: &Window, event: &OwnedEvent<T>) {
747          match *event {
748              OwnedEvent::WindowEvent {
749                  window_id,
750                  ref event,
751              } if window_id == window.id() => {
752                  // We need to track modifiers separately because some system like macOS, will
753                  // not reliably send modifier states during certain events like ScreenCapture.
754                  // Gotta let the people show off their pretty imgui_supp widgets!
755                  if let OwnedWindowEvent::ModifiersChanged(modifiers) = event {
756                      io.key_shift = modifiers.shift();
757                      io.key_ctrl = modifiers.ctrl();
758                      io.key_alt = modifiers.alt();
759                      io.key_super = modifiers.logo();
760                  }
761  
762                  self.handle_window_event(io, window, event);
763              }
764              // Track key release events outside our window. If we don't do this,
765              // we might never see the release event if some other window gets focus.
766              OwnedEvent::DeviceEvent {
767                  event:
768                  DeviceEvent::Key(KeyboardInput {
769                                       state: ElementState::Released,
770                                       virtual_keycode: Some(key),
771                                       ..
772                                   }),
773                  ..
774              } => {
775                  io.keys_down[key as usize] = false;
776              }
777              _ => (),
778          }
779      }
780      #[cfg(all(
781      not(any(
782      feature = "winit-24",
783      feature = "winit-23",
784      feature = "winit-22",
785      feature = "winit-20"
786      )),
787      feature = "winit-19",
788      ))]
789      fn handle_window_event(&mut self, io: &mut Io, window: &Window, event: &WindowEvent) {
790          match *event {
791              WindowEvent::Resized(logical_size) => {
792                  let logical_size = self.scale_size_from_winit(window, logical_size);
793                  io.display_size = [logical_size.width as f32, logical_size.height as f32];
794              }
795              WindowEvent::HiDpiFactorChanged(scale) => {
796                  let hidpi_factor = match self.hidpi_mode {
797                      ActiveHiDpiMode::Default => scale,
798                      ActiveHiDpiMode::Rounded => scale.round(),
799                      _ => return,
800                  };
801                  // Mouse position needs to be changed while we still have both the old and the new
802                  // values
803                  if io.mouse_pos[0].is_finite() && io.mouse_pos[1].is_finite() {
804                      io.mouse_pos = [
805                          io.mouse_pos[0] * (hidpi_factor / self.hidpi_factor) as f32,
806                          io.mouse_pos[1] * (hidpi_factor / self.hidpi_factor) as f32,
807                      ];
808                  }
809                  self.hidpi_factor = hidpi_factor;
810                  io.display_framebuffer_scale = [hidpi_factor as f32, hidpi_factor as f32];
811                  // Window size might change too if we are using DPI rounding
812                  if let Some(logical_size) = window.get_inner_size() {
813                      let logical_size = self.scale_size_from_winit(window, logical_size);
814                      io.display_size = [logical_size.width as f32, logical_size.height as f32];
815                  }
816              }
817              WindowEvent::KeyboardInput {
818                  input:
819                  KeyboardInput {
820                      virtual_keycode: Some(key),
821                      state,
822                      ..
823                  },
824                  ..
825              } => {
826                  io.keys_down[key as usize] = state == ElementState::Pressed;
827              }
828              WindowEvent::ReceivedCharacter(ch) => {
829                  // Exclude the backspace key ('\u{7f}'). Otherwise we will insert this char and then
830                  // delete it.
831                  if ch != '\u{7f}' {
832                      io.add_input_character(ch)
833                  }
834              }
835              WindowEvent::CursorMoved { position, .. } => {
836                  let position = self.scale_pos_from_winit(window, position);
837                  io.mouse_pos = [position.x as f32, position.y as f32];
838              }
839              WindowEvent::MouseWheel {
840                  delta,
841                  phase: TouchPhase::Moved,
842                  ..
843              } => match delta {
844                  MouseScrollDelta::LineDelta(h, v) => {
845                      io.mouse_wheel_h = h;
846                      io.mouse_wheel = v;
847                  }
848                  MouseScrollDelta::PixelDelta(pos) => {
849                      match pos.x.partial_cmp(&0.0) {
850                          Some(Ordering::Greater) => io.mouse_wheel_h += 1.0,
851                          Some(Ordering::Less) => io.mouse_wheel_h -= 1.0,
852                          _ => (),
853                      }
854                      match pos.y.partial_cmp(&0.0) {
855                          Some(Ordering::Greater) => io.mouse_wheel += 1.0,
856                          Some(Ordering::Less) => io.mouse_wheel -= 1.0,
857                          _ => (),
858                      }
859                  }
860              },
861              WindowEvent::MouseInput { state, button, .. } => {
862                  let pressed = state == ElementState::Pressed;
863                  match button {
864                      MouseButton::Left => self.mouse_buttons[0].set(pressed),
865                      MouseButton::Right => self.mouse_buttons[1].set(pressed),
866                      MouseButton::Middle => self.mouse_buttons[2].set(pressed),
867                      MouseButton::Other(idx @ 0..=4) => {
868                          self.mouse_buttons[idx as usize].set(pressed)
869                      }
870                      _ => (),
871                  }
872              }
873              _ => (),
874          }
875      }
876      #[cfg(all(
877      not(any(feature = "winit-23", feature = "winit-24")),
878      any(feature = "winit-20", feature = "winit-22")
879      ))]
880      fn handle_window_event(&mut self, io: &mut Io, window: &Window, event: &WindowEvent) {
881          match *event {
882              WindowEvent::Resized(physical_size) => {
883                  let logical_size = physical_size.to_logical(window.scale_factor());
884                  let logical_size = self.scale_size_from_winit(window, logical_size);
885                  io.display_size = [logical_size.width as f32, logical_size.height as f32];
886              }
887              WindowEvent::ScaleFactorChanged { scale_factor, .. } => {
888                  let hidpi_factor = match self.hidpi_mode {
889                      ActiveHiDpiMode::Default => scale_factor,
890                      ActiveHiDpiMode::Rounded => scale_factor.round(),
891                      _ => return,
892                  };
893                  // Mouse position needs to be changed while we still have both the old and the new
894                  // values
895                  if io.mouse_pos[0].is_finite() && io.mouse_pos[1].is_finite() {
896                      io.mouse_pos = [
897                          io.mouse_pos[0] * (hidpi_factor / self.hidpi_factor) as f32,
898                          io.mouse_pos[1] * (hidpi_factor / self.hidpi_factor) as f32,
899                      ];
900                  }
901                  self.hidpi_factor = hidpi_factor;
902                  io.display_framebuffer_scale = [hidpi_factor as f32, hidpi_factor as f32];
903                  // Window size might change too if we are using DPI rounding
904                  let logical_size = window.inner_size().to_logical(scale_factor);
905                  let logical_size = self.scale_size_from_winit(window, logical_size);
906                  io.display_size = [logical_size.width as f32, logical_size.height as f32];
907              }
908              WindowEvent::KeyboardInput {
909                  input:
910                  KeyboardInput {
911                      virtual_keycode: Some(key),
912                      state,
913                      ..
914                  },
915                  ..
916              } => {
917                  let pressed = state == ElementState::Pressed;
918                  io.keys_down[key as usize] = pressed;
919  
920                  // This is a bit redundant here, but we'll leave it in. The OS occasionally
921                  // fails to send modifiers keys, but it doesn't seem to send false-positives,
922                  // so double checking isn't terrible in case some system *doesn't* send
923                  // device events sometimes.
924                  match key {
925                      VirtualKeyCode::LShift | VirtualKeyCode::RShift => io.key_shift = pressed,
926                      VirtualKeyCode::LControl | VirtualKeyCode::RControl => io.key_ctrl = pressed,
927                      VirtualKeyCode::LAlt | VirtualKeyCode::RAlt => io.key_alt = pressed,
928                      VirtualKeyCode::LWin | VirtualKeyCode::RWin => io.key_super = pressed,
929                      _ => (),
930                  }
931              }
932              WindowEvent::ReceivedCharacter(ch) => {
933                  // Exclude the backspace key ('\u{7f}'). Otherwise we will insert this char and then
934                  // delete it.
935                  if ch != '\u{7f}' {
936                      io.add_input_character(ch)
937                  }
938              }
939              WindowEvent::CursorMoved { position, .. } => {
940                  let position = position.to_logical(window.scale_factor());
941                  let position = self.scale_pos_from_winit(window, position);
942                  io.mouse_pos = [position.x as f32, position.y as f32];
943              }
944              WindowEvent::MouseWheel {
945                  delta,
946                  phase: TouchPhase::Moved,
947                  ..
948              } => match delta {
949                  MouseScrollDelta::LineDelta(h, v) => {
950                      io.mouse_wheel_h = h;
951                      io.mouse_wheel = v;
952                  }
953                  MouseScrollDelta::PixelDelta(pos) => {
954                      match pos.x.partial_cmp(&0.0) {
955                          Some(Ordering::Greater) => io.mouse_wheel_h += 1.0,
956                          Some(Ordering::Less) => io.mouse_wheel_h -= 1.0,
957                          _ => (),
958                      }
959                      match pos.y.partial_cmp(&0.0) {
960                          Some(Ordering::Greater) => io.mouse_wheel += 1.0,
961                          Some(Ordering::Less) => io.mouse_wheel -= 1.0,
962                          _ => (),
963                      }
964                  }
965              },
966              WindowEvent::MouseInput { state, button, .. } => {
967                  let pressed = state == ElementState::Pressed;
968                  match button {
969                      MouseButton::Left => self.mouse_buttons[0].set(pressed),
970                      MouseButton::Right => self.mouse_buttons[1].set(pressed),
971                      MouseButton::Middle => self.mouse_buttons[2].set(pressed),
972                      MouseButton::Other(idx @ 0..=4) => {
973                          self.mouse_buttons[idx as usize].set(pressed)
974                      }
975                      _ => (),
976                  }
977              }
978              _ => (),
979          }
980      }
981  
982      #[cfg(any(feature = "winit-23", feature = "winit-24"))]
983      fn handle_window_event(&mut self, io: &mut Io, window: &Window, event: &OwnedWindowEvent) {
984          match *event {
985              OwnedWindowEvent::Resized(physical_size) => {
986                  let logical_size = physical_size.to_logical(window.scale_factor());
987                  let logical_size = self.scale_size_from_winit(window, logical_size);
988                  io.display_size = [logical_size.width as f32, logical_size.height as f32];
989              }
990              OwnedWindowEvent::ScaleFactorChanged { scale_factor, .. } => {
991                  let hidpi_factor = match self.hidpi_mode {
992                      ActiveHiDpiMode::Default => scale_factor,
993                      ActiveHiDpiMode::Rounded => scale_factor.round(),
994                      _ => return,
995                  };
996                  // Mouse position needs to be changed while we still have both the old and the new
997                  // values
998                  if io.mouse_pos[0].is_finite() && io.mouse_pos[1].is_finite() {
999                      io.mouse_pos = [
1000                         io.mouse_pos[0] * (hidpi_factor / self.hidpi_factor) as f32,
1001                         io.mouse_pos[1] * (hidpi_factor / self.hidpi_factor) as f32,
1002                     ];
1003                 }
1004                 self.hidpi_factor = hidpi_factor;
1005                 io.display_framebuffer_scale = [hidpi_factor as f32, hidpi_factor as f32];
1006                 // Window size might change too if we are using DPI rounding
1007                 let logical_size = window.inner_size().to_logical(scale_factor);
1008                 let logical_size = self.scale_size_from_winit(window, logical_size);
1009                 io.display_size = [logical_size.width as f32, logical_size.height as f32];
1010             }
1011             OwnedWindowEvent::KeyboardInput {
1012                 input:
1013                 KeyboardInput {
1014                     virtual_keycode: Some(key),
1015                     state,
1016                     ..
1017                 },
1018                 ..
1019             } => {
1020                 let pressed = state == ElementState::Pressed;
1021                 io.keys_down[key as usize] = pressed;
1022 
1023                 // This is a bit redundant here, but we'll leave it in. The OS occasionally
1024                 // fails to send modifiers keys, but it doesn't seem to send false-positives,
1025                 // so double checking isn't terrible in case some system *doesn't* send
1026                 // device events sometimes.
1027                 match key {
1028                     VirtualKeyCode::LShift | VirtualKeyCode::RShift => io.key_shift = pressed,
1029                     VirtualKeyCode::LControl | VirtualKeyCode::RControl => io.key_ctrl = pressed,
1030                     VirtualKeyCode::LAlt | VirtualKeyCode::RAlt => io.key_alt = pressed,
1031                     VirtualKeyCode::LWin | VirtualKeyCode::RWin => io.key_super = pressed,
1032                     _ => (),
1033                 }
1034             }
1035             OwnedWindowEvent::ReceivedCharacter(ch) => {
1036                 // Exclude the backspace key ('\u{7f}'). Otherwise we will insert this char and then
1037                 // delete it.
1038                 if ch != '\u{7f}' {
1039                     io.add_input_character(ch)
1040                 }
1041             }
1042             OwnedWindowEvent::CursorMoved { position, .. } => {
1043                 let position = position.to_logical(window.scale_factor());
1044                 let position = self.scale_pos_from_winit(window, position);
1045                 io.mouse_pos = [position.x as f32, position.y as f32];
1046             }
1047             OwnedWindowEvent::MouseWheel {
1048                 delta,
1049                 phase: TouchPhase::Moved,
1050                 ..
1051             } => match delta {
1052                 MouseScrollDelta::LineDelta(h, v) => {
1053                     io.mouse_wheel_h = h;
1054                     io.mouse_wheel = v;
1055                 }
1056                 MouseScrollDelta::PixelDelta(pos) => {
1057                     let pos = pos.to_logical::<f64>(self.hidpi_factor);
1058                     match pos.x.partial_cmp(&0.0) {
1059                         Some(Ordering::Greater) => io.mouse_wheel_h += 1.0,
1060                         Some(Ordering::Less) => io.mouse_wheel_h -= 1.0,
1061                         _ => (),
1062                     }
1063                     match pos.y.partial_cmp(&0.0) {
1064                         Some(Ordering::Greater) => io.mouse_wheel += 1.0,
1065                         Some(Ordering::Less) => io.mouse_wheel -= 1.0,
1066                         _ => (),
1067                     }
1068                 }
1069             },
1070             OwnedWindowEvent::MouseInput { state, button, .. } => {
1071                 let pressed = state == ElementState::Pressed;
1072                 match button {
1073                     MouseButton::Left => self.mouse_buttons[0].set(pressed),
1074                     MouseButton::Right => self.mouse_buttons[1].set(pressed),
1075                     MouseButton::Middle => self.mouse_buttons[2].set(pressed),
1076                     MouseButton::Other(idx @ 0..=4) => {
1077                         self.mouse_buttons[idx as usize].set(pressed)
1078                     }
1079                     _ => (),
1080                 }
1081             }
1082             _ => (),
1083         }
1084     }
1085     /// Frame preparation callback.
1086     ///
1087     /// Call this before calling the imgui_supp-rs context `frame` function.
1088     /// This function performs the following actions:
1089     ///
1090     /// * mouse cursor is repositioned (if requested by imgui_supp-rs)
1091     #[cfg(all(
1092     not(any(
1093     feature = "winit-24",
1094     feature = "winit-23",
1095     feature = "winit-22",
1096     feature = "winit-20"
1097     )),
1098     feature = "winit-19",
1099     ))]
1100     pub fn prepare_frame(&self, io: &mut Io, window: &Window) -> Result<(), String> {
1101         self.copy_mouse_to_io(&mut io.mouse_down);
1102         if io.want_set_mouse_pos {
1103             let logical_pos = self.scale_pos_for_winit(
1104                 window,
1105                 LogicalPosition::new(f64::from(io.mouse_pos[0]), f64::from(io.mouse_pos[1])),
1106             );
1107             window.set_cursor_position(logical_pos)
1108         } else {
1109             Ok(())
1110         }
1111     }
1112     /// Frame preparation callback.
1113     ///
1114     /// Call this before calling the imgui_supp-rs context `frame` function.
1115     /// This function performs the following actions:
1116     ///
1117     /// * mouse cursor is repositioned (if requested by imgui_supp-rs)
1118     #[cfg(any(
1119     feature = "winit-20",
1120     feature = "winit-22",
1121     feature = "winit-23",
1122     feature = "winit-24"
1123     ))]
1124     pub fn prepare_frame(&self, io: &mut Io, window: &Window) -> Result<(), ExternalError> {
1125         self.copy_mouse_to_io(&mut io.mouse_down);
1126         if io.want_set_mouse_pos {
1127             let logical_pos = self.scale_pos_for_winit(
1128                 window,
1129                 LogicalPosition::new(f64::from(io.mouse_pos[0]), f64::from(io.mouse_pos[1])),
1130             );
1131             window.set_cursor_position(logical_pos)
1132         } else {
1133             Ok(())
1134         }
1135     }
1136 
1137     fn copy_mouse_to_io(&self, io_mouse_down: &mut [bool]) {
1138         for (io_down, button) in io_mouse_down.iter_mut().zip(&self.mouse_buttons) {
1139             *io_down = button.get();
1140         }
1141     }
1142 
1143     /// Render preparation callback.
1144     ///
1145     /// Call this before calling the imgui_supp-rs UI `render_with`/`render` function.
1146     /// This function performs the following actions:
1147     ///
1148     /// * mouse cursor is changed and/or hidden (if requested by imgui_supp-rs)
1149     pub fn prepare_render(&mut self, ui: &Ui, window: &Window) {
1150         let io = ui.io();
1151         if !io
1152             .config_flags
1153             .contains(ConfigFlags::NO_MOUSE_CURSOR_CHANGE)
1154         {
1155             let cursor = CursorSettings {
1156                 cursor: ui.mouse_cursor(),
1157                 draw_cursor: io.mouse_draw_cursor,
1158             };
1159             if self.cursor_cache != Some(cursor) {
1160                 cursor.apply(window);
1161                 self.cursor_cache = Some(cursor);
1162             }
1163         }
1164     }
1165 }
1166