1use super::*;
2
3mod cursor;
4mod events;
5
6pub use cursor::*;
7pub use events::*;
8
9#[cfg(target_arch = "wasm32")]
10mod js {
11 use super::*;
12
13 #[wasm_bindgen(module = "/src/window/web.js")]
14 extern "C" {
15 pub fn initialize_window(canvas: &web_sys::HtmlCanvasElement);
16 pub fn is_fullscreen() -> bool;
17 pub fn set_fullscreen(canvas: &web_sys::HtmlCanvasElement, fullscreen: bool);
18 }
19}
20
21pub struct Window {
22 #[cfg(target_arch = "wasm32")]
23 canvas: web_sys::HtmlCanvasElement,
24 #[cfg(not(target_arch = "wasm32"))]
25 glutin_window: glutin::WindowedContext<glutin::PossiblyCurrent>,
26 #[cfg(not(target_arch = "wasm32"))]
27 glutin_event_loop: RefCell<glutin::event_loop::EventLoop<()>>,
28 event_handler: Rc<RefCell<Option<Box<dyn FnMut(Event)>>>>,
29 pressed_keys: Rc<RefCell<HashSet<Key>>>,
30 pressed_buttons: Rc<RefCell<HashSet<MouseButton>>>,
31 should_close: Cell<bool>,
32 mouse_pos: Rc<Cell<Vec2<f64>>>,
33 ugli: Rc<Ugli>,
34 #[cfg(not(target_arch = "wasm32"))]
35 is_fullscreen: Cell<bool>,
36}
37
38impl Window {
39 pub fn new(title: &str, vsync: bool) -> Self {
40 #[cfg(target_arch = "wasm32")]
41 let window = {
42 let _ = title;
43 let _ = vsync;
44 let canvas = web_sys::window()
45 .unwrap()
46 .document()
47 .unwrap()
48 .get_element_by_id("geng-canvas")
49 .expect("#geng-canvas not found")
50 .dyn_into::<web_sys::HtmlCanvasElement>()
51 .expect("#geng-canvas is not a canvas");
52 js::initialize_window(&canvas);
53 let ugli = Rc::new(Ugli::create_webgl(&canvas));
54 let window = Self {
55 canvas,
56 event_handler: Rc::new(RefCell::new(None)),
57 ugli,
58 should_close: Cell::new(false),
59 mouse_pos: Rc::new(Cell::new(vec2(0.0, 0.0))),
60 pressed_keys: Rc::new(RefCell::new(HashSet::new())),
61 pressed_buttons: Rc::new(RefCell::new(HashSet::new())),
62 };
63 let event_handler = window.event_handler.clone();
64 let pressed_keys = window.pressed_keys.clone();
65 let pressed_buttons = window.pressed_buttons.clone();
66 let mouse_pos = window.mouse_pos.clone();
67 window.subscribe_events(move |event| {
68 Self::default_handler(&event, &pressed_keys, &pressed_buttons, &mouse_pos);
69 if let Some(ref mut handler) = *event_handler.borrow_mut() {
70 handler(event);
71 }
72 });
73 window
74 };
75 #[cfg(not(target_arch = "wasm32"))]
76 let window = {
77 let glutin_event_loop = glutin::event_loop::EventLoop::<()>::new();
78 let glutin_window = glutin::ContextBuilder::new()
80 .with_vsync(vsync)
81 .build_windowed(
82 glutin::window::WindowBuilder::new().with_title(title), &glutin_event_loop,
84 )
85 .unwrap();
86 let glutin_window = unsafe { glutin_window.make_current() }.unwrap();
87 let ugli = Rc::new(Ugli::create_from_glutin(&glutin_window));
88 Self {
89 glutin_window,
90 glutin_event_loop: RefCell::new(glutin_event_loop),
91 event_handler: Rc::new(RefCell::new(None)),
92 ugli,
93 should_close: Cell::new(false),
94 mouse_pos: Rc::new(Cell::new(vec2(0.0, 0.0))),
95 pressed_keys: Rc::new(RefCell::new(HashSet::new())),
96 pressed_buttons: Rc::new(RefCell::new(HashSet::new())),
97 is_fullscreen: Cell::new(false),
98 }
99 };
100 window
101 }
102
103 pub(crate) fn set_event_handler(&self, handler: Box<dyn FnMut(Event)>) {
104 *self.event_handler.borrow_mut() = Some(handler);
105 }
106
107 pub fn swap_buffers(&self) {
113 #[cfg(not(target_arch = "wasm32"))]
115 {
116 self.glutin_window.swap_buffers().unwrap();
117 }
118 #[cfg(not(target_arch = "wasm32"))]
119 for event in self.internal_get_events() {
120 Self::default_handler(
121 &event,
122 &self.pressed_keys,
123 &self.pressed_buttons,
124 &self.mouse_pos,
125 );
126 if let Some(ref mut handler) = *self.event_handler.borrow_mut() {
127 handler(event);
128 }
129 }
130 }
131
132 fn default_handler(
133 event: &Event,
134 pressed_keys: &RefCell<HashSet<Key>>,
135 pressed_buttons: &RefCell<HashSet<MouseButton>>,
136 mouse_pos: &Cell<Vec2<f64>>,
137 ) {
138 match *event {
139 Event::KeyDown { key } => {
140 pressed_keys.borrow_mut().insert(key);
141 }
142 Event::KeyUp { key } => {
143 pressed_keys.borrow_mut().remove(&key);
144 }
145 Event::MouseMove { position } => {
146 mouse_pos.set(position);
147 }
148 Event::MouseDown { button, .. } => {
149 pressed_buttons.borrow_mut().insert(button);
150 }
151 Event::MouseUp { button, .. } => {
152 pressed_buttons.borrow_mut().remove(&button);
153 }
154 _ => {}
155 }
156 }
157
158 pub fn real_size(&self) -> Vec2<usize> {
159 #[cfg(target_arch = "wasm32")]
160 return {
161 let width = self.canvas.width() as usize;
162 let height = self.canvas.height() as usize;
163 vec2(width, height)
164 };
165 #[cfg(not(target_arch = "wasm32"))]
166 return {
167 let size = self.glutin_window.window().inner_size();
168 let (width, height) = (size.width, size.height);
169 vec2(width as usize, height as usize)
170 };
171 }
172 pub fn size(&self) -> Vec2<usize> {
173 self.real_size().map(|x| x.max(1))
174 }
175
176 pub fn ugli(&self) -> &Rc<Ugli> {
177 self.ugli._set_size(self.size());
178 &self.ugli
179 }
180
181 pub fn should_close(&self) -> bool {
182 self.should_close.get()
183 }
184
185 pub fn is_key_pressed(&self, key: Key) -> bool {
186 self.pressed_keys.borrow().contains(&key)
187 }
188
189 pub fn is_button_pressed(&self, button: MouseButton) -> bool {
190 self.pressed_buttons.borrow().contains(&button)
191 }
192
193 pub fn pressed_keys(&self) -> HashSet<Key> {
194 self.pressed_keys.borrow().clone()
195 }
196
197 pub fn pressed_buttons(&self) -> HashSet<MouseButton> {
198 self.pressed_buttons.borrow().clone()
199 }
200
201 pub fn mouse_pos(&self) -> Vec2<f64> {
202 self.mouse_pos.get()
203 }
204
205 pub fn set_fullscreen(&self, fullscreen: bool) {
206 #[cfg(target_arch = "wasm32")]
207 js::set_fullscreen(&self.canvas, fullscreen);
208 #[cfg(not(target_arch = "wasm32"))]
209 {
210 self.glutin_window.window().set_fullscreen(if fullscreen {
211 Some(glutin::window::Fullscreen::Borderless(
212 self.glutin_window.window().current_monitor(),
213 ))
214 } else {
215 None
216 });
217 self.is_fullscreen.set(fullscreen);
218 }
219 }
220
221 pub fn is_fullscreen(&self) -> bool {
222 #[cfg(target_arch = "wasm32")]
223 return js::is_fullscreen();
224 #[cfg(not(target_arch = "wasm32"))]
225 self.is_fullscreen.get()
226 }
227
228 pub fn toggle_fullscreen(&self) {
229 self.set_fullscreen(!self.is_fullscreen());
230 }
231}