mint2d/core/
mod.rs

1//! Core window, context and state management.
2
3extern 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/// Possible errors that can occur from creating a window.
22#[derive(Clone, Debug, PartialEq, Eq)]
23pub enum WindowError {
24	/// [`Fullscreen::Monitor`](enum.Fullscreen.html#variant.Monitor)
25	/// didn't match any monitor name.
26	UnknownMonitor,
27
28	/// An unknown internal error occurred.
29	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
42/// A window that handles the context and state of the game.
43pub struct Window {
44	events: EventsLoop,
45	window: Rc<GlWindow>,
46	input: Input
47}
48
49impl Window {
50	/// # Errors
51	/// If [`Config.fullscreen`](struct.Config.html#structfield.fullscreen)
52	/// is [`Fullscreen::Monitor`](enum.Fullscreen.html#variant.Monitor)
53	/// and it doesn't match any monitor name, this will return with
54	/// [`WindowError::UnknownMonitor`](enum.WindowError.html#variant.UnknownMonitor).
55	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	/// Updates the window and processes all events.
128	/// Will return false if the window has been closed,
129	/// true otherwise.
130	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	/// Gets the primary monitor.
166	pub fn get_primary_monitor(&self) -> Monitor {
167		Monitor {
168			monitor: self.events.get_primary_monitor()
169		}
170	}
171
172	/// Gets an iterator of all the monitors.
173	pub fn get_all_monitors(&self) -> MonitorIter {
174		MonitorIter {
175			iter: self.events.get_available_monitors()
176		}
177	}
178
179	/// Gets the current size of the window.
180	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}