1 #![cfg(any(target_os = "windows"))]
2 
3 mod make_current_guard;
4 
5 use crate::{
6     Api, ContextError, CreationError, GlAttributes, GlProfile, GlRequest,
7     PixelFormat, PixelFormatRequirements, ReleaseBehavior, Robustness,
8 };
9 
10 use self::make_current_guard::CurrentContextGuard;
11 
12 use glutin_wgl_sys as gl;
13 use winapi::shared::minwindef::HMODULE;
14 use winapi::shared::minwindef::*;
15 use winapi::shared::ntdef::LPCWSTR;
16 use winapi::shared::windef::{HDC, HGLRC, HWND};
17 use winapi::um::libloaderapi::*;
18 use winapi::um::wingdi::*;
19 use winapi::um::winuser::*;
20 
21 use std::ffi::{CStr, CString, OsStr};
22 use std::os::raw;
23 use std::os::windows::ffi::OsStrExt;
24 
25 /// A WGL context.
26 ///
27 /// Note: should be destroyed before its window.
28 #[derive(Debug)]
29 pub struct Context {
30     context: ContextWrapper,
31 
32     hdc: HDC,
33 
34     /// Bound to `opengl32.dll`.
35     ///
36     /// `wglGetProcAddress` returns null for GL 1.1 functions because they are
37     ///  already defined by the system. This module contains them.
38     gl_library: HMODULE,
39 
40     /// The pixel format that has been used to create this context.
41     pixel_format: PixelFormat,
42 }
43 
44 /// A simple wrapper that destroys the window when it is destroyed.
45 #[derive(Debug)]
46 struct WindowWrapper(HWND, HDC);
47 
48 impl Drop for WindowWrapper {
49     #[inline]
drop(&mut self)50     fn drop(&mut self) {
51         unsafe {
52             DestroyWindow(self.0);
53         }
54     }
55 }
56 
57 /// Wraps around a context so that it is destroyed when necessary.
58 #[derive(Debug)]
59 struct ContextWrapper(HGLRC);
60 
61 impl Drop for ContextWrapper {
62     #[inline]
drop(&mut self)63     fn drop(&mut self) {
64         unsafe {
65             gl::wgl::DeleteContext(self.0 as *const _);
66         }
67     }
68 }
69 
70 impl Context {
71     /// Attempt to build a new WGL context on a window.
72     ///
73     /// # Unsafety
74     ///
75     /// The `window` must continue to exist as long as the resulting `Context`
76     /// exists.
77     #[inline]
new( pf_reqs: &PixelFormatRequirements, opengl: &GlAttributes<HGLRC>, win: HWND, ) -> Result<Context, CreationError>78     pub unsafe fn new(
79         pf_reqs: &PixelFormatRequirements,
80         opengl: &GlAttributes<HGLRC>,
81         win: HWND,
82     ) -> Result<Context, CreationError> {
83         let hdc = GetDC(win);
84         if hdc.is_null() {
85             let err = Err(CreationError::OsError(format!(
86                 "GetDC function failed: {}",
87                 std::io::Error::last_os_error()
88             )));
89             return err;
90         }
91 
92         // loading the functions that are not guaranteed to be supported
93         let extra_functions = load_extra_functions(win)?;
94 
95         // getting the list of the supported extensions
96         let extensions = if extra_functions.GetExtensionsStringARB.is_loaded() {
97             let data = extra_functions.GetExtensionsStringARB(hdc as *const _);
98             let data = CStr::from_ptr(data).to_bytes().to_vec();
99             String::from_utf8(data).unwrap()
100         } else if extra_functions.GetExtensionsStringEXT.is_loaded() {
101             let data = extra_functions.GetExtensionsStringEXT();
102             let data = CStr::from_ptr(data).to_bytes().to_vec();
103             String::from_utf8(data).unwrap()
104         } else {
105             format!("")
106         };
107 
108         let use_arb_for_pixel_format = extensions
109             .split(' ')
110             .find(|&i| i == "WGL_ARB_pixel_format")
111             .is_some();
112 
113         // calling SetPixelFormat, if not already done
114         let mut pixel_format_id = GetPixelFormat(hdc);
115         if pixel_format_id == 0 {
116             let id = if use_arb_for_pixel_format {
117                 choose_arb_pixel_format_id(
118                     &extra_functions,
119                     &extensions,
120                     hdc,
121                     pf_reqs,
122                 )
123                 .map_err(|_| CreationError::NoAvailablePixelFormat)?
124             } else {
125                 choose_native_pixel_format_id(hdc, pf_reqs)
126                     .map_err(|_| CreationError::NoAvailablePixelFormat)?
127             };
128 
129             set_pixel_format(hdc, id)?;
130             pixel_format_id = id;
131         }
132 
133         let pixel_format = if use_arb_for_pixel_format {
134             choose_arb_pixel_format(
135                 &extra_functions,
136                 &extensions,
137                 hdc,
138                 pixel_format_id,
139             )
140             .map_err(|_| CreationError::NoAvailablePixelFormat)?
141         } else {
142             choose_native_pixel_format(hdc, pf_reqs, pixel_format_id)
143                 .map_err(|_| CreationError::NoAvailablePixelFormat)?
144         };
145 
146         // creating the OpenGL context
147         let context = create_context(
148             Some((&extra_functions, pf_reqs, opengl, &extensions)),
149             win,
150             hdc,
151         )?;
152 
153         // loading the opengl32 module
154         let gl_library = load_opengl32_dll()?;
155 
156         // handling vsync
157         if extensions
158             .split(' ')
159             .find(|&i| i == "WGL_EXT_swap_control")
160             .is_some()
161         {
162             let _guard = CurrentContextGuard::make_current(hdc, context.0)?;
163 
164             if extra_functions.SwapIntervalEXT(if opengl.vsync { 1 } else { 0 })
165                 == 0
166             {
167                 return Err(CreationError::OsError(
168                     "wglSwapIntervalEXT failed".to_string(),
169                 ));
170             }
171         }
172 
173         Ok(Context {
174             context,
175             hdc,
176             gl_library,
177             pixel_format,
178         })
179     }
180 
181     /// Returns the raw HGLRC.
182     #[inline]
get_hglrc(&self) -> HGLRC183     pub fn get_hglrc(&self) -> HGLRC {
184         self.context.0
185     }
186 
187     #[inline]
make_current(&self) -> Result<(), ContextError>188     pub unsafe fn make_current(&self) -> Result<(), ContextError> {
189         if gl::wgl::MakeCurrent(
190             self.hdc as *const _,
191             self.context.0 as *const _,
192         ) != 0
193         {
194             Ok(())
195         } else {
196             Err(ContextError::IoError(std::io::Error::last_os_error()))
197         }
198     }
199 
200     #[inline]
make_not_current(&self) -> Result<(), ContextError>201     pub unsafe fn make_not_current(&self) -> Result<(), ContextError> {
202         if self.is_current()
203             && gl::wgl::MakeCurrent(self.hdc as *const _, std::ptr::null()) != 0
204         {
205             Ok(())
206         } else {
207             Err(ContextError::IoError(std::io::Error::last_os_error()))
208         }
209     }
210 
211     #[inline]
is_current(&self) -> bool212     pub fn is_current(&self) -> bool {
213         unsafe {
214             gl::wgl::GetCurrentContext() == self.context.0 as *const raw::c_void
215         }
216     }
217 
get_proc_address(&self, addr: &str) -> *const ()218     pub fn get_proc_address(&self, addr: &str) -> *const () {
219         let addr = CString::new(addr.as_bytes()).unwrap();
220         let addr = addr.as_ptr();
221 
222         unsafe {
223             let p = gl::wgl::GetProcAddress(addr) as *const ();
224             if !p.is_null() {
225                 return p;
226             }
227             GetProcAddress(self.gl_library, addr) as *const _
228         }
229     }
230 
231     #[inline]
swap_buffers(&self) -> Result<(), ContextError>232     pub fn swap_buffers(&self) -> Result<(), ContextError> {
233         // TODO: decide how to handle the error
234         // if unsafe { SwapBuffers(self.hdc) } != 0 {
235         // Ok(())
236         // } else {
237         // Err(ContextError::IoError(std::io::Error::last_os_error()))
238         // }
239         unsafe { SwapBuffers(self.hdc) };
240         Ok(())
241     }
242 
243     #[inline]
get_api(&self) -> Api244     pub fn get_api(&self) -> Api {
245         // FIXME: can be opengl es
246         Api::OpenGl
247     }
248 
249     #[inline]
get_pixel_format(&self) -> PixelFormat250     pub fn get_pixel_format(&self) -> PixelFormat {
251         self.pixel_format.clone()
252     }
253 }
254 
255 unsafe impl Send for Context {}
256 unsafe impl Sync for Context {}
257 
258 /// Creates an OpenGL context.
259 ///
260 /// If `extra` is `Some`, this function will attempt to use the latest WGL
261 /// functions to create the context.
262 ///
263 /// Otherwise, only the basic API will be used and the chances of
264 /// `CreationError::NotSupported` being returned increase.
create_context( extra: Option<( &gl::wgl_extra::Wgl, &PixelFormatRequirements, &GlAttributes<HGLRC>, &str, )>, _: HWND, hdc: HDC, ) -> Result<ContextWrapper, CreationError>265 unsafe fn create_context(
266     extra: Option<(
267         &gl::wgl_extra::Wgl,
268         &PixelFormatRequirements,
269         &GlAttributes<HGLRC>,
270         &str,
271     )>,
272     _: HWND,
273     hdc: HDC,
274 ) -> Result<ContextWrapper, CreationError> {
275     let share;
276 
277     if let Some((extra_functions, _pf_reqs, opengl, extensions)) = extra {
278         share = opengl.sharing.unwrap_or(std::ptr::null_mut());
279 
280         if extensions
281             .split(' ')
282             .find(|&i| i == "WGL_ARB_create_context")
283             .is_some()
284         {
285             let mut attributes = Vec::new();
286 
287             match opengl.version {
288                 GlRequest::Latest => {}
289                 GlRequest::Specific(Api::OpenGl, (major, minor)) => {
290                     attributes.push(
291                         gl::wgl_extra::CONTEXT_MAJOR_VERSION_ARB as raw::c_int,
292                     );
293                     attributes.push(major as raw::c_int);
294                     attributes.push(
295                         gl::wgl_extra::CONTEXT_MINOR_VERSION_ARB as raw::c_int,
296                     );
297                     attributes.push(minor as raw::c_int);
298                 }
299                 GlRequest::Specific(Api::OpenGlEs, (major, minor)) => {
300                     if extensions
301                         .split(' ')
302                         .find(|&i| i == "WGL_EXT_create_context_es2_profile")
303                         .is_some()
304                     {
305                         attributes.push(
306                             gl::wgl_extra::CONTEXT_PROFILE_MASK_ARB
307                                 as raw::c_int,
308                         );
309                         attributes.push(
310                             gl::wgl_extra::CONTEXT_ES2_PROFILE_BIT_EXT
311                                 as raw::c_int,
312                         );
313                     } else {
314                         return Err(CreationError::OpenGlVersionNotSupported);
315                     }
316 
317                     attributes.push(
318                         gl::wgl_extra::CONTEXT_MAJOR_VERSION_ARB as raw::c_int,
319                     );
320                     attributes.push(major as raw::c_int);
321                     attributes.push(
322                         gl::wgl_extra::CONTEXT_MINOR_VERSION_ARB as raw::c_int,
323                     );
324                     attributes.push(minor as raw::c_int);
325                 }
326                 GlRequest::Specific(_, _) => {
327                     return Err(CreationError::OpenGlVersionNotSupported);
328                 }
329                 GlRequest::GlThenGles {
330                     opengl_version: (major, minor),
331                     ..
332                 } => {
333                     attributes.push(
334                         gl::wgl_extra::CONTEXT_MAJOR_VERSION_ARB as raw::c_int,
335                     );
336                     attributes.push(major as raw::c_int);
337                     attributes.push(
338                         gl::wgl_extra::CONTEXT_MINOR_VERSION_ARB as raw::c_int,
339                     );
340                     attributes.push(minor as raw::c_int);
341                 }
342             }
343 
344             if let Some(profile) = opengl.profile {
345                 if extensions
346                     .split(' ')
347                     .find(|&i| i == "WGL_ARB_create_context_profile")
348                     .is_some()
349                 {
350                     let flag = match profile {
351                         GlProfile::Compatibility => {
352                             gl::wgl_extra::CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB
353                         }
354                         GlProfile::Core => {
355                             gl::wgl_extra::CONTEXT_CORE_PROFILE_BIT_ARB
356                         }
357                     };
358                     attributes.push(
359                         gl::wgl_extra::CONTEXT_PROFILE_MASK_ARB as raw::c_int,
360                     );
361                     attributes.push(flag as raw::c_int);
362                 } else {
363                     return Err(CreationError::NotSupported(
364                         "required extension \"WGL_ARB_create_context_profile\" not found".to_string(),
365                     ));
366                 }
367             }
368 
369             let flags = {
370                 let mut flags = 0;
371 
372                 // robustness
373                 if extensions
374                     .split(' ')
375                     .find(|&i| i == "WGL_ARB_create_context_robustness")
376                     .is_some()
377                 {
378                     match opengl.robustness {
379                         Robustness::RobustNoResetNotification
380                         | Robustness::TryRobustNoResetNotification => {
381                             attributes.push(
382                                 gl::wgl_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB as raw::c_int,
383                             );
384                             attributes.push(
385                                 gl::wgl_extra::NO_RESET_NOTIFICATION_ARB
386                                     as raw::c_int,
387                             );
388                             flags = flags
389                                 | gl::wgl_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB
390                                     as raw::c_int;
391                         }
392                         Robustness::RobustLoseContextOnReset
393                         | Robustness::TryRobustLoseContextOnReset => {
394                             attributes.push(
395                                 gl::wgl_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB as raw::c_int,
396                             );
397                             attributes.push(
398                                 gl::wgl_extra::LOSE_CONTEXT_ON_RESET_ARB
399                                     as raw::c_int,
400                             );
401                             flags = flags
402                                 | gl::wgl_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB
403                                     as raw::c_int;
404                         }
405                         Robustness::NotRobust => (),
406                         Robustness::NoError => (),
407                     }
408                 } else {
409                     match opengl.robustness {
410                         Robustness::RobustNoResetNotification
411                         | Robustness::RobustLoseContextOnReset => {
412                             return Err(CreationError::RobustnessNotSupported);
413                         }
414                         _ => (),
415                     }
416                 }
417 
418                 if opengl.debug {
419                     flags = flags
420                         | gl::wgl_extra::CONTEXT_DEBUG_BIT_ARB as raw::c_int;
421                 }
422 
423                 flags
424             };
425 
426             attributes.push(gl::wgl_extra::CONTEXT_FLAGS_ARB as raw::c_int);
427             attributes.push(flags);
428 
429             attributes.push(0);
430 
431             let ctx = extra_functions.CreateContextAttribsARB(
432                 hdc as *const raw::c_void,
433                 share as *const raw::c_void,
434                 attributes.as_ptr(),
435             );
436 
437             if ctx.is_null() {
438                 return Err(CreationError::OsError(format!(
439                     "wglCreateContextAttribsARB failed: {}",
440                     std::io::Error::last_os_error()
441                 )));
442             } else {
443                 return Ok(ContextWrapper(ctx as HGLRC));
444             }
445         }
446     } else {
447         share = std::ptr::null_mut();
448     }
449 
450     let ctx = gl::wgl::CreateContext(hdc as *const raw::c_void);
451     if ctx.is_null() {
452         return Err(CreationError::OsError(format!(
453             "wglCreateContext failed: {}",
454             std::io::Error::last_os_error()
455         )));
456     }
457 
458     if !share.is_null() {
459         if gl::wgl::ShareLists(share as *const raw::c_void, ctx) == 0 {
460             return Err(CreationError::OsError(format!(
461                 "wglShareLists failed: {}",
462                 std::io::Error::last_os_error()
463             )));
464         }
465     };
466 
467     Ok(ContextWrapper(ctx as HGLRC))
468 }
469 
470 /// Chooses a pixel formats without using WGL.
471 ///
472 /// Gives less precise results than `enumerate_arb_pixel_formats`.
choose_native_pixel_format_id( hdc: HDC, reqs: &PixelFormatRequirements, ) -> Result<raw::c_int, ()>473 unsafe fn choose_native_pixel_format_id(
474     hdc: HDC,
475     reqs: &PixelFormatRequirements,
476 ) -> Result<raw::c_int, ()> {
477     // TODO: hardware acceleration is not handled
478 
479     // handling non-supported stuff
480     if reqs.float_color_buffer {
481         return Err(());
482     }
483 
484     match reqs.multisampling {
485         Some(0) => (),
486         None => (),
487         Some(_) => return Err(()),
488     };
489 
490     if reqs.stereoscopy {
491         return Err(());
492     }
493 
494     if reqs.srgb {
495         return Err(());
496     }
497 
498     if reqs.release_behavior != ReleaseBehavior::Flush {
499         return Err(());
500     }
501 
502     // building the descriptor to pass to ChoosePixelFormat
503     let descriptor = PIXELFORMATDESCRIPTOR {
504         nSize: std::mem::size_of::<PIXELFORMATDESCRIPTOR>() as u16,
505         nVersion: 1,
506         dwFlags: {
507             let f1 = match reqs.double_buffer {
508                 None => PFD_DOUBLEBUFFER, /* Should be PFD_DOUBLEBUFFER_DONTCARE after you can choose */
509                 Some(true) => PFD_DOUBLEBUFFER,
510                 Some(false) => 0,
511             };
512 
513             let f2 = if reqs.stereoscopy { PFD_STEREO } else { 0 };
514 
515             PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | f1 | f2
516         },
517         iPixelType: PFD_TYPE_RGBA,
518         cColorBits: reqs.color_bits.unwrap_or(0),
519         cRedBits: 0,
520         cRedShift: 0,
521         cGreenBits: 0,
522         cGreenShift: 0,
523         cBlueBits: 0,
524         cBlueShift: 0,
525         cAlphaBits: reqs.alpha_bits.unwrap_or(0),
526         cAlphaShift: 0,
527         cAccumBits: 0,
528         cAccumRedBits: 0,
529         cAccumGreenBits: 0,
530         cAccumBlueBits: 0,
531         cAccumAlphaBits: 0,
532         cDepthBits: reqs.depth_bits.unwrap_or(0),
533         cStencilBits: reqs.stencil_bits.unwrap_or(0),
534         cAuxBuffers: 0,
535         iLayerType: PFD_MAIN_PLANE,
536         bReserved: 0,
537         dwLayerMask: 0,
538         dwVisibleMask: 0,
539         dwDamageMask: 0,
540     };
541 
542     // now querying
543     let pf_id = ChoosePixelFormat(hdc, &descriptor);
544     if pf_id == 0 {
545         return Err(());
546     }
547 
548     Ok(pf_id)
549 }
550 
choose_native_pixel_format( hdc: HDC, reqs: &PixelFormatRequirements, pf_id: raw::c_int, ) -> Result<PixelFormat, ()>551 unsafe fn choose_native_pixel_format(
552     hdc: HDC,
553     reqs: &PixelFormatRequirements,
554     pf_id: raw::c_int,
555 ) -> Result<PixelFormat, ()> {
556     // querying back the capabilities of what windows told us
557     let mut output: PIXELFORMATDESCRIPTOR = std::mem::zeroed();
558     if DescribePixelFormat(
559         hdc,
560         pf_id,
561         std::mem::size_of::<PIXELFORMATDESCRIPTOR>() as u32,
562         &mut output,
563     ) == 0
564     {
565         return Err(());
566     }
567 
568     // windows may return us a non-conforming pixel format if none are
569     // supported, so we have to check this
570     if (output.dwFlags & PFD_DRAW_TO_WINDOW) == 0 {
571         return Err(());
572     }
573     if (output.dwFlags & PFD_SUPPORT_OPENGL) == 0 {
574         return Err(());
575     }
576     if output.iPixelType != PFD_TYPE_RGBA {
577         return Err(());
578     }
579 
580     let pf_desc = PixelFormat {
581         hardware_accelerated: (output.dwFlags & PFD_GENERIC_FORMAT) == 0,
582         color_bits: output.cRedBits + output.cGreenBits + output.cBlueBits,
583         alpha_bits: output.cAlphaBits,
584         depth_bits: output.cDepthBits,
585         stencil_bits: output.cStencilBits,
586         stereoscopy: (output.dwFlags & PFD_STEREO) != 0,
587         double_buffer: (output.dwFlags & PFD_DOUBLEBUFFER) != 0,
588         multisampling: None,
589         srgb: false,
590     };
591 
592     if pf_desc.alpha_bits < reqs.alpha_bits.unwrap_or(0) {
593         return Err(());
594     }
595     if pf_desc.depth_bits < reqs.depth_bits.unwrap_or(0) {
596         return Err(());
597     }
598     if pf_desc.stencil_bits < reqs.stencil_bits.unwrap_or(0) {
599         return Err(());
600     }
601     if pf_desc.color_bits < reqs.color_bits.unwrap_or(0) {
602         return Err(());
603     }
604     if let Some(req) = reqs.hardware_accelerated {
605         if pf_desc.hardware_accelerated != req {
606             return Err(());
607         }
608     }
609     if let Some(req) = reqs.double_buffer {
610         if pf_desc.double_buffer != req {
611             return Err(());
612         }
613     }
614 
615     Ok(pf_desc)
616 }
617 
618 /// Enumerates the list of pixel formats by using extra WGL functions.
619 ///
620 /// Gives more precise results than `enumerate_native_pixel_formats`.
choose_arb_pixel_format_id( extra: &gl::wgl_extra::Wgl, extensions: &str, hdc: HDC, reqs: &PixelFormatRequirements, ) -> Result<raw::c_int, ()>621 unsafe fn choose_arb_pixel_format_id(
622     extra: &gl::wgl_extra::Wgl,
623     extensions: &str,
624     hdc: HDC,
625     reqs: &PixelFormatRequirements,
626 ) -> Result<raw::c_int, ()> {
627     let descriptor = {
628         let mut out: Vec<raw::c_int> = Vec::with_capacity(37);
629 
630         out.push(gl::wgl_extra::DRAW_TO_WINDOW_ARB as raw::c_int);
631         out.push(1);
632 
633         out.push(gl::wgl_extra::SUPPORT_OPENGL_ARB as raw::c_int);
634         out.push(1);
635 
636         out.push(gl::wgl_extra::PIXEL_TYPE_ARB as raw::c_int);
637         if reqs.float_color_buffer {
638             if extensions
639                 .split(' ')
640                 .find(|&i| i == "WGL_ARB_pixel_format_float")
641                 .is_some()
642             {
643                 out.push(gl::wgl_extra::TYPE_RGBA_FLOAT_ARB as raw::c_int);
644             } else {
645                 return Err(());
646             }
647         } else {
648             out.push(gl::wgl_extra::TYPE_RGBA_ARB as raw::c_int);
649         }
650 
651         if let Some(hardware_accelerated) = reqs.hardware_accelerated {
652             out.push(gl::wgl_extra::ACCELERATION_ARB as raw::c_int);
653             out.push(if hardware_accelerated {
654                 gl::wgl_extra::FULL_ACCELERATION_ARB as raw::c_int
655             } else {
656                 gl::wgl_extra::NO_ACCELERATION_ARB as raw::c_int
657             });
658         }
659 
660         if let Some(color) = reqs.color_bits {
661             out.push(gl::wgl_extra::COLOR_BITS_ARB as raw::c_int);
662             out.push(color as raw::c_int);
663         }
664 
665         if let Some(alpha) = reqs.alpha_bits {
666             out.push(gl::wgl_extra::ALPHA_BITS_ARB as raw::c_int);
667             out.push(alpha as raw::c_int);
668         }
669 
670         if let Some(depth) = reqs.depth_bits {
671             out.push(gl::wgl_extra::DEPTH_BITS_ARB as raw::c_int);
672             out.push(depth as raw::c_int);
673         }
674 
675         if let Some(stencil) = reqs.stencil_bits {
676             out.push(gl::wgl_extra::STENCIL_BITS_ARB as raw::c_int);
677             out.push(stencil as raw::c_int);
678         }
679 
680         // Prefer double buffering if unspecified (probably shouldn't once you
681         // can choose)
682         let double_buffer = reqs.double_buffer.unwrap_or(true);
683         out.push(gl::wgl_extra::DOUBLE_BUFFER_ARB as raw::c_int);
684         out.push(if double_buffer { 1 } else { 0 });
685 
686         if let Some(multisampling) = reqs.multisampling {
687             if extensions
688                 .split(' ')
689                 .find(|&i| i == "WGL_ARB_multisample")
690                 .is_some()
691             {
692                 out.push(gl::wgl_extra::SAMPLE_BUFFERS_ARB as raw::c_int);
693                 out.push(if multisampling == 0 { 0 } else { 1 });
694                 out.push(gl::wgl_extra::SAMPLES_ARB as raw::c_int);
695                 out.push(multisampling as raw::c_int);
696             } else {
697                 return Err(());
698             }
699         }
700 
701         out.push(gl::wgl_extra::STEREO_ARB as raw::c_int);
702         out.push(if reqs.stereoscopy { 1 } else { 0 });
703 
704         if reqs.srgb {
705             if extensions
706                 .split(' ')
707                 .find(|&i| i == "WGL_ARB_framebuffer_sRGB")
708                 .is_some()
709             {
710                 out.push(
711                     gl::wgl_extra::FRAMEBUFFER_SRGB_CAPABLE_ARB as raw::c_int,
712                 );
713                 out.push(1);
714             } else if extensions
715                 .split(' ')
716                 .find(|&i| i == "WGL_EXT_framebuffer_sRGB")
717                 .is_some()
718             {
719                 out.push(
720                     gl::wgl_extra::FRAMEBUFFER_SRGB_CAPABLE_EXT as raw::c_int,
721                 );
722                 out.push(1);
723             } else {
724                 return Err(());
725             }
726         }
727 
728         match reqs.release_behavior {
729             ReleaseBehavior::Flush => (),
730             ReleaseBehavior::None => {
731                 if extensions
732                     .split(' ')
733                     .find(|&i| i == "WGL_ARB_context_flush_control")
734                     .is_some()
735                 {
736                     out.push(
737                         gl::wgl_extra::CONTEXT_RELEASE_BEHAVIOR_ARB
738                             as raw::c_int,
739                     );
740                     out.push(
741                         gl::wgl_extra::CONTEXT_RELEASE_BEHAVIOR_NONE_ARB
742                             as raw::c_int,
743                     );
744                 }
745             }
746         }
747 
748         out.push(0);
749         out
750     };
751 
752     let mut format_id = std::mem::uninitialized();
753     let mut num_formats = std::mem::uninitialized();
754     if extra.ChoosePixelFormatARB(
755         hdc as *const _,
756         descriptor.as_ptr(),
757         std::ptr::null(),
758         1,
759         &mut format_id,
760         &mut num_formats,
761     ) == 0
762     {
763         return Err(());
764     }
765 
766     if num_formats == 0 {
767         return Err(());
768     }
769 
770     Ok(format_id)
771 }
772 
choose_arb_pixel_format( extra: &gl::wgl_extra::Wgl, extensions: &str, hdc: HDC, format_id: raw::c_int, ) -> Result<PixelFormat, ()>773 unsafe fn choose_arb_pixel_format(
774     extra: &gl::wgl_extra::Wgl,
775     extensions: &str,
776     hdc: HDC,
777     format_id: raw::c_int,
778 ) -> Result<PixelFormat, ()> {
779     let get_info = |attrib: u32| {
780         let mut value = std::mem::uninitialized();
781         extra.GetPixelFormatAttribivARB(
782             hdc as *const _,
783             format_id as raw::c_int,
784             0,
785             1,
786             [attrib as raw::c_int].as_ptr(),
787             &mut value,
788         );
789         value as u32
790     };
791 
792     let pf_desc = PixelFormat {
793         hardware_accelerated: get_info(gl::wgl_extra::ACCELERATION_ARB)
794             != gl::wgl_extra::NO_ACCELERATION_ARB,
795         color_bits: get_info(gl::wgl_extra::RED_BITS_ARB) as u8
796             + get_info(gl::wgl_extra::GREEN_BITS_ARB) as u8
797             + get_info(gl::wgl_extra::BLUE_BITS_ARB) as u8,
798         alpha_bits: get_info(gl::wgl_extra::ALPHA_BITS_ARB) as u8,
799         depth_bits: get_info(gl::wgl_extra::DEPTH_BITS_ARB) as u8,
800         stencil_bits: get_info(gl::wgl_extra::STENCIL_BITS_ARB) as u8,
801         stereoscopy: get_info(gl::wgl_extra::STEREO_ARB) != 0,
802         double_buffer: get_info(gl::wgl_extra::DOUBLE_BUFFER_ARB) != 0,
803         multisampling: {
804             if extensions
805                 .split(' ')
806                 .find(|&i| i == "WGL_ARB_multisample")
807                 .is_some()
808             {
809                 match get_info(gl::wgl_extra::SAMPLES_ARB) {
810                     0 => None,
811                     a => Some(a as u16),
812                 }
813             } else {
814                 None
815             }
816         },
817         srgb: if extensions
818             .split(' ')
819             .find(|&i| i == "WGL_ARB_framebuffer_sRGB")
820             .is_some()
821         {
822             get_info(gl::wgl_extra::FRAMEBUFFER_SRGB_CAPABLE_ARB) != 0
823         } else if extensions
824             .split(' ')
825             .find(|&i| i == "WGL_EXT_framebuffer_sRGB")
826             .is_some()
827         {
828             get_info(gl::wgl_extra::FRAMEBUFFER_SRGB_CAPABLE_EXT) != 0
829         } else {
830             false
831         },
832     };
833 
834     Ok(pf_desc)
835 }
836 
837 /// Calls `SetPixelFormat` on a window.
set_pixel_format( hdc: HDC, id: raw::c_int, ) -> Result<(), CreationError>838 unsafe fn set_pixel_format(
839     hdc: HDC,
840     id: raw::c_int,
841 ) -> Result<(), CreationError> {
842     let mut output: PIXELFORMATDESCRIPTOR = std::mem::zeroed();
843 
844     if DescribePixelFormat(
845         hdc,
846         id,
847         std::mem::size_of::<PIXELFORMATDESCRIPTOR>() as UINT,
848         &mut output,
849     ) == 0
850     {
851         return Err(CreationError::OsError(format!(
852             "DescribePixelFormat function failed: {}",
853             std::io::Error::last_os_error()
854         )));
855     }
856 
857     if SetPixelFormat(hdc, id, &output) == 0 {
858         return Err(CreationError::OsError(format!(
859             "SetPixelFormat function failed: {}",
860             std::io::Error::last_os_error()
861         )));
862     }
863 
864     Ok(())
865 }
866 
867 /// Loads the `opengl32.dll` library.
load_opengl32_dll() -> Result<HMODULE, CreationError>868 unsafe fn load_opengl32_dll() -> Result<HMODULE, CreationError> {
869     let name = OsStr::new("opengl32.dll")
870         .encode_wide()
871         .chain(Some(0).into_iter())
872         .collect::<Vec<_>>();
873 
874     let lib = LoadLibraryW(name.as_ptr());
875 
876     if lib.is_null() {
877         return Err(CreationError::OsError(format!(
878             "LoadLibrary function failed: {}",
879             std::io::Error::last_os_error()
880         )));
881     }
882 
883     Ok(lib)
884 }
885 
886 /// Loads the WGL functions that are not guaranteed to be supported.
887 ///
888 /// The `window` must be passed because the driver can vary depending on the
889 /// window's characteristics.
load_extra_functions( win: HWND, ) -> Result<gl::wgl_extra::Wgl, CreationError>890 unsafe fn load_extra_functions(
891     win: HWND,
892 ) -> Result<gl::wgl_extra::Wgl, CreationError> {
893     let (ex_style, style) = (
894         WS_EX_APPWINDOW,
895         WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
896     );
897 
898     // creating a dummy invisible window
899     let dummy_win = {
900         // getting the rect of the real window
901         let rect = {
902             let mut placement: WINDOWPLACEMENT = std::mem::zeroed();
903             placement.length = std::mem::size_of::<WINDOWPLACEMENT>() as UINT;
904             if GetWindowPlacement(win, &mut placement) == 0 {
905                 panic!();
906             }
907             placement.rcNormalPosition
908         };
909 
910         // getting the class name of the real window
911         let mut class_name = [0u16; 128];
912         if GetClassNameW(win, class_name.as_mut_ptr(), 128) == 0 {
913             return Err(CreationError::OsError(format!(
914                 "GetClassNameW function failed: {}",
915                 std::io::Error::last_os_error()
916             )));
917         }
918 
919         // access to class information of the real window
920         let instance = GetModuleHandleW(std::ptr::null());
921         let mut class: WNDCLASSEXW = std::mem::zeroed();
922 
923         if GetClassInfoExW(instance, class_name.as_ptr(), &mut class) == 0 {
924             return Err(CreationError::OsError(format!(
925                 "GetClassInfoExW function failed: {}",
926                 std::io::Error::last_os_error()
927             )));
928         }
929 
930         // register a new class for the dummy window,
931         // similar to the class of the real window but with a different callback
932         let class_name = OsStr::new("WglDummy Class")
933             .encode_wide()
934             .chain(Some(0).into_iter())
935             .collect::<Vec<_>>();
936 
937         class.cbSize = std::mem::size_of::<WNDCLASSEXW>() as UINT;
938         class.lpszClassName = class_name.as_ptr();
939         class.lpfnWndProc = Some(DefWindowProcW);
940 
941         // this shouldn't fail if the registration of the real window class
942         // worked. multiple registrations of the window class trigger an
943         // error which we want to ignore silently (e.g for multi-window
944         // setups)
945         RegisterClassExW(&class);
946 
947         // this dummy window should match the real one enough to get the same
948         // OpenGL driver
949         let title = OsStr::new("dummy window")
950             .encode_wide()
951             .chain(Some(0).into_iter())
952             .collect::<Vec<_>>();
953         let win = CreateWindowExW(
954             ex_style,
955             class_name.as_ptr(),
956             title.as_ptr() as LPCWSTR,
957             style,
958             CW_USEDEFAULT,
959             CW_USEDEFAULT,
960             rect.right - rect.left,
961             rect.bottom - rect.top,
962             std::ptr::null_mut(),
963             std::ptr::null_mut(),
964             GetModuleHandleW(std::ptr::null()),
965             std::ptr::null_mut(),
966         );
967 
968         if win.is_null() {
969             return Err(CreationError::OsError(format!(
970                 "CreateWindowEx function failed: {}",
971                 std::io::Error::last_os_error()
972             )));
973         }
974 
975         let hdc = GetDC(win);
976         if hdc.is_null() {
977             let err = Err(CreationError::OsError(format!(
978                 "GetDC function failed: {}",
979                 std::io::Error::last_os_error()
980             )));
981             return err;
982         }
983 
984         WindowWrapper(win, hdc)
985     };
986 
987     // getting the pixel format that we will use and setting it
988     {
989         let id = choose_dummy_pixel_format(dummy_win.1)?;
990         set_pixel_format(dummy_win.1, id)?;
991     }
992 
993     // creating the dummy OpenGL context and making it current
994     let dummy_ctx = create_context(None, dummy_win.0, dummy_win.1)?;
995     let _current_context =
996         CurrentContextGuard::make_current(dummy_win.1, dummy_ctx.0)?;
997 
998     // loading the extra WGL functions
999     Ok(gl::wgl_extra::Wgl::load_with(|addr| {
1000         let addr = CString::new(addr.as_bytes()).unwrap();
1001         let addr = addr.as_ptr();
1002         gl::wgl::GetProcAddress(addr) as *const raw::c_void
1003     }))
1004 }
1005 
1006 /// This function chooses a pixel format that is likely to be provided by
1007 /// the main video driver of the system.
choose_dummy_pixel_format(hdc: HDC) -> Result<raw::c_int, CreationError>1008 fn choose_dummy_pixel_format(hdc: HDC) -> Result<raw::c_int, CreationError> {
1009     // building the descriptor to pass to ChoosePixelFormat
1010     let descriptor = PIXELFORMATDESCRIPTOR {
1011         nSize: std::mem::size_of::<PIXELFORMATDESCRIPTOR>() as u16,
1012         nVersion: 1,
1013         dwFlags: PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
1014         iPixelType: PFD_TYPE_RGBA,
1015         cColorBits: 24,
1016         cRedBits: 0,
1017         cRedShift: 0,
1018         cGreenBits: 0,
1019         cGreenShift: 0,
1020         cBlueBits: 0,
1021         cBlueShift: 0,
1022         cAlphaBits: 8,
1023         cAlphaShift: 0,
1024         cAccumBits: 0,
1025         cAccumRedBits: 0,
1026         cAccumGreenBits: 0,
1027         cAccumBlueBits: 0,
1028         cAccumAlphaBits: 0,
1029         cDepthBits: 24,
1030         cStencilBits: 8,
1031         cAuxBuffers: 0,
1032         iLayerType: PFD_MAIN_PLANE,
1033         bReserved: 0,
1034         dwLayerMask: 0,
1035         dwVisibleMask: 0,
1036         dwDamageMask: 0,
1037     };
1038 
1039     // now querying
1040     let pf_id = unsafe { ChoosePixelFormat(hdc, &descriptor) };
1041     if pf_id == 0 {
1042         return Err(CreationError::OsError(
1043             "No available pixel format".to_owned(),
1044         ));
1045     }
1046 
1047     Ok(pf_id)
1048 }
1049