1 #![cfg(target_os = "windows")]
2 
3 use crate::{
4     Api, ContextCurrentState, ContextError, CreationError, GlAttributes,
5     GlRequest, NotCurrent, PixelFormat, PixelFormatRequirements,
6 };
7 
8 use crate::api::egl::{
9     Context as EglContext, NativeDisplay, SurfaceType as EglSurfaceType, EGL,
10 };
11 use crate::api::wgl::Context as WglContext;
12 use crate::os::windows::WindowExt;
13 
14 use glutin_egl_sys as ffi;
15 use winapi::shared::windef::{HGLRC, HWND};
16 use winit;
17 use winit::dpi;
18 
19 use std::marker::PhantomData;
20 use std::os::raw;
21 
22 /// Context handles available on Windows.
23 #[derive(Clone, Debug)]
24 pub enum RawHandle {
25     Egl(ffi::EGLContext),
26     Wgl(HGLRC),
27 }
28 
29 #[derive(Debug)]
30 pub enum Context {
31     /// A regular window
32     Egl(EglContext),
33     Wgl(WglContext),
34     /// A regular window, but invisible.
35     HiddenWindowEgl(winit::Window, EglContext),
36     HiddenWindowWgl(winit::Window, WglContext),
37     /// An EGL pbuffer.
38     EglPbuffer(EglContext),
39 }
40 
41 unsafe impl Send for Context {}
42 unsafe impl Sync for Context {}
43 
44 impl Context {
45     /// See the docs in the crate root file.
46     #[inline]
new_windowed( wb: winit::WindowBuilder, el: &winit::EventsLoop, pf_reqs: &PixelFormatRequirements, gl_attr: &GlAttributes<&Self>, ) -> Result<(winit::Window, Self), CreationError>47     pub fn new_windowed(
48         wb: winit::WindowBuilder,
49         el: &winit::EventsLoop,
50         pf_reqs: &PixelFormatRequirements,
51         gl_attr: &GlAttributes<&Self>,
52     ) -> Result<(winit::Window, Self), CreationError> {
53         let win = wb.build(el)?;
54         let hwnd = win.get_hwnd() as HWND;
55         let ctx = Self::new_raw_context(hwnd, pf_reqs, gl_attr)?;
56 
57         Ok((win, ctx))
58     }
59 
60     #[inline]
new_raw_context( hwnd: HWND, pf_reqs: &PixelFormatRequirements, gl_attr: &GlAttributes<&Self>, ) -> Result<Self, CreationError>61     pub fn new_raw_context(
62         hwnd: HWND,
63         pf_reqs: &PixelFormatRequirements,
64         gl_attr: &GlAttributes<&Self>,
65     ) -> Result<Self, CreationError> {
66         match gl_attr.version {
67             GlRequest::Specific(Api::OpenGlEs, (_major, _minor)) => {
68                 match (gl_attr.sharing, &*EGL) {
69                     // We must use WGL.
70                     (Some(&Context::HiddenWindowWgl(_, _)), _)
71                     | (Some(&Context::Wgl(_)), _)
72                     | (None, None) => {
73                         let gl_attr_wgl =
74                             gl_attr.clone().map_sharing(|ctx| match *ctx {
75                                 Context::HiddenWindowWgl(_, ref c)
76                                 | Context::Wgl(ref c) => c.get_hglrc(),
77                                 _ => unreachable!(),
78                             });
79                         unsafe {
80                             WglContext::new(&pf_reqs, &gl_attr_wgl, hwnd)
81                                 .map(Context::Wgl)
82                         }
83                     }
84                     // We must use EGL.
85                     (Some(_), Some(_)) => {
86                         let gl_attr_egl =
87                             gl_attr.clone().map_sharing(|ctx| match *ctx {
88                                 Context::Egl(ref c)
89                                 | Context::EglPbuffer(ref c)
90                                 | Context::HiddenWindowEgl(_, ref c) => c,
91                                 _ => unreachable!(),
92                             });
93 
94                         EglContext::new(
95                             &pf_reqs,
96                             &gl_attr_egl,
97                             NativeDisplay::Other(Some(std::ptr::null())),
98                             EglSurfaceType::Window,
99                         )
100                         .and_then(|p| p.finish(hwnd))
101                         .map(|c| Context::Egl(c))
102                     }
103                     // Try EGL, fallback to WGL.
104                     (None, Some(_)) => {
105                         let gl_attr_egl =
106                             gl_attr.clone().map_sharing(|_| unreachable!());
107                         let gl_attr_wgl =
108                             gl_attr.clone().map_sharing(|_| unreachable!());
109 
110                         if let Ok(c) = EglContext::new(
111                             &pf_reqs,
112                             &gl_attr_egl,
113                             NativeDisplay::Other(Some(std::ptr::null())),
114                             EglSurfaceType::Window,
115                         )
116                         .and_then(|p| p.finish(hwnd))
117                         {
118                             Ok(Context::Egl(c))
119                         } else {
120                             unsafe {
121                                 WglContext::new(&pf_reqs, &gl_attr_wgl, hwnd)
122                                     .map(Context::Wgl)
123                             }
124                         }
125                     }
126                     _ => panic!(),
127                 }
128             }
129             _ => {
130                 let gl_attr_wgl =
131                     gl_attr.clone().map_sharing(|ctx| match *ctx {
132                         Context::HiddenWindowWgl(_, ref c)
133                         | Context::Wgl(ref c) => c.get_hglrc(),
134                         _ => panic!(),
135                     });
136                 unsafe {
137                     WglContext::new(&pf_reqs, &gl_attr_wgl, hwnd)
138                         .map(Context::Wgl)
139                 }
140             }
141         }
142     }
143 
144     #[inline]
new_headless( el: &winit::EventsLoop, pf_reqs: &PixelFormatRequirements, gl_attr: &GlAttributes<&Context>, size: dpi::PhysicalSize, ) -> Result<Self, CreationError>145     pub fn new_headless(
146         el: &winit::EventsLoop,
147         pf_reqs: &PixelFormatRequirements,
148         gl_attr: &GlAttributes<&Context>,
149         size: dpi::PhysicalSize,
150     ) -> Result<Self, CreationError> {
151         // if EGL is available, we try using EGL first
152         // if EGL returns an error, we try the hidden window method
153         match (gl_attr.sharing, &*EGL) {
154             (None, Some(_))
155             | (Some(&Context::Egl(_)), Some(_))
156             | (Some(&Context::HiddenWindowEgl(_, _)), Some(_))
157             | (Some(&Context::EglPbuffer(_)), Some(_)) => {
158                 let gl_attr_egl =
159                     gl_attr.clone().map_sharing(|ctx| match *ctx {
160                         Context::Egl(ref c)
161                         | Context::EglPbuffer(ref c)
162                         | Context::HiddenWindowEgl(_, ref c) => c,
163                         _ => unreachable!(),
164                     });
165 
166                 let native_display = NativeDisplay::Other(None);
167                 let context = EglContext::new(
168                     pf_reqs,
169                     &gl_attr_egl,
170                     native_display,
171                     EglSurfaceType::PBuffer,
172                 )
173                 .and_then(|prototype| prototype.finish_pbuffer(size))
174                 .map(|ctx| Context::EglPbuffer(ctx));
175 
176                 if let Ok(context) = context {
177                     return Ok(context);
178                 }
179             }
180             _ => (),
181         }
182 
183         let wb = winit::WindowBuilder::new()
184             .with_visibility(false)
185             .with_dimensions(size.to_logical(1.));
186         Self::new_windowed(wb, &el, pf_reqs, gl_attr).map(|(win, context)| {
187             match context {
188                 Context::Egl(context) => Context::HiddenWindowEgl(win, context),
189                 Context::Wgl(context) => Context::HiddenWindowWgl(win, context),
190                 _ => unreachable!(),
191             }
192         })
193     }
194 
195     #[inline]
resize(&self, _width: u32, _height: u32)196     pub fn resize(&self, _width: u32, _height: u32) {
197         // Method is for API consistency.
198     }
199 
200     #[inline]
make_current(&self) -> Result<(), ContextError>201     pub unsafe fn make_current(&self) -> Result<(), ContextError> {
202         match *self {
203             Context::Wgl(ref c) | Context::HiddenWindowWgl(_, ref c) => {
204                 c.make_current()
205             }
206             Context::Egl(ref c)
207             | Context::HiddenWindowEgl(_, ref c)
208             | Context::EglPbuffer(ref c) => c.make_current(),
209         }
210     }
211 
212     #[inline]
make_not_current(&self) -> Result<(), ContextError>213     pub unsafe fn make_not_current(&self) -> Result<(), ContextError> {
214         match *self {
215             Context::Wgl(ref c) | Context::HiddenWindowWgl(_, ref c) => {
216                 c.make_not_current()
217             }
218             Context::Egl(ref c)
219             | Context::HiddenWindowEgl(_, ref c)
220             | Context::EglPbuffer(ref c) => c.make_not_current(),
221         }
222     }
223 
224     #[inline]
is_current(&self) -> bool225     pub fn is_current(&self) -> bool {
226         match *self {
227             Context::Wgl(ref c) | Context::HiddenWindowWgl(_, ref c) => {
228                 c.is_current()
229             }
230             Context::Egl(ref c)
231             | Context::HiddenWindowEgl(_, ref c)
232             | Context::EglPbuffer(ref c) => c.is_current(),
233         }
234     }
235 
236     #[inline]
get_proc_address(&self, addr: &str) -> *const ()237     pub fn get_proc_address(&self, addr: &str) -> *const () {
238         match *self {
239             Context::Wgl(ref c) | Context::HiddenWindowWgl(_, ref c) => {
240                 c.get_proc_address(addr)
241             }
242             Context::Egl(ref c)
243             | Context::HiddenWindowEgl(_, ref c)
244             | Context::EglPbuffer(ref c) => c.get_proc_address(addr),
245         }
246     }
247 
248     #[inline]
swap_buffers(&self) -> Result<(), ContextError>249     pub fn swap_buffers(&self) -> Result<(), ContextError> {
250         match *self {
251             Context::Wgl(ref c) => c.swap_buffers(),
252             Context::Egl(ref c) => c.swap_buffers(),
253             _ => unreachable!(),
254         }
255     }
256 
257     #[inline]
get_api(&self) -> Api258     pub fn get_api(&self) -> Api {
259         match *self {
260             Context::Wgl(ref c) | Context::HiddenWindowWgl(_, ref c) => {
261                 c.get_api()
262             }
263             Context::Egl(ref c)
264             | Context::HiddenWindowEgl(_, ref c)
265             | Context::EglPbuffer(ref c) => c.get_api(),
266         }
267     }
268 
269     #[inline]
get_pixel_format(&self) -> PixelFormat270     pub fn get_pixel_format(&self) -> PixelFormat {
271         match *self {
272             Context::Wgl(ref c) => c.get_pixel_format(),
273             Context::Egl(ref c) => c.get_pixel_format(),
274             _ => unreachable!(),
275         }
276     }
277 
278     #[inline]
raw_handle(&self) -> RawHandle279     pub unsafe fn raw_handle(&self) -> RawHandle {
280         match *self {
281             Context::Wgl(ref c) | Context::HiddenWindowWgl(_, ref c) => {
282                 RawHandle::Wgl(c.get_hglrc())
283             }
284             Context::Egl(ref c)
285             | Context::HiddenWindowEgl(_, ref c)
286             | Context::EglPbuffer(ref c) => RawHandle::Egl(c.raw_handle()),
287         }
288     }
289 
290     #[inline]
get_egl_display(&self) -> Option<*const raw::c_void>291     pub unsafe fn get_egl_display(&self) -> Option<*const raw::c_void> {
292         match *self {
293             Context::Egl(ref c)
294             | Context::HiddenWindowEgl(_, ref c)
295             | Context::EglPbuffer(ref c) => Some(c.get_egl_display()),
296             _ => None,
297         }
298     }
299 }
300 
301 pub trait RawContextExt {
302     /// Creates a raw context on the provided window.
303     ///
304     /// Unsafe behaviour might happen if you:
305     ///   - Provide us with invalid parameters.
306     ///   - The window is destroyed before the context
build_raw_context( self, hwnd: *mut raw::c_void, ) -> Result<crate::RawContext<NotCurrent>, CreationError> where Self: Sized307     unsafe fn build_raw_context(
308         self,
309         hwnd: *mut raw::c_void,
310     ) -> Result<crate::RawContext<NotCurrent>, CreationError>
311     where
312         Self: Sized;
313 }
314 
315 impl<'a, T: ContextCurrentState> RawContextExt
316     for crate::ContextBuilder<'a, T>
317 {
318     #[inline]
build_raw_context( self, hwnd: *mut raw::c_void, ) -> Result<crate::RawContext<NotCurrent>, CreationError> where Self: Sized,319     unsafe fn build_raw_context(
320         self,
321         hwnd: *mut raw::c_void,
322     ) -> Result<crate::RawContext<NotCurrent>, CreationError>
323     where
324         Self: Sized,
325     {
326         let crate::ContextBuilder { pf_reqs, gl_attr } = self;
327         let gl_attr = gl_attr.map_sharing(|ctx| &ctx.context);
328         Context::new_raw_context(hwnd as *mut _, &pf_reqs, &gl_attr)
329             .map(|context| crate::Context {
330                 context,
331                 phantom: PhantomData,
332             })
333             .map(|context| crate::RawContext {
334                 context,
335                 window: (),
336             })
337     }
338 }
339