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
//! Surface implementation.
//!
//! This module exports the `GlfwSurface` as an implementation of `Surface`.

use gl;
use glfw::{self, Context, CursorMode, SwapInterval, Window, WindowMode};
use luminance::context::GraphicsContext;
use luminance::state::GraphicsState;
pub use luminance_windowing::{Surface, WindowDim, WindowOpt};
use std::cell::RefCell;
use std::os::raw::c_void;
use std::rc::Rc;
use std::sync::mpsc::Receiver;

pub use event::{Action, Key, MouseButton, WindowEvent};
pub use error::{InitError, GlfwSurfaceError, StateQueryError};

/// GLFW surface.
///
/// This type implements `GraphicsContext` so that you can use it to perform render with
/// **luminance**.
pub struct GlfwSurface {
  window: Window,
  events_rx: Receiver<(f64, WindowEvent)>,
  gfx_state: Rc<RefCell<GraphicsState>>,
}

unsafe impl GraphicsContext for GlfwSurface {
  fn state(&self) -> &Rc<RefCell<GraphicsState>> {
    &self.gfx_state
  }

  fn swap_buffers(&mut self) {
    self.window.swap_buffers();
  }
}

impl Surface for GlfwSurface {
  type Event = WindowEvent;

  type Error = GlfwSurfaceError;

  fn new(dim: WindowDim, title: &str, win_opt: WindowOpt) -> Result<Self, Self::Error> {
    let mut glfw = glfw::init(glfw::FAIL_ON_ERRORS).map_err(GlfwSurfaceError::InitError)?;

    // OpenGL hints
    glfw.window_hint(glfw::WindowHint::OpenGlProfile(glfw::OpenGlProfileHint::Core));
    glfw.window_hint(glfw::WindowHint::OpenGlForwardCompat(true));
    glfw.window_hint(glfw::WindowHint::ContextVersionMajor(3));
    glfw.window_hint(glfw::WindowHint::ContextVersionMinor(3));

    // open a window in windowed or fullscreen mode
    let (mut window, events_rx) = match dim {
      WindowDim::Windowed(w, h) => {
        glfw.create_window(w,
                           h,
                           title,
                           WindowMode::Windowed).ok_or(GlfwSurfaceError::WindowCreationFailed)?
      },
      WindowDim::Fullscreen => {
        glfw.with_primary_monitor(|glfw, monitor| {
          let monitor = monitor.ok_or(GlfwSurfaceError::NoPrimaryMonitor)?;
          let vmode = monitor.get_video_mode().ok_or(GlfwSurfaceError::NoVideoMode)?;
          let (w, h) = (vmode.width, vmode.height);

          Ok(glfw.create_window(w,
                                h,
                                title,
                                WindowMode::FullScreen(monitor)
                               ).ok_or(GlfwSurfaceError::WindowCreationFailed)?)
        })?
      },
      WindowDim::FullscreenRestricted(w, h) => {
        glfw.with_primary_monitor(|glfw, monitor| {
          let monitor = monitor.ok_or(GlfwSurfaceError::NoPrimaryMonitor)?;

          Ok(glfw.create_window(w,
                                h,
                                title,
                                WindowMode::FullScreen(monitor)
                               ).ok_or(GlfwSurfaceError::WindowCreationFailed)?)
        })?
      }
    };

    window.make_current();

    if win_opt.is_cursor_hidden() {
      window.set_cursor_mode(CursorMode::Disabled);
    }

    window.set_all_polling(true);
    glfw.set_swap_interval(SwapInterval::Sync(1));

    // init OpenGL
    gl::load_with(|s| window.get_proc_address(s) as *const c_void);

    let gfx_state = GraphicsState::new().map_err(GlfwSurfaceError::GraphicsStateError)?;
    let surface = GlfwSurface {
      window,
      events_rx,
      gfx_state: Rc::new(RefCell::new(gfx_state)),
    };

    Ok(surface)
  }

  fn size(&self) -> [u32; 2] {
    let (x, y) = self.window.get_framebuffer_size();
    [x as u32, y as u32]
  }

  fn wait_events<'a>(&'a mut self) -> Box<Iterator<Item = Self::Event> + 'a> {
    self.window.glfw.wait_events();
    Box::new(self.events_rx.iter().map(|(_, e)| e))
  }

  fn poll_events<'a>(&'a mut self) -> Box<Iterator<Item = Self::Event> + 'a> {
    self.window.glfw.poll_events();
    Box::new(self.events_rx.try_iter().map(|(_, e)| e))
  }
}