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