1extern crate glutin;
4use self::glutin::{EventsLoop, Event, WindowEvent, ElementState};
5use self::glutin::{GlWindow, GlContext, GlRequest, Api};
6use self::glutin::{WindowBuilder, ContextBuilder, dpi::LogicalSize};
7
8use std::error::Error;
9use std::fmt::{Display, Formatter, Error as FmtError};
10use std::collections::HashSet;
11use std::rc::Rc;
12
13mod monitor;
14pub use self::monitor::*;
15
16mod config;
17pub use self::config::*;
18
19use ::{Point, Size, input::Input};
20
21#[derive(Clone, Debug, PartialEq, Eq)]
23pub enum WindowError {
24 UnknownMonitor,
27
28 InternalError(String)
30}
31
32impl Display for WindowError {
33 fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
34 match self {
35 &WindowError::UnknownMonitor => write!(f, "Unknown monitor"),
36 &WindowError::InternalError(ref error) => write!(f, "{}", error)
37 }
38 }
39}
40impl Error for WindowError {}
41
42pub struct Window {
44 events: EventsLoop,
45 window: Rc<GlWindow>,
46 input: Input
47}
48
49impl Window {
50 pub fn new(config: Config) -> Result<Window, WindowError> {
56 let events = EventsLoop::new();
57 let mut window = WindowBuilder::new()
58 .with_title(config.title)
59 .with_maximized(config.maximized)
60 .with_resizable(config.resizable)
61 .with_dimensions(LogicalSize {
62 width: config.size.width,
63 height: config.size.height
64 });
65
66 if let Some(size) = config.min_size {
67 window = window.with_min_dimensions(LogicalSize {
68 width: size.width,
69 height: size.height
70 });
71 }
72
73 if !config.resizable {
74 window = window.with_max_dimensions(LogicalSize {
75 width: config.size.width,
76 height: config.size.height
77 })
78 } else if let Some(size) = config.max_size {
79 window = window.with_max_dimensions(LogicalSize {
80 width: size.width,
81 height: size.height
82 });
83 }
84
85 window = window.with_fullscreen(match config.fullscreen {
86 Fullscreen::Disabled => None,
87 Fullscreen::Primary => Some(events.get_primary_monitor()),
88 Fullscreen::Monitor(name) => {
89 let mut result = Err(WindowError::UnknownMonitor);
90 for monitor in events.get_available_monitors() {
91 if let Some(n) = monitor.get_name() {
92 if name == n {
93 result = Ok(monitor);
94 }
95 }
96 }
97 Some(result?)
98 }
99 });
100
101 let context = ContextBuilder::new()
102 .with_gl(GlRequest::Specific(Api::OpenGl, (3, 2)))
103 .with_vsync(config.vsync)
104 .with_multisampling(config.msaa);
105
106 let window = GlWindow::new(window, context, &events)
107 .map_err(|error| WindowError::InternalError(ToString::to_string(&error)))?;
108
109 unsafe {
110 window.make_current()
111 .map_err(|error| WindowError::InternalError(ToString::to_string(&error)))?;
112 }
113
114 let rc = Rc::new(window);
115 Ok(Window {
116 events,
117 window: Rc::clone(&rc),
118 input: Input {
119 window: Rc::clone(&rc),
120 keys: HashSet::new(),
121 buttons: HashSet::new(),
122 cursor: Point::default()
123 }
124 })
125 }
126
127 pub fn update(&mut self) -> Result<bool, String> {
131 let input = &mut self.input;
132
133 let mut result = true;
134 self.events.poll_events(|event| {
135 if let Event::WindowEvent {event, ..} = event {
136 match event {
137 WindowEvent::CloseRequested => result = false,
138 WindowEvent::KeyboardInput {input: event, ..} => {
139 if let Some(key) = event.virtual_keycode {
140 match event.state {
141 ElementState::Pressed => input.keys.insert(key),
142 ElementState::Released => input.keys.remove(&key)
143 };
144 }
145 },
146 WindowEvent::MouseInput {button, state, ..} => {
147 match state {
148 ElementState::Pressed => input.buttons.insert(button),
149 ElementState::Released => input.buttons.remove(&button)
150 };
151 },
152 WindowEvent::CursorMoved {position, ..} => {
153 input.cursor = Point {
154 x: position.x,
155 y: position.y
156 }
157 }
158 _ => ()
159 }
160 }
161 });
162 Ok(result)
163 }
164
165 pub fn get_primary_monitor(&self) -> Monitor {
167 Monitor {
168 monitor: self.events.get_primary_monitor()
169 }
170 }
171
172 pub fn get_all_monitors(&self) -> MonitorIter {
174 MonitorIter {
175 iter: self.events.get_available_monitors()
176 }
177 }
178
179 pub fn get_size(&self) -> Size {
181 self.window.get_inner_size()
182 .map_or(Size {
183 width: 1.0,
184 height: 1.0
185 }, |size| Size {
186 width: size.width,
187 height: size.height
188 })
189 }
190
191 pub fn input(&mut self) -> &mut Input {
192 &mut self.input
193 }
194}