1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
use winit::{
  dpi::{
    LogicalSize,
    PhysicalSize,
  },
  event::Event,
  event_loop::{
    ControlFlow,
    EventLoop,
    EventLoopBuilder,
    EventLoopProxy,
    EventLoopWindowTarget,
  },
  monitor::MonitorHandle,
  window::{
    Window,
    WindowBuilder,
  },
};

/// Embedded module for exporting data/types from winit as minimally/controlled
/// as possible. The exports from this module are not guaranteed to be stable.
pub mod winit_exports {
  pub use winit::{
    event::{
      ElementState,
      Event,
      VirtualKeyCode,
      WindowEvent,
    },
    event_loop::{
      ControlFlow,
      EventLoop,
      EventLoopProxy,
      EventLoopWindowTarget,
    },
  };
}

/// LoopBuilder - Putting this here for consistency.
pub struct LoopBuilder;

impl LoopBuilder {
  pub fn new() -> Self {
    return Self;
  }

  pub fn build<Events: 'static + std::fmt::Debug>(self) -> Loop<Events> {
    let event_loop = EventLoopBuilder::<Events>::with_user_event().build();
    return Loop { event_loop };
  }
}

/// Loop wrapping for the winit event loop.
pub struct Loop<E: 'static + std::fmt::Debug> {
  event_loop: EventLoop<E>,
}

/// Structure that contains properties needed for building a window.
pub struct WindowProperties {
  pub name: String,
  pub dimensions: (u32, u32),
  pub monitor_handle: MonitorHandle,
}

/// Metadata for Lambda window sizing that supports Copy and Move operations.
#[derive(Clone, Copy)]
pub struct WindowSize {
  pub width: u32,
  pub height: u32,
  pub logical: LogicalSize<u32>,
  pub physical: PhysicalSize<u32>,
}

pub struct WindowHandle {
  pub window_handle: Window,
  pub size: WindowSize,
  pub monitor_handle: MonitorHandle,
}

// Should we take the loop as a field right here? Probably a ref or something? IDK
pub struct WindowHandleBuilder {
  window_handle: Option<Window>,
  size: WindowSize,
  monitor_handle: Option<MonitorHandle>,
}

impl WindowHandleBuilder {
  /// Instantiate an empty builder
  pub fn new() -> Self {
    // Initialize the window size with some default values.
    let logical: LogicalSize<u32> = [0, 0].into();
    let physical = logical.to_physical(1.0);
    let size = WindowSize {
      width: 0,
      height: 0,
      logical,
      physical,
    };

    return Self {
      window_handle: None,
      size,
      monitor_handle: None,
    };
  }

  /// Set the window size for the WindowHandle
  fn with_window_size(
    mut self,
    window_size: (u32, u32),
    scale_factor: f64,
  ) -> Self {
    let logical: LogicalSize<u32> = window_size.into();
    let physical: PhysicalSize<u32> = logical.to_physical(scale_factor);
    let (width, height) = window_size;

    let window_size = WindowSize {
      width,
      height,
      logical,
      physical,
    };

    self.size = window_size;
    return self;
  }

  /// Probably the function that'll be used the most
  pub fn with_window_properties<E: 'static + std::fmt::Debug>(
    mut self,
    window_properties: WindowProperties,
    lambda_loop: &Loop<E>,
  ) -> Self {
    let WindowProperties {
      name,
      dimensions,
      monitor_handle,
    } = window_properties;

    // TODO(ahlawat) = Find out if there's a better way to do this. Looks kinda ugly.
    self = self.with_window_size(dimensions, monitor_handle.scale_factor());

    let window_handle = WindowBuilder::new()
      .with_title(name)
      .with_inner_size(self.size.logical)
      .build(&lambda_loop.event_loop)
      .expect("Failed creation of window handle");

    self.monitor_handle = Some(monitor_handle);
    self.window_handle = Some(window_handle);
    return self;
  }

  /// Build the WindowHandle
  pub fn build(self) -> WindowHandle {
    return WindowHandle {
      monitor_handle: self
        .monitor_handle
        .expect("Unable to find a MonitorHandle."),
      size: self.size,
      window_handle: self.window_handle.expect("Unable to find WindowHandle."),
    };
  }
}

/// Event loop publisher wrapper for pushing events into a winit event loop.
pub struct LoopPublisher<E: 'static> {
  winit_proxy: EventLoopProxy<E>,
}

impl<E: 'static + std::fmt::Debug> LoopPublisher<E> {
  /// New LoopPublishers are created from a lambda_loop directly and don't need
  #[inline]
  pub fn new(lambda_loop: &Loop<E>) -> Self {
    let winit_proxy = lambda_loop.event_loop.create_proxy();
    return LoopPublisher { winit_proxy };
  }

  /// Publishes an event into the event loop that created this publisher.
  #[inline]
  pub fn publish_event(&self, event: E) {
    self
      .winit_proxy
      .send_event(event)
      .expect("Failed to send event");
  }
}

impl<E: 'static + std::fmt::Debug> Loop<E> {
  /// Create an event publisher for this Loop.
  pub fn create_event_publisher(&mut self) -> LoopPublisher<E> {
    return LoopPublisher::new(&self);
  }

  /// Returns the primary monitor for the current OS if detectable.
  pub fn get_primary_monitor(&self) -> Option<MonitorHandle> {
    return self.event_loop.primary_monitor();
  }

  /// Get all monitors available on the system.
  pub fn get_all_monitors(&self) -> impl Iterator<Item = MonitorHandle> {
    return self.event_loop.available_monitors();
  }

  /// Gets the first available monitor or panics.
  pub fn get_any_available_monitors(&self) -> Option<MonitorHandle> {
    return self.event_loop.available_monitors().next();
  }

  /// Uses the winit event loop to run forever
  pub fn run_forever<Callback>(self, callback: Callback)
  where
    Callback: 'static
      + FnMut(Event<E>, &EventLoopWindowTarget<E>, &mut ControlFlow) -> (),
  {
    self.event_loop.run(callback);
  }
}