@@ -13,6 +13,7 @@ mod errors;
1313#[ derive( Debug ) ]
1414pub enum CreationFailedError {
1515 InvalidFBConfig ,
16+ NoVisual ,
1617 GetProcAddressFailed ,
1718 MakeCurrentFailed ,
1819 ContextCreationFailed ,
@@ -55,9 +56,31 @@ pub struct GlContext {
5556 context : glx:: GLXContext ,
5657}
5758
59+ /// The frame buffer configuration along with the general OpenGL configuration to somewhat minimize
60+ /// misuse.
61+ pub struct FbConfig {
62+ gl_config : GlConfig ,
63+ fb_config : * mut glx:: __GLXFBConfigRec ,
64+ }
65+
66+ /// The configuration a window should be created with after calling
67+ /// [GlContext::get_fb_config_and_visual].
68+ pub struct WindowConfig {
69+ pub depth : u8 ,
70+ pub visual : u32 ,
71+ }
72+
5873impl GlContext {
74+ /// Creating an OpenGL context under X11 works slightly different. Different OpenGL
75+ /// configurations require different framebuffer configurations, and to be able to use that
76+ /// context with a window the window needs to be created with a matching visual. This means that
77+ /// you need to decide on the framebuffer config before creating the window, ask the X11 server
78+ /// for a matching visual for that framebuffer config, crate the window with that visual, and
79+ /// only then create the OpenGL context.
80+ ///
81+ /// Use [Self::get_fb_config_and_visual] to create both of these things.
5982 pub unsafe fn create (
60- parent : & impl HasRawWindowHandle , config : GlConfig ,
83+ parent : & impl HasRawWindowHandle , config : FbConfig ,
6184 ) -> Result < GlContext , GlError > {
6285 let handle = if let RawWindowHandle :: Xlib ( handle) = parent. raw_window_handle ( ) {
6386 handle
@@ -72,38 +95,6 @@ impl GlContext {
7295 let display = handle. display as * mut xlib:: _XDisplay ;
7396
7497 errors:: XErrorHandler :: handle ( display, |error_handler| {
75- let screen = unsafe { xlib:: XDefaultScreen ( display) } ;
76-
77- #[ rustfmt:: skip]
78- let fb_attribs = [
79- glx:: GLX_X_RENDERABLE , 1 ,
80- glx:: GLX_X_VISUAL_TYPE , glx:: GLX_TRUE_COLOR ,
81- glx:: GLX_DRAWABLE_TYPE , glx:: GLX_WINDOW_BIT ,
82- glx:: GLX_RENDER_TYPE , glx:: GLX_RGBA_BIT ,
83- glx:: GLX_RED_SIZE , config. red_bits as i32 ,
84- glx:: GLX_GREEN_SIZE , config. green_bits as i32 ,
85- glx:: GLX_BLUE_SIZE , config. blue_bits as i32 ,
86- glx:: GLX_ALPHA_SIZE , config. alpha_bits as i32 ,
87- glx:: GLX_DEPTH_SIZE , config. depth_bits as i32 ,
88- glx:: GLX_STENCIL_SIZE , config. stencil_bits as i32 ,
89- glx:: GLX_DOUBLEBUFFER , config. double_buffer as i32 ,
90- glx:: GLX_SAMPLE_BUFFERS , config. samples . is_some ( ) as i32 ,
91- glx:: GLX_SAMPLES , config. samples . unwrap_or ( 0 ) as i32 ,
92- GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB , config. srgb as i32 ,
93- 0 ,
94- ] ;
95-
96- let mut n_configs = 0 ;
97- let fb_config = unsafe {
98- glx:: glXChooseFBConfig ( display, screen, fb_attribs. as_ptr ( ) , & mut n_configs)
99- } ;
100-
101- error_handler. check ( ) ?;
102-
103- if n_configs <= 0 {
104- return Err ( GlError :: CreationFailed ( CreationFailedError :: InvalidFBConfig ) ) ;
105- }
106-
10798 #[ allow( non_snake_case) ]
10899 let glXCreateContextAttribsARB: GlXCreateContextAttribsARB = unsafe {
109100 let addr = get_proc_address ( "glXCreateContextAttribsARB" ) ;
@@ -126,23 +117,23 @@ impl GlContext {
126117
127118 error_handler. check ( ) ?;
128119
129- let profile_mask = match config. profile {
120+ let profile_mask = match config. gl_config . profile {
130121 Profile :: Core => glx:: arb:: GLX_CONTEXT_CORE_PROFILE_BIT_ARB ,
131122 Profile :: Compatibility => glx:: arb:: GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB ,
132123 } ;
133124
134125 #[ rustfmt:: skip]
135126 let ctx_attribs = [
136- glx:: arb:: GLX_CONTEXT_MAJOR_VERSION_ARB , config. version . 0 as i32 ,
137- glx:: arb:: GLX_CONTEXT_MINOR_VERSION_ARB , config. version . 1 as i32 ,
127+ glx:: arb:: GLX_CONTEXT_MAJOR_VERSION_ARB , config. gl_config . version . 0 as i32 ,
128+ glx:: arb:: GLX_CONTEXT_MINOR_VERSION_ARB , config. gl_config . version . 1 as i32 ,
138129 glx:: arb:: GLX_CONTEXT_PROFILE_MASK_ARB , profile_mask,
139130 0 ,
140131 ] ;
141132
142133 let context = unsafe {
143134 glXCreateContextAttribsARB (
144135 display,
145- * fb_config,
136+ config . fb_config ,
146137 std:: ptr:: null_mut ( ) ,
147138 1 ,
148139 ctx_attribs. as_ptr ( ) ,
@@ -162,7 +153,7 @@ impl GlContext {
162153 return Err ( GlError :: CreationFailed ( CreationFailedError :: MakeCurrentFailed ) ) ;
163154 }
164155
165- glXSwapIntervalEXT ( display, handle. window , config. vsync as i32 ) ;
156+ glXSwapIntervalEXT ( display, handle. window , config. gl_config . vsync as i32 ) ;
166157 error_handler. check ( ) ?;
167158
168159 if glx:: glXMakeCurrent ( display, 0 , std:: ptr:: null_mut ( ) ) == 0 {
@@ -175,6 +166,59 @@ impl GlContext {
175166 } )
176167 }
177168
169+ /// Find a matching framebuffer config and window visual for the given OpenGL configuration.
170+ /// This needs to be passed to [Self::create] along with a handle to a window that was created
171+ /// using the visual also returned from this function.
172+ pub unsafe fn get_fb_config_and_visual (
173+ display : * mut xlib:: _XDisplay , config : GlConfig ,
174+ ) -> Result < ( FbConfig , WindowConfig ) , GlError > {
175+ errors:: XErrorHandler :: handle ( display, |error_handler| {
176+ let screen = unsafe { xlib:: XDefaultScreen ( display) } ;
177+
178+ #[ rustfmt:: skip]
179+ let fb_attribs = [
180+ glx:: GLX_X_RENDERABLE , 1 ,
181+ glx:: GLX_X_VISUAL_TYPE , glx:: GLX_TRUE_COLOR ,
182+ glx:: GLX_DRAWABLE_TYPE , glx:: GLX_WINDOW_BIT ,
183+ glx:: GLX_RENDER_TYPE , glx:: GLX_RGBA_BIT ,
184+ glx:: GLX_RED_SIZE , config. red_bits as i32 ,
185+ glx:: GLX_GREEN_SIZE , config. green_bits as i32 ,
186+ glx:: GLX_BLUE_SIZE , config. blue_bits as i32 ,
187+ glx:: GLX_ALPHA_SIZE , config. alpha_bits as i32 ,
188+ glx:: GLX_DEPTH_SIZE , config. depth_bits as i32 ,
189+ glx:: GLX_STENCIL_SIZE , config. stencil_bits as i32 ,
190+ glx:: GLX_DOUBLEBUFFER , config. double_buffer as i32 ,
191+ glx:: GLX_SAMPLE_BUFFERS , config. samples . is_some ( ) as i32 ,
192+ glx:: GLX_SAMPLES , config. samples . unwrap_or ( 0 ) as i32 ,
193+ GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB , config. srgb as i32 ,
194+ 0 ,
195+ ] ;
196+
197+ let mut n_configs = 0 ;
198+ let fb_config = unsafe {
199+ glx:: glXChooseFBConfig ( display, screen, fb_attribs. as_ptr ( ) , & mut n_configs)
200+ } ;
201+
202+ error_handler. check ( ) ?;
203+ if n_configs <= 0 || fb_config. is_null ( ) {
204+ return Err ( GlError :: CreationFailed ( CreationFailedError :: InvalidFBConfig ) ) ;
205+ }
206+
207+ // Now that we have a matching framebuffer config, we need to know which visual matches
208+ // thsi config so the window is compatible with the OpenGL context we're about to create
209+ let fb_config = * fb_config;
210+ let visual = glx:: glXGetVisualFromFBConfig ( display, fb_config) ;
211+ if visual. is_null ( ) {
212+ return Err ( GlError :: CreationFailed ( CreationFailedError :: NoVisual ) ) ;
213+ }
214+
215+ Ok ( (
216+ FbConfig { fb_config, gl_config : config } ,
217+ WindowConfig { depth : ( * visual) . depth as u8 , visual : ( * visual) . visualid as u32 } ,
218+ ) )
219+ } )
220+ }
221+
178222 pub unsafe fn make_current ( & self ) {
179223 errors:: XErrorHandler :: handle ( self . display , |error_handler| {
180224 let res = glx:: glXMakeCurrent ( self . display , self . window , self . context ) ;
0 commit comments