lambda_platform/winit/
mod.rs

1//! Winit wrapper to easily construct cross platform windows
2
3use winit::{
4  dpi::{
5    LogicalSize,
6    PhysicalSize,
7  },
8  event::Event,
9  event_loop::{
10    ControlFlow,
11    EventLoop,
12    EventLoopBuilder,
13    EventLoopProxy,
14    EventLoopWindowTarget,
15  },
16  monitor::MonitorHandle,
17  window::{
18    Window,
19    WindowBuilder,
20  },
21};
22
23/// Embedded module for exporting data/types from winit as minimally/controlled
24/// as possible. The exports from this module are not guaranteed to be stable.
25pub mod winit_exports {
26  pub use winit::{
27    event::{
28      ElementState,
29      Event,
30      MouseButton,
31      VirtualKeyCode,
32      WindowEvent,
33    },
34    event_loop::{
35      ControlFlow,
36      EventLoop,
37      EventLoopProxy,
38      EventLoopWindowTarget,
39    },
40  };
41}
42
43/// LoopBuilder - Putting this here for consistency.
44pub struct LoopBuilder;
45
46impl LoopBuilder {
47  pub fn new() -> Self {
48    return Self;
49  }
50
51  pub fn build<Events: 'static + std::fmt::Debug>(self) -> Loop<Events> {
52    let event_loop = EventLoopBuilder::<Events>::with_user_event().build();
53    return Loop { event_loop };
54  }
55}
56
57/// Loop wrapping for the winit event loop.
58pub struct Loop<E: 'static + std::fmt::Debug> {
59  event_loop: EventLoop<E>,
60}
61
62/// Structure that contains properties needed for building a window.
63pub struct WindowProperties {
64  pub name: String,
65  pub dimensions: (u32, u32),
66  pub monitor_handle: MonitorHandle,
67}
68
69/// Metadata for Lambda window sizing that supports Copy and Move operations.
70#[derive(Clone, Copy)]
71pub struct WindowSize {
72  pub width: u32,
73  pub height: u32,
74  pub logical: LogicalSize<u32>,
75  pub physical: PhysicalSize<u32>,
76}
77
78pub struct WindowHandle {
79  pub window_handle: Window,
80  pub size: WindowSize,
81  pub monitor_handle: MonitorHandle,
82}
83
84// Should we take the loop as a field right here? Probably a ref or something? IDK
85pub struct WindowHandleBuilder {
86  window_handle: Option<Window>,
87  size: WindowSize,
88  monitor_handle: Option<MonitorHandle>,
89}
90
91impl WindowHandleBuilder {
92  /// Instantiate an empty builder
93  pub fn new() -> Self {
94    // Initialize the window size with some default values.
95    let logical: LogicalSize<u32> = [0, 0].into();
96    let physical = logical.to_physical(1.0);
97    let size = WindowSize {
98      width: 0,
99      height: 0,
100      logical,
101      physical,
102    };
103
104    return Self {
105      window_handle: None,
106      size,
107      monitor_handle: None,
108    };
109  }
110
111  /// Set the window size for the WindowHandle
112  fn with_window_size(
113    mut self,
114    window_size: (u32, u32),
115    scale_factor: f64,
116  ) -> Self {
117    let logical: LogicalSize<u32> = window_size.into();
118    let physical: PhysicalSize<u32> = logical.to_physical(scale_factor);
119    let (width, height) = window_size;
120
121    let window_size = WindowSize {
122      width,
123      height,
124      logical,
125      physical,
126    };
127
128    self.size = window_size;
129    return self;
130  }
131
132  /// Probably the function that'll be used the most
133  pub fn with_window_properties<E: 'static + std::fmt::Debug>(
134    mut self,
135    window_properties: WindowProperties,
136    lambda_loop: &Loop<E>,
137  ) -> Self {
138    let WindowProperties {
139      name,
140      dimensions,
141      monitor_handle,
142    } = window_properties;
143
144    // TODO(ahlawat) = Find out if there's a better way to do this. Looks kinda ugly.
145    self = self.with_window_size(dimensions, monitor_handle.scale_factor());
146
147    let window_handle = WindowBuilder::new()
148      .with_title(name)
149      .with_inner_size(self.size.logical)
150      .build(&lambda_loop.event_loop)
151      .expect("Failed creation of window handle");
152
153    self.monitor_handle = Some(monitor_handle);
154    self.window_handle = Some(window_handle);
155    return self;
156  }
157
158  /// Build the WindowHandle
159  pub fn build(self) -> WindowHandle {
160    return WindowHandle {
161      monitor_handle: self
162        .monitor_handle
163        .expect("Unable to find a MonitorHandle."),
164      size: self.size,
165      window_handle: self.window_handle.expect("Unable to find WindowHandle."),
166    };
167  }
168}
169
170/// Event loop publisher wrapper for pushing events into a winit event loop.
171pub struct LoopPublisher<E: 'static> {
172  winit_proxy: EventLoopProxy<E>,
173}
174
175impl<E: 'static + std::fmt::Debug> LoopPublisher<E> {
176  /// New LoopPublishers are created from a lambda_loop directly and don't need
177  #[inline]
178  pub fn new(lambda_loop: &Loop<E>) -> Self {
179    let winit_proxy = lambda_loop.event_loop.create_proxy();
180    return LoopPublisher { winit_proxy };
181  }
182
183  /// Publishes an event into the event loop that created this publisher.
184  #[inline]
185  pub fn publish_event(&self, event: E) {
186    self
187      .winit_proxy
188      .send_event(event)
189      .expect("Failed to send event");
190  }
191}
192
193impl<E: 'static + std::fmt::Debug> Loop<E> {
194  /// Create an event publisher for this Loop.
195  pub fn create_event_publisher(&mut self) -> LoopPublisher<E> {
196    return LoopPublisher::new(&self);
197  }
198
199  /// Returns the primary monitor for the current OS if detectable.
200  pub fn get_primary_monitor(&self) -> Option<MonitorHandle> {
201    return self.event_loop.primary_monitor();
202  }
203
204  /// Get all monitors available on the system.
205  pub fn get_all_monitors(&self) -> impl Iterator<Item = MonitorHandle> {
206    return self.event_loop.available_monitors();
207  }
208
209  /// Gets the first available monitor or panics.
210  pub fn get_any_available_monitors(&self) -> Option<MonitorHandle> {
211    return self.event_loop.available_monitors().next();
212  }
213
214  /// Uses the winit event loop to run forever
215  pub fn run_forever<Callback>(self, callback: Callback)
216  where
217    Callback: 'static
218      + FnMut(Event<E>, &EventLoopWindowTarget<E>, &mut ControlFlow) -> (),
219  {
220    self.event_loop.run(callback);
221  }
222}