diff options
| author | Pierre Krieger <pierre.krieger1708@gmail.com> | 2015-07-19 13:53:40 +0200 | 
|---|---|---|
| committer | Pierre Krieger <pierre.krieger1708@gmail.com> | 2015-07-19 13:53:40 +0200 | 
| commit | baf9b92d3f8a62c6a10d2ea1506e262597e70ac9 (patch) | |
| tree | 91f8a1c9c3b4a255e6b2aacc6668733edecb1204 /src/api/glx | |
| parent | 896640f2e0fd659e952e492c1b74841624db8d08 (diff) | |
| download | glutin-baf9b92d3f8a62c6a10d2ea1506e262597e70ac9.tar.gz glutin-baf9b92d3f8a62c6a10d2ea1506e262597e70ac9.zip  | |
Correct window creation on X11
Diffstat (limited to 'src/api/glx')
| -rw-r--r-- | src/api/glx/mod.rs | 311 | 
1 files changed, 222 insertions, 89 deletions
diff --git a/src/api/glx/mod.rs b/src/api/glx/mod.rs index d286813..3779597 100644 --- a/src/api/glx/mod.rs +++ b/src/api/glx/mod.rs @@ -23,6 +23,7 @@ pub struct Context {      display: *mut ffi::Display,      window: ffi::Window,      context: ffi::GLXContext, +    pixel_format: PixelFormat,  }  // TODO: remove me @@ -33,11 +34,157 @@ fn with_c_str<F, T>(s: &str, f: F) -> T where F: FnOnce(*const libc::c_char) ->  }  impl Context { -    pub fn new(glx: ffi::glx::Glx, builder: BuilderAttribs, display: *mut ffi::Display, window: ffi::Window, -               fb_config: ffi::glx::types::GLXFBConfig, mut visual_infos: ffi::glx::types::XVisualInfo) -               -> Result<Context, CreationError> +    pub fn new<'a>(glx: ffi::glx::Glx, xlib: &ffi::Xlib, builder: &'a BuilderAttribs<'a>, +                   display: *mut ffi::Display) +                   -> Result<ContextPrototype<'a>, CreationError>      { -        let share = if let Some(win) = builder.sharing { +        // finding the pixel format we want +        // TODO: enumerate them instead +        let fb_config = unsafe { +            let mut visual_attributes = vec![ +                ffi::glx::X_RENDERABLE as libc::c_int,  1, +                ffi::glx::DRAWABLE_TYPE as libc::c_int, ffi::glx::WINDOW_BIT as libc::c_int, +                ffi::glx::RENDER_TYPE as libc::c_int,   ffi::glx::RGBA_BIT as libc::c_int, +                ffi::glx::X_VISUAL_TYPE as libc::c_int, ffi::glx::TRUE_COLOR as libc::c_int, +                ffi::glx::RED_SIZE as libc::c_int,      8, +                ffi::glx::GREEN_SIZE as libc::c_int,    8, +                ffi::glx::BLUE_SIZE as libc::c_int,     8, +                ffi::glx::ALPHA_SIZE as libc::c_int,    8, +                ffi::glx::DEPTH_SIZE as libc::c_int,    24, +                ffi::glx::STENCIL_SIZE as libc::c_int,  8, +                ffi::glx::DOUBLEBUFFER as libc::c_int,  1, +            ]; + +            if let Some(val) = builder.multisampling { +                visual_attributes.push(ffi::glx::SAMPLE_BUFFERS as libc::c_int); +                visual_attributes.push(1); +                visual_attributes.push(ffi::glx::SAMPLES as libc::c_int); +                visual_attributes.push(val as libc::c_int); +            } + +            if let Some(val) = builder.srgb { +                visual_attributes.push(ffi::glx_extra::FRAMEBUFFER_SRGB_CAPABLE_ARB as libc::c_int); +                visual_attributes.push(if val {1} else {0}); +            } + +            visual_attributes.push(0); + +            let mut num_fb: libc::c_int = mem::uninitialized(); + +            let fb = glx.ChooseFBConfig(display as *mut _, (xlib.XDefaultScreen)(display), +                visual_attributes.as_ptr(), &mut num_fb); +            if fb.is_null() { +                return Err(CreationError::OsError(format!("glxChooseFBConfig failed"))); +            } + +            let preferred_fb = if builder.transparent { +                let mut best_fbi_for_transparent = 0isize; + +                for i in 0isize..num_fb as isize { +                    let vi = glx.GetVisualFromFBConfig(display as *mut _, *fb.offset(i)); +                    if (*vi).depth == 32 { +                        best_fbi_for_transparent = i; +                        break; +                    } +                } + +                *fb.offset(best_fbi_for_transparent) +            } else { +                *fb // TODO: choose more wisely +            }; + +            (xlib.XFree)(fb as *mut _); +            preferred_fb +        }; + +        // getting the visual infos +        let visual_infos: ffi::glx::types::XVisualInfo = unsafe { +            let vi = glx.GetVisualFromFBConfig(display as *mut _, fb_config); +            if vi.is_null() { +                return Err(CreationError::OsError(format!("glxGetVisualFromFBConfig failed"))); +            } +            let vi_copy = ptr::read(vi as *const _); +            (xlib.XFree)(vi as *mut _); +            vi_copy +        }; + +        Ok(ContextPrototype { +            glx: glx, +            builder: builder, +            display: display, +            fb_config: fb_config, +            visual_infos: unsafe { mem::transmute(visual_infos) }, +        }) +    } +} + +impl GlContext for Context { +    unsafe fn make_current(&self) -> Result<(), ContextError> { +        // TODO: glutin needs some internal changes for proper error recovery +        let res = self.glx.MakeCurrent(self.display as *mut _, self.window, self.context); +        if res == 0 { +            panic!("glx::MakeCurrent failed"); +        } +        Ok(()) +    } + +    fn is_current(&self) -> bool { +        unsafe { self.glx.GetCurrentContext() == self.context } +    } + +    fn get_proc_address(&self, addr: &str) -> *const libc::c_void { +        let addr = CString::new(addr.as_bytes()).unwrap(); +        let addr = addr.as_ptr(); +        unsafe { +            self.glx.GetProcAddress(addr as *const _) as *const _ +        } +    } + +    fn swap_buffers(&self) -> Result<(), ContextError> { +        // TODO: glutin needs some internal changes for proper error recovery +        unsafe { self.glx.SwapBuffers(self.display as *mut _, self.window); } +        Ok(()) +    } + +    fn get_api(&self) -> ::Api { +        ::Api::OpenGl +    } + +    fn get_pixel_format(&self) -> PixelFormat { +        self.pixel_format.clone() +    } +} + +unsafe impl Send for Context {} +unsafe impl Sync for Context {} + +impl Drop for Context { +    fn drop(&mut self) { +        unsafe { +            if self.is_current() { +                self.glx.MakeCurrent(self.display as *mut _, 0, ptr::null_mut()); +            } + +            self.glx.DestroyContext(self.display as *mut _, self.context); +        } +    } +} + +pub struct ContextPrototype<'a> { +    glx: ffi::glx::Glx, +    builder: &'a BuilderAttribs<'a>, +    display: *mut ffi::Display, +    fb_config: ffi::glx::types::GLXFBConfig, +    visual_infos: ffi::XVisualInfo, +} + +impl<'a> ContextPrototype<'a> { +    pub fn get_visual_infos(&self) -> &ffi::XVisualInfo { +        &self.visual_infos +    } + +    pub fn finish(self, window: ffi::Window) -> Result<Context, CreationError> { +        let share = if let Some(win) = self.builder.sharing {              match win {                  &PlatformWindow::X(ref win) => match win.x.context {                      ::api::x11::Context::Glx(ref c) => c.context, @@ -51,7 +198,7 @@ impl Context {          // loading the list of extensions          let extensions = unsafe { -            let extensions = glx.QueryExtensionsString(display as *mut _, 0);     // FIXME: screen number +            let extensions = self.glx.QueryExtensionsString(self.display as *mut _, 0);     // FIXME: screen number              let extensions = CStr::from_ptr(extensions).to_bytes().to_vec();              String::from_utf8(extensions).unwrap()          }; @@ -59,62 +206,66 @@ impl Context {          // loading the extra GLX functions          let extra_functions = ffi::glx_extra::Glx::load_with(|addr| {              with_c_str(addr, |s| { -                unsafe { glx.GetProcAddress(s as *const u8) as *const _ } +                unsafe { self.glx.GetProcAddress(s as *const u8) as *const _ }              })          });          // creating GL context -        let context = match builder.gl_version { +        let context = match self.builder.gl_version {              GlRequest::Latest => { -                if let Ok(ctxt) = create_context(&glx, &extra_functions, &extensions, (3, 2), -                                                 builder.gl_profile, builder.gl_debug, -                                                 builder.gl_robustness, share, -                                                 display, fb_config, &mut visual_infos) +                if let Ok(ctxt) = create_context(&self.glx, &extra_functions, &extensions, (3, 2), +                                                 self.builder.gl_profile, self.builder.gl_debug, +                                                 self.builder.gl_robustness, share, +                                                 self.display, self.fb_config, &self.visual_infos)                  {                      ctxt -                } else if let Ok(ctxt) = create_context(&glx, &extra_functions, &extensions, (3, 1), -                                                        builder.gl_profile, builder.gl_debug, -                                                        builder.gl_robustness, share, display, -                                                        fb_config, &mut visual_infos) +                } else if let Ok(ctxt) = create_context(&self.glx, &extra_functions, &extensions, +                                                        (3, 1), self.builder.gl_profile, +                                                        self.builder.gl_debug, +                                                        self.builder.gl_robustness, share, self.display, +                                                        self.fb_config, &self.visual_infos)                  {                      ctxt                  } else { -                    try!(create_context(&glx, &extra_functions, &extensions, (1, 0), -                                        builder.gl_profile, builder.gl_debug, builder.gl_robustness, -                                        share, display, fb_config, &mut visual_infos)) +                    try!(create_context(&self.glx, &extra_functions, &extensions, (1, 0), +                                        self.builder.gl_profile, self.builder.gl_debug, +                                        self.builder.gl_robustness, +                                        share, self.display, self.fb_config, &self.visual_infos))                  }              },              GlRequest::Specific(Api::OpenGl, (major, minor)) => { -                try!(create_context(&glx, &extra_functions, &extensions, (major, minor), -                                    builder.gl_profile, builder.gl_debug, builder.gl_robustness, -                                    share, display, fb_config, &mut visual_infos)) +                try!(create_context(&self.glx, &extra_functions, &extensions, (major, minor), +                                    self.builder.gl_profile, self.builder.gl_debug, +                                    self.builder.gl_robustness, share, self.display, self.fb_config, +                                    &self.visual_infos))              },              GlRequest::Specific(_, _) => panic!("Only OpenGL is supported"),              GlRequest::GlThenGles { opengl_version: (major, minor), .. } => { -                try!(create_context(&glx, &extra_functions, &extensions, (major, minor), -                                    builder.gl_profile, builder.gl_debug, builder.gl_robustness, -                                    share, display, fb_config, &mut visual_infos)) +                try!(create_context(&self.glx, &extra_functions, &extensions, (major, minor), +                                    self.builder.gl_profile, self.builder.gl_debug, +                                    self.builder.gl_robustness, share, self.display, self.fb_config, +                                    &self.visual_infos))              },          };          // vsync -        if builder.vsync { -            unsafe { glx.MakeCurrent(display as *mut _, window, context) }; +        if self.builder.vsync { +            unsafe { self.glx.MakeCurrent(self.display as *mut _, window, context) };              if extra_functions.SwapIntervalEXT.is_loaded() {                  // this should be the most common extension                  unsafe { -                    extra_functions.SwapIntervalEXT(display as *mut _, window, 1); +                    extra_functions.SwapIntervalEXT(self.display as *mut _, window, 1);                  }                  // checking that it worked -                if builder.strict { +                if self.builder.strict {                      let mut swap = unsafe { mem::uninitialized() };                      unsafe { -                        glx.QueryDrawable(display as *mut _, window, -                                                ffi::glx_extra::SWAP_INTERVAL_EXT as i32, -                                                &mut swap); +                        self.glx.QueryDrawable(self.display as *mut _, window, +                                               ffi::glx_extra::SWAP_INTERVAL_EXT as i32, +                                               &mut swap);                      }                      if swap != 1 { @@ -134,79 +285,30 @@ impl Context {                      extra_functions.SwapIntervalSGI(1);                  } -            } else if builder.strict { +            } else if self.builder.strict {                  return Err(CreationError::OsError(format!("Couldn't find any available vsync extension")));              } -            unsafe { glx.MakeCurrent(display as *mut _, 0, ptr::null()) }; +            unsafe { self.glx.MakeCurrent(self.display as *mut _, 0, ptr::null()) };          } +        let pixel_format = unsafe { get_pixel_format(&self.glx, self.display, self.fb_config) }; +          Ok(Context { -            glx: glx, -            display: display, +            glx: self.glx, +            display: self.display,              window: window,              context: context, +            pixel_format: pixel_format,          })      }  } -impl GlContext for Context { -    unsafe fn make_current(&self) -> Result<(), ContextError> { -        // TODO: glutin needs some internal changes for proper error recovery -        let res = self.glx.MakeCurrent(self.display as *mut _, self.window, self.context); -        if res == 0 { -            panic!("glx::MakeCurrent failed"); -        } -        Ok(()) -    } - -    fn is_current(&self) -> bool { -        unsafe { self.glx.GetCurrentContext() == self.context } -    } - -    fn get_proc_address(&self, addr: &str) -> *const libc::c_void { -        let addr = CString::new(addr.as_bytes()).unwrap(); -        let addr = addr.as_ptr(); -        unsafe { -            self.glx.GetProcAddress(addr as *const _) as *const _ -        } -    } - -    fn swap_buffers(&self) -> Result<(), ContextError> { -        // TODO: glutin needs some internal changes for proper error recovery -        unsafe { self.glx.SwapBuffers(self.display as *mut _, self.window); } -        Ok(()) -    } - -    fn get_api(&self) -> ::Api { -        ::Api::OpenGl -    } - -    fn get_pixel_format(&self) -> PixelFormat { -        unimplemented!(); -    } -} - -unsafe impl Send for Context {} -unsafe impl Sync for Context {} - -impl Drop for Context { -    fn drop(&mut self) { -        unsafe { -            if self.is_current() { -                self.glx.MakeCurrent(self.display as *mut _, 0, ptr::null_mut()); -            } - -            self.glx.DestroyContext(self.display as *mut _, self.context); -        } -    } -} -  fn create_context(glx: &ffi::glx::Glx, extra_functions: &ffi::glx_extra::Glx, extensions: &str,                    version: (u8, u8), profile: Option<GlProfile>, debug: bool,                    robustness: Robustness, share: ffi::GLXContext, display: *mut ffi::Display,                    fb_config: ffi::glx::types::GLXFBConfig, -                  visual_infos: &mut ffi::glx::types::XVisualInfo) +                  visual_infos: &ffi::XVisualInfo)                    -> Result<ffi::GLXContext, CreationError>  {      unsafe { @@ -274,7 +376,8 @@ fn create_context(glx: &ffi::glx::Glx, extra_functions: &ffi::glx_extra::Glx, ex                                                      attributes.as_ptr())          } else { -            glx.CreateContext(display as *mut _, visual_infos, share, 1) +            let visual_infos: *const ffi::XVisualInfo = visual_infos; +            glx.CreateContext(display as *mut _, visual_infos as *mut _, share, 1)          };          if context.is_null() { @@ -284,3 +387,33 @@ fn create_context(glx: &ffi::glx::Glx, extra_functions: &ffi::glx_extra::Glx, ex          Ok(context)      }  } + +unsafe fn get_pixel_format(glx: &ffi::glx::Glx, display: *mut ffi::Display, +                           fb_config: ffi::glx::types::GLXFBConfig) -> PixelFormat +{ +    let get_attrib = |attrib: libc::c_int| -> i32 { +        let mut value = 0; +        glx.GetFBConfigAttrib(display as *mut _, fb_config, attrib, &mut value); +        // TODO: check return value +        value +    }; + +    // TODO: make sure everything is supported +    PixelFormat { +        hardware_accelerated: true, +        color_bits: get_attrib(ffi::glx::RED_SIZE as libc::c_int) as u8 + +                    get_attrib(ffi::glx::GREEN_SIZE as libc::c_int) as u8 + +                    get_attrib(ffi::glx::BLUE_SIZE as libc::c_int) as u8, +        alpha_bits: get_attrib(ffi::glx::ALPHA_SIZE as libc::c_int) as u8, +        depth_bits: get_attrib(ffi::glx::DEPTH_SIZE as libc::c_int) as u8, +        stencil_bits: get_attrib(ffi::glx::STENCIL_SIZE as libc::c_int) as u8, +        stereoscopy: get_attrib(ffi::glx::STEREO as libc::c_int) != 0, +        double_buffer: get_attrib(ffi::glx::DOUBLEBUFFER as libc::c_int) != 0, +        multisampling: if get_attrib(ffi::glx::SAMPLE_BUFFERS as libc::c_int) != 0 { +            Some(get_attrib(ffi::glx::SAMPLES as libc::c_int) as u16) +        } else { +            None +        }, +        srgb: get_attrib(ffi::glx_extra::FRAMEBUFFER_SRGB_CAPABLE_ARB as libc::c_int) != 0, +    } +}  | 
