diff --git a/window/build.rs b/window/build.rs index 6c8d60c98..108a41538 100644 --- a/window/build.rs +++ b/window/build.rs @@ -41,5 +41,13 @@ fn main() { } .unwrap() } + + if target.contains("windows") { + let mut file = File::create(&dest.join("wgl_bindings.rs")).unwrap(); + let reg = Registry::new(Api::Wgl, (1, 0), Profile::Core, Fallbacks::All, []); + + reg.write_bindings(gl_generator::StructGenerator, &mut file) + .unwrap() + } } } diff --git a/window/src/lib.rs b/window/src/lib.rs index 5e5aef49d..aae4273de 100644 --- a/window/src/lib.rs +++ b/window/src/lib.rs @@ -7,7 +7,11 @@ pub mod os; mod spawn; mod tasks; -#[cfg(all(not(target_os = "macos"), feature = "opengl"))] +#[cfg(all( + not(target_os = "macos"), + not(target_os = "windows"), + feature = "opengl" +))] mod egl; #[cfg(feature = "opengl")] diff --git a/window/src/os/windows/mod.rs b/window/src/os/windows/mod.rs index 4da87e77b..ff2e3710a 100644 --- a/window/src/os/windows/mod.rs +++ b/window/src/os/windows/mod.rs @@ -1,6 +1,7 @@ pub mod connection; pub mod event; pub mod gdi; +mod wgl; pub mod window; pub use connection::*; diff --git a/window/src/os/windows/wgl.rs b/window/src/os/windows/wgl.rs new file mode 100644 index 000000000..bafa58c0a --- /dev/null +++ b/window/src/os/windows/wgl.rs @@ -0,0 +1,122 @@ +#![cfg(feature = "opengl")] + +use failure::Fallible; +use std::os::raw::c_void; +use winapi::shared::windef::*; +use winapi::um::wingdi::*; +use winapi::um::winuser::*; + +pub mod ffi { + include!(concat!(env!("OUT_DIR"), "/wgl_bindings.rs")); +} + +pub struct WglWrapper { + lib: libloading::Library, + wgl: ffi::Wgl, +} + +type GetProcAddressFunc = + unsafe extern "system" fn(*const std::os::raw::c_char) -> *const std::os::raw::c_void; + +impl WglWrapper { + pub fn create() -> Fallible { + let lib = libloading::Library::new("opengl32.dll")?; + + let get_proc_address: libloading::Symbol = + unsafe { lib.get(b"wglGetProcAddress\0")? }; + let wgl = ffi::Wgl::load_with(|s: &'static str| { + let sym_name = std::ffi::CString::new(s).expect("symbol to be cstring compatible"); + if let Ok(sym) = unsafe { lib.get(sym_name.as_bytes_with_nul()) } { + return *sym; + } + unsafe { get_proc_address(sym_name.as_ptr()) } + }); + Ok(Self { lib, wgl }) + } +} + +pub struct GlState { + wgl: WglWrapper, + hdc: HDC, + rc: ffi::types::HGLRC, +} + +impl GlState { + pub fn create(window: HWND) -> Fallible { + let wgl = WglWrapper::create()?; + + let hdc = unsafe { GetDC(window) }; + + let pfd = PIXELFORMATDESCRIPTOR { + nSize: std::mem::size_of::() as u16, + nVersion: 1, + dwFlags: PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, + iPixelType: PFD_TYPE_RGBA, + cColorBits: 24, + cRedBits: 0, + cRedShift: 0, + cGreenBits: 0, + cGreenShift: 0, + cBlueBits: 0, + cBlueShift: 0, + cAlphaBits: 8, + cAlphaShift: 0, + cAccumBits: 0, + cAccumRedBits: 0, + cAccumGreenBits: 0, + cAccumBlueBits: 0, + cAccumAlphaBits: 0, + cDepthBits: 24, + cStencilBits: 8, + cAuxBuffers: 0, + iLayerType: PFD_MAIN_PLANE, + bReserved: 0, + dwLayerMask: 0, + dwVisibleMask: 0, + dwDamageMask: 0, + }; + let format = unsafe { ChoosePixelFormat(hdc, &pfd) }; + unsafe { + SetPixelFormat(hdc, format, &pfd); + } + + let rc = unsafe { wgl.wgl.CreateContext(hdc as *mut _) }; + unsafe { + wgl.wgl.MakeCurrent(hdc as *mut _, rc); + } + + Ok(Self { wgl, rc, hdc }) + } +} + +unsafe impl glium::backend::Backend for GlState { + fn swap_buffers(&self) -> Result<(), glium::SwapBuffersError> { + unsafe { + SwapBuffers(self.hdc); + } + Ok(()) + } + + unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void { + let sym_name = std::ffi::CString::new(symbol).expect("symbol to be cstring compatible"); + if let Ok(sym) = self.wgl.lib.get(sym_name.as_bytes_with_nul()) { + //eprintln!("{} -> {:?}", symbol, sym); + return *sym; + } + let res = self.wgl.wgl.GetProcAddress(sym_name.as_ptr()) as *const c_void; + // eprintln!("{} -> {:?}", symbol, res); + res + } + + fn get_framebuffer_dimensions(&self) -> (u32, u32) { + unimplemented!(); + } + + fn is_current(&self) -> bool { + unsafe { self.wgl.wgl.GetCurrentContext() == self.rc } + } + + unsafe fn make_current(&self) { + self.wgl.wgl.MakeCurrent(self.hdc as *mut _, self.rc); + } +} diff --git a/window/src/os/windows/window.rs b/window/src/os/windows/window.rs index da5199070..77a8af295 100644 --- a/window/src/os/windows/window.rs +++ b/window/src/os/windows/window.rs @@ -31,6 +31,8 @@ pub(crate) struct WindowInner { hwnd: HWindow, callbacks: RefCell>, bitmap: RefCell, + #[cfg(feature = "opengl")] + gl_state: Option>, } #[derive(Debug, Clone)] @@ -166,6 +168,8 @@ impl Window { hwnd: HWindow(null_mut()), callbacks: RefCell::new(callbacks), bitmap: RefCell::new(GdiBitmap::new_empty()), + #[cfg(feature = "opengl")] + gl_state: None, })); // Careful: `raw` owns a ref to inner, but there is no Drop impl @@ -291,17 +295,23 @@ impl WindowOps for Window { Connection::with_window_inner(self.0, move |inner| { let window = Window(inner.hwnd); - let dc = unsafe { GetDC(inner.hwnd.0) }; - match crate::egl::GlState::create(Some(dc as *const _), inner.hwnd.0) { - Ok(_) => eprintln!("EGL initialized!?"), - Err(err) => eprintln!("EGL: {}", err), - }; + let gl_state = super::wgl::GlState::create(inner.hwnd.0) + .map(Rc::new) + .and_then(|state| unsafe { + Ok(glium::backend::Context::new( + Rc::clone(&state), + true, + if cfg!(debug_assertions) { + glium::debug::DebugCallbackBehavior::DebugMessageOnError + } else { + glium::debug::DebugCallbackBehavior::Ignore + }, + )?) + }); - func( - inner.callbacks.borrow_mut().as_any(), - &window, - Err(failure::err_msg("opengl not supported in this build")), - ); + inner.gl_state = gl_state.as_ref().map(Rc::clone).ok(); + + func(inner.callbacks.borrow_mut().as_any(), &window, gl_state); }); } } @@ -449,6 +459,19 @@ unsafe fn wm_paint(hwnd: HWND, _msg: UINT, _wparam: WPARAM, _lparam: LPARAM) -> let width = rect_width(&rect) as usize; let height = rect_height(&rect) as usize; + #[cfg(feature = "opengl")] + { + if let Some(gl_context) = inner.gl_state.as_ref() { + let mut frame = + glium::Frame::new(Rc::clone(&gl_context), (width as u32, height as u32)); + + inner.callbacks.borrow_mut().paint_opengl(&mut frame); + frame.finish().expect("frame.finish failed"); + EndPaint(hwnd, &mut ps); + return Some(0); + } + } + if width > 0 && height > 0 { let mut bitmap = inner.bitmap.borrow_mut(); let (bm_width, bm_height) = bitmap.image_dimensions();