glfw_window/
lib.rs

1#![deny(missing_docs)]
2
3//! A GLFW window back-end for the Piston game engine.
4
5extern crate gl;
6extern crate glfw;
7extern crate input;
8extern crate shader_version;
9extern crate window;
10
11// External crates.
12use glfw::{Context, Joystick, JoystickId};
13use input::{
14    keyboard, Button, ButtonArgs, ButtonState, CloseArgs, ControllerAxisArgs, ControllerButton,
15    Event, FileDrag, Input, Motion, MouseButton, ResizeArgs,
16};
17use std::collections::VecDeque;
18use std::error::Error;
19use std::time::Duration;
20use std::collections::HashMap;
21use glfw::GlfwReceiver as Receiver;
22use window::{
23    AdvancedWindow, Api, BuildFromWindowSettings, OpenGLWindow, Position, ProcAddress, Size,
24    UnsupportedGraphicsApiError, Window, WindowSettings,
25};
26
27pub use shader_version::OpenGL;
28
29// list of joysticks to check
30const JOYSTICKS: [JoystickId; 16] = [
31    JoystickId::Joystick1,
32    JoystickId::Joystick2,
33    JoystickId::Joystick3,
34    JoystickId::Joystick4,
35    JoystickId::Joystick5,
36    JoystickId::Joystick6,
37    JoystickId::Joystick7,
38    JoystickId::Joystick8,
39    JoystickId::Joystick9,
40    JoystickId::Joystick10,
41    JoystickId::Joystick11,
42    JoystickId::Joystick12,
43    JoystickId::Joystick13,
44    JoystickId::Joystick14,
45    JoystickId::Joystick15,
46    JoystickId::Joystick16,
47];
48
49/// Contains stuff for game window.
50pub struct GlfwWindow {
51    /// The window.
52    pub window: glfw::PWindow,
53    /// Receives events from window.
54    events: Receiver<(f64, glfw::WindowEvent)>,
55    /// GLFW context.
56    pub glfw: glfw::Glfw,
57    event_queue: VecDeque<Input>,
58    // Used to compute relative mouse movement.
59    last_mouse_pos: Option<(f64, f64)>,
60    // The back-end does not remember the title.
61    title: String,
62    exit_on_esc: bool,
63    automatic_close: bool,
64
65    /// ignore controller axis inputs below this threshold
66    pub joystick_deadzone: f64,
67    joysticks: Vec<JoystickHelper>,
68}
69
70impl GlfwWindow {
71    /// Create a new game window from an existing GLFW window.
72    pub fn from_pieces(
73        mut win: glfw::PWindow,
74        glfw: glfw::Glfw,
75        events: Receiver<(f64, glfw::WindowEvent)>,
76        exit_on_esc: bool,
77    ) -> GlfwWindow {
78        win.set_all_polling(true);
79        win.make_current();
80        let title = "<unknown window title, created from_pieces>";
81
82        // setup joysticks
83        let mut joysticks = Vec::new();
84        for &i in &JOYSTICKS {
85            joysticks.push(JoystickHelper::new(glfw.get_joystick(i)));
86        }
87
88        GlfwWindow {
89            joysticks,
90            window: win,
91            events,
92            glfw,
93            exit_on_esc,
94            event_queue: VecDeque::new(),
95            last_mouse_pos: None,
96            title: title.to_string(),
97            automatic_close: true,
98            joystick_deadzone: 0.0,
99        }
100    }
101
102    /// Creates a new game window for GLFW.
103    pub fn new(settings: &WindowSettings) -> Result<GlfwWindow, Box<dyn Error>> {
104        use glfw::SwapInterval;
105        use std::ptr::null;
106
107        // Initialize GLFW.
108        let mut glfw = glfw::init_no_callbacks()?;
109
110        let api = settings
111            .get_maybe_graphics_api()
112            .unwrap_or(Api::opengl(3, 2));
113        if api.api != "OpenGL" {
114            return Err(UnsupportedGraphicsApiError {
115                found: api.api,
116                expected: vec!["OpenGL".into()],
117            }
118            .into());
119        };
120
121        // Make sure we have the right GL version.
122        glfw.window_hint(glfw::WindowHint::ContextVersion(api.major, api.minor));
123        glfw.window_hint(glfw::WindowHint::Resizable(settings.get_resizable()));
124        glfw.window_hint(glfw::WindowHint::Decorated(settings.get_decorated()));
125        glfw.window_hint(glfw::WindowHint::TransparentFramebuffer(
126            settings.get_transparent(),
127        ));
128        // Set sRGB.
129        glfw.window_hint(glfw::WindowHint::SRgbCapable(settings.get_srgb()));
130        if api >= Api::opengl(3, 2) {
131            if cfg!(target_os = "macos") {
132                glfw.window_hint(glfw::WindowHint::OpenGlForwardCompat(true));
133            }
134            glfw.window_hint(glfw::WindowHint::OpenGlProfile(
135                glfw::OpenGlProfileHint::Core,
136            ));
137        }
138        if settings.get_samples() != 0 {
139            glfw.window_hint(glfw::WindowHint::Samples(Some(
140                settings.get_samples() as u32
141            )));
142        }
143
144        // Create GLFW window.
145        let (mut window, events) = glfw
146            .create_window(
147                settings.get_size().width as u32,
148                settings.get_size().height as u32,
149                &settings.get_title(),
150                glfw::WindowMode::Windowed,
151            )
152            .ok_or("Failed to create GLFW window.")?;
153        window.set_all_polling(true);
154        window.make_current();
155
156        if settings.get_vsync() {
157            glfw.set_swap_interval(SwapInterval::Sync(1));
158        } else {
159            glfw.set_swap_interval(SwapInterval::None);
160        }
161
162        // Load the OpenGL function pointers.
163        gl::load_with(|s| window.get_proc_address(s)
164            .map(|n| n as *const _)
165            .unwrap_or(null()));
166
167        // setup joysticks
168        let mut joysticks = Vec::new();
169        if settings.get_controllers() {
170            for &i in &JOYSTICKS {
171                joysticks.push(JoystickHelper::new(glfw.get_joystick(i)));
172            }
173        }
174
175        Ok(GlfwWindow {
176            joysticks,
177            window,
178            events,
179            glfw,
180            event_queue: VecDeque::new(),
181            last_mouse_pos: None,
182            title: settings.get_title(),
183            exit_on_esc: settings.get_exit_on_esc(),
184            automatic_close: settings.get_automatic_close(),
185            joystick_deadzone: 0.0,
186        })
187    }
188
189    fn flush_messages(&mut self) {
190        for (_, event) in glfw::flush_messages(&self.events) {
191            match event {
192                glfw::WindowEvent::Key(glfw::Key::Escape, _, glfw::Action::Press, _)
193                    if self.exit_on_esc =>
194                {
195                    self.window.set_should_close(true);
196                }
197                glfw::WindowEvent::Close => {
198                    if !self.automatic_close {
199                        self.window.set_should_close(false);
200                    }
201                    self.event_queue.push_back(Input::Close(CloseArgs));
202                }
203                glfw::WindowEvent::Char(ch) => {
204                    self.event_queue.push_back(Input::Text(ch.to_string()));
205                }
206                glfw::WindowEvent::Key(key, scancode, glfw::Action::Press, _) => {
207                    self.event_queue.push_back(Input::Button(ButtonArgs {
208                        state: ButtonState::Press,
209                        button: Button::Keyboard(glfw_map_key(key)),
210                        scancode: Some(scancode as i32),
211                    }));
212                }
213                glfw::WindowEvent::Key(key, scancode, glfw::Action::Release, _) => {
214                    self.event_queue.push_back(Input::Button(ButtonArgs {
215                        state: ButtonState::Release,
216                        button: Button::Keyboard(glfw_map_key(key)),
217                        scancode: Some(scancode as i32),
218                    }));
219                }
220                glfw::WindowEvent::MouseButton(button, glfw::Action::Press, _) => {
221                    self.event_queue.push_back(Input::Button(ButtonArgs {
222                        state: ButtonState::Press,
223                        button: Button::Mouse(glfw_map_mouse(button)),
224                        scancode: None,
225                    }));
226                }
227                glfw::WindowEvent::MouseButton(button, glfw::Action::Release, _) => {
228                    self.event_queue.push_back(Input::Button(ButtonArgs {
229                        state: ButtonState::Release,
230                        button: Button::Mouse(glfw_map_mouse(button)),
231                        scancode: None,
232                    }));
233                }
234                glfw::WindowEvent::CursorPos(x, y) => {
235                    self.event_queue
236                        .push_back(Input::Move(Motion::MouseCursor([x, y])));
237                    match self.last_mouse_pos {
238                        Some((lx, ly)) => self
239                            .event_queue
240                            .push_back(Input::Move(Motion::MouseRelative([x - lx, y - ly]))),
241                        None => (),
242                    }
243                    self.last_mouse_pos = Some((x, y));
244                }
245                glfw::WindowEvent::Scroll(x, y) => {
246                    self.event_queue
247                        .push_back(Input::Move(Motion::MouseScroll([x, y])));
248                }
249                glfw::WindowEvent::Size(w, h) => {
250                    let draw_size = self.draw_size();
251                    self.event_queue.push_back(Input::Resize(ResizeArgs {
252                        window_size: [w as f64, h as f64],
253                        draw_size: draw_size.into(),
254                    }));
255                }
256                glfw::WindowEvent::Focus(focus) => {
257                    self.event_queue.push_back(Input::Focus(focus));
258                }
259                glfw::WindowEvent::CursorEnter(cursor) => {
260                    self.event_queue.push_back(Input::Cursor(cursor));
261                }
262                glfw::WindowEvent::FileDrop(files) => {
263                    for file in files {
264                        self.event_queue
265                            .push_back(Input::FileDrag(FileDrag::Drop(file)))
266                    }
267                }
268                _ => (),
269            }
270        }
271
272        // println!("checking gamepads");
273        for j in self.joysticks.iter_mut() {
274            j.update(&mut self.event_queue, self.joystick_deadzone);
275        }
276    }
277
278    fn wait_event(&mut self) -> Event {
279        loop {
280            if self.event_queue.len() == 0 {
281                self.glfw.wait_events();
282                self.flush_messages();
283            }
284            if let Some(event) = self.event_queue.pop_front() {
285                return Event::Input(event, None);
286            }
287        }
288    }
289
290    fn wait_event_timeout(&mut self, timeout: Duration) -> Option<Event> {
291        if self.event_queue.len() == 0 {
292            let timeout_secs =
293                timeout.as_secs() as f64 + (timeout.subsec_nanos() as f64 / 1_000_000_000.0);
294            self.glfw.wait_events_timeout(timeout_secs);
295            self.flush_messages();
296        }
297        self.event_queue.pop_front().map(|x| Event::Input(x, None))
298    }
299
300    fn poll_event(&mut self) -> Option<Event> {
301        if self.event_queue.len() == 0 {
302            self.glfw.poll_events();
303            self.flush_messages();
304        }
305        self.event_queue.pop_front().map(|x| Event::Input(x, None))
306    }
307
308    fn capture_cursor(&mut self, enabled: bool) {
309        if enabled {
310            self.window.set_cursor_mode(glfw::CursorMode::Disabled);
311        } else {
312            self.window.set_cursor_mode(glfw::CursorMode::Normal);
313            self.last_mouse_pos = None;
314        }
315    }
316}
317
318impl BuildFromWindowSettings for GlfwWindow {
319    fn build_from_window_settings(settings: &WindowSettings) -> Result<GlfwWindow, Box<dyn Error>> {
320        GlfwWindow::new(settings)
321    }
322}
323
324impl Window for GlfwWindow {
325    fn size(&self) -> Size {
326        let (w, h) = self.window.get_size();
327        Size {
328            width: w as f64,
329            height: h as f64,
330        }
331    }
332
333    fn draw_size(&self) -> Size {
334        let (w, h) = self.window.get_framebuffer_size();
335        Size {
336            width: w as f64,
337            height: h as f64,
338        }
339    }
340
341    fn set_should_close(&mut self, value: bool) {
342        self.window.set_should_close(value);
343    }
344
345    fn should_close(&self) -> bool {
346        self.window.should_close()
347    }
348
349    fn swap_buffers(&mut self) {
350        self.window.swap_buffers()
351    }
352
353    fn wait_event(&mut self) -> Event {
354        self.wait_event()
355    }
356
357    fn wait_event_timeout(&mut self, timeout: Duration) -> Option<Event> {
358        self.wait_event_timeout(timeout)
359    }
360
361    fn poll_event(&mut self) -> Option<Event> {
362        self.poll_event()
363    }
364}
365
366impl AdvancedWindow for GlfwWindow {
367    fn get_title(&self) -> String {
368        self.title.clone()
369    }
370
371    fn set_title(&mut self, value: String) {
372        self.window.set_title(&value)
373    }
374
375    fn get_automatic_close(&self) -> bool {
376        self.automatic_close
377    }
378
379    fn set_automatic_close(&mut self, value: bool) {
380        self.automatic_close = value;
381    }
382
383    fn get_exit_on_esc(&self) -> bool {
384        self.exit_on_esc
385    }
386
387    fn set_exit_on_esc(&mut self, value: bool) {
388        self.exit_on_esc = value
389    }
390
391    fn set_capture_cursor(&mut self, value: bool) {
392        self.capture_cursor(value)
393    }
394
395    fn show(&mut self) {
396        self.window.show();
397    }
398
399    fn hide(&mut self) {
400        self.window.hide();
401    }
402
403    fn get_position(&self) -> Option<Position> {
404        let (x, y) = self.window.get_pos();
405        Some(Position { x: x, y: y })
406    }
407
408    fn set_position<P: Into<Position>>(&mut self, pos: P) {
409        let pos: Position = pos.into();
410        self.window.set_pos(pos.x, pos.y);
411    }
412
413    fn set_size<S: Into<Size>>(&mut self, size: S) {
414        let size: Size = size.into();
415        self.window.set_size(size.width as i32, size.height as i32);
416    }
417}
418
419impl OpenGLWindow for GlfwWindow {
420    fn get_proc_address(&mut self, proc_name: &str) -> ProcAddress {
421        use std::ptr::null;
422        self.window.get_proc_address(proc_name)
423            .map(|n| n as *const _)
424            .unwrap_or(null())
425    }
426
427    fn is_current(&self) -> bool {
428        self.window.is_current()
429    }
430
431    fn make_current(&mut self) {
432        self.window.make_current()
433    }
434}
435
436fn glfw_map_key(keycode: glfw::Key) -> keyboard::Key {
437    use input::Key;
438
439    match keycode {
440        glfw::Key::Num0 => Key::D0,
441        glfw::Key::Num1 => Key::D1,
442        glfw::Key::Num2 => Key::D2,
443        glfw::Key::Num3 => Key::D3,
444        glfw::Key::Num4 => Key::D4,
445        glfw::Key::Num5 => Key::D5,
446        glfw::Key::Num6 => Key::D6,
447        glfw::Key::Num7 => Key::D7,
448        glfw::Key::Num8 => Key::D8,
449        glfw::Key::Num9 => Key::D9,
450        glfw::Key::A => Key::A,
451        glfw::Key::B => Key::B,
452        glfw::Key::C => Key::C,
453        glfw::Key::D => Key::D,
454        glfw::Key::E => Key::E,
455        glfw::Key::F => Key::F,
456        glfw::Key::G => Key::G,
457        glfw::Key::H => Key::H,
458        glfw::Key::I => Key::I,
459        glfw::Key::J => Key::J,
460        glfw::Key::K => Key::K,
461        glfw::Key::L => Key::L,
462        glfw::Key::M => Key::M,
463        glfw::Key::N => Key::N,
464        glfw::Key::O => Key::O,
465        glfw::Key::P => Key::P,
466        glfw::Key::Q => Key::Q,
467        glfw::Key::R => Key::R,
468        glfw::Key::S => Key::S,
469        glfw::Key::T => Key::T,
470        glfw::Key::U => Key::U,
471        glfw::Key::V => Key::V,
472        glfw::Key::W => Key::W,
473        glfw::Key::X => Key::X,
474        glfw::Key::Y => Key::Y,
475        glfw::Key::Z => Key::Z,
476        glfw::Key::Apostrophe => Key::Unknown,
477        glfw::Key::Backslash => Key::Backslash,
478        glfw::Key::Backspace => Key::Backspace,
479        glfw::Key::CapsLock => Key::CapsLock,
480        glfw::Key::Delete => Key::Delete,
481        glfw::Key::Comma => Key::Comma,
482        glfw::Key::Down => Key::Down,
483        glfw::Key::End => Key::End,
484        glfw::Key::Enter => Key::Return,
485        glfw::Key::Equal => Key::Equals,
486        glfw::Key::Escape => Key::Escape,
487        glfw::Key::F1 => Key::F1,
488        glfw::Key::F2 => Key::F2,
489        glfw::Key::F3 => Key::F3,
490        glfw::Key::F4 => Key::F4,
491        glfw::Key::F5 => Key::F5,
492        glfw::Key::F6 => Key::F6,
493        glfw::Key::F7 => Key::F7,
494        glfw::Key::F8 => Key::F8,
495        glfw::Key::F9 => Key::F9,
496        glfw::Key::F10 => Key::F10,
497        glfw::Key::F11 => Key::F11,
498        glfw::Key::F12 => Key::F12,
499        glfw::Key::F13 => Key::F13,
500        glfw::Key::F14 => Key::F14,
501        glfw::Key::F15 => Key::F15,
502        glfw::Key::F16 => Key::F16,
503        glfw::Key::F17 => Key::F17,
504        glfw::Key::F18 => Key::F18,
505        glfw::Key::F19 => Key::F19,
506        glfw::Key::F20 => Key::F20,
507        glfw::Key::F21 => Key::F21,
508        glfw::Key::F22 => Key::F22,
509        glfw::Key::F23 => Key::F23,
510        glfw::Key::F24 => Key::F24,
511        // Possibly next code.
512        glfw::Key::F25 => Key::Unknown,
513        glfw::Key::Kp0 => Key::NumPad0,
514        glfw::Key::Kp1 => Key::NumPad1,
515        glfw::Key::Kp2 => Key::NumPad2,
516        glfw::Key::Kp3 => Key::NumPad3,
517        glfw::Key::Kp4 => Key::NumPad4,
518        glfw::Key::Kp5 => Key::NumPad5,
519        glfw::Key::Kp6 => Key::NumPad6,
520        glfw::Key::Kp7 => Key::NumPad7,
521        glfw::Key::Kp8 => Key::NumPad8,
522        glfw::Key::Kp9 => Key::NumPad9,
523        glfw::Key::KpDecimal => Key::NumPadDecimal,
524        glfw::Key::KpDivide => Key::NumPadDivide,
525        glfw::Key::KpMultiply => Key::NumPadMultiply,
526        glfw::Key::KpSubtract => Key::NumPadMinus,
527        glfw::Key::KpAdd => Key::NumPadPlus,
528        glfw::Key::KpEnter => Key::NumPadEnter,
529        glfw::Key::KpEqual => Key::NumPadEquals,
530        glfw::Key::LeftShift => Key::LShift,
531        glfw::Key::LeftControl => Key::LCtrl,
532        glfw::Key::LeftAlt => Key::LAlt,
533        glfw::Key::LeftSuper => Key::LGui,
534        glfw::Key::RightShift => Key::RShift,
535        glfw::Key::RightControl => Key::RCtrl,
536        glfw::Key::RightAlt => Key::RAlt,
537        glfw::Key::RightSuper => Key::RGui,
538        // Map to backslash?
539        glfw::Key::GraveAccent => Key::Backquote,
540        glfw::Key::Home => Key::Home,
541        glfw::Key::Insert => Key::Insert,
542        glfw::Key::Left => Key::Left,
543        glfw::Key::LeftBracket => Key::LeftBracket,
544        glfw::Key::Menu => Key::Menu,
545        glfw::Key::Minus => Key::Minus,
546        glfw::Key::NumLock => Key::NumLockClear,
547        glfw::Key::PageDown => Key::PageDown,
548        glfw::Key::PageUp => Key::PageUp,
549        glfw::Key::Pause => Key::Pause,
550        glfw::Key::Period => Key::Period,
551        glfw::Key::PrintScreen => Key::PrintScreen,
552        glfw::Key::Right => Key::Right,
553        glfw::Key::RightBracket => Key::RightBracket,
554        glfw::Key::ScrollLock => Key::ScrollLock,
555        glfw::Key::Semicolon => Key::Semicolon,
556        glfw::Key::Slash => Key::Slash,
557        glfw::Key::Space => Key::Space,
558        glfw::Key::Tab => Key::Tab,
559        glfw::Key::Up => Key::Up,
560        glfw::Key::World1 => Key::Unknown,
561        glfw::Key::World2 => Key::Unknown,
562        glfw::Key::Unknown => Key::Unknown,
563    }
564}
565
566fn glfw_map_mouse(mouse_button: glfw::MouseButton) -> MouseButton {
567    match mouse_button {
568        glfw::MouseButton::Button1 => MouseButton::Left,
569        glfw::MouseButton::Button2 => MouseButton::Right,
570        glfw::MouseButton::Button3 => MouseButton::Middle,
571        glfw::MouseButton::Button4 => MouseButton::X1,
572        glfw::MouseButton::Button5 => MouseButton::X2,
573        glfw::MouseButton::Button6 => MouseButton::Button6,
574        glfw::MouseButton::Button7 => MouseButton::Button7,
575        glfw::MouseButton::Button8 => MouseButton::Button8,
576    }
577}
578
579/// helper struct for joystick
580struct JoystickHelper {
581    /// joystick to check
582    joystick: Joystick,
583
584    // states
585    buttons: HashMap<u8, bool>,
586    axes: HashMap<u8, f64>,
587    /// last known connected state
588    connected: bool,
589}
590impl JoystickHelper {
591    fn new(joystick: Joystick) -> Self {
592        Self {
593            joystick,
594            connected: false,
595            buttons: HashMap::new(),
596            axes: HashMap::new(),
597        }
598    }
599
600    fn update(&mut self, event_queue: &mut VecDeque<Input>, deadzone: f64) {
601        match (self.joystick.is_present(), self.connected) {
602            // not connected, and we know its not connected
603            (false, false) => return,
604
605            // was disconnected since last update
606            (false, true) => {
607                self.connected = false;
608                // clear maps to free up memory
609                self.buttons.clear();
610                self.axes.clear();
611                return;
612            }
613
614            // was connected since last update
615            (true, false) => {
616                // insert values
617                self.connected = true;
618
619                // only issue with this approach is a skipped input on the update the controller is connected
620                // i dont think this is a big issue though
621                for (axis, a) in self.joystick.get_axes().iter().enumerate() {
622                    self.axes.insert(axis as u8, *a as f64);
623                }
624                for (button, a) in self.joystick.get_buttons().iter().enumerate() {
625                    self.buttons.insert(button as u8, *a > 1);
626                }
627
628                // exit
629                return;
630            }
631
632            // still connected, nothing to do
633            (true, true) => {}
634        }
635
636        // check axes
637        for (axis, a) in self.joystick.get_axes().iter().enumerate() {
638            let previous = self.axes.get_mut(&(axis as u8)).unwrap();
639            let a = *a as f64;
640
641            if a == *previous || a.abs() < deadzone {
642                // if the value is the same, or within the deadzone, dont do an update
643                continue;
644            } else {
645                // new value, update existing value
646                *previous = a
647            }
648
649            // add change as event
650            event_queue.push_back(Input::Move(Motion::ControllerAxis(
651                ControllerAxisArgs::new(self.joystick.id as u32, axis as u8, a),
652            )));
653        }
654
655        // check buttons
656        for (button, a) in self.joystick.get_buttons().iter().enumerate() {
657            let previous = self.buttons.get_mut(&(button as u8)).unwrap();
658            let pressed = *a > 0;
659
660            if pressed == *previous {
661                // if the value is the same, dont do an update
662                continue;
663            } else {
664                // new value, update existing value
665                *previous = pressed
666            }
667
668            // add change as event
669            event_queue.push_back(Input::Button(ButtonArgs {
670                state: if pressed {
671                    ButtonState::Press
672                } else {
673                    ButtonState::Release
674                },
675                button: Button::Controller(ControllerButton::new(
676                    self.joystick.id as u32,
677                    button as u8,
678                )),
679                scancode: None,
680            }));
681        }
682    }
683}