1//! Everything related to `GLXContext`.
2
3use std::fmt;
4use std::marker::PhantomData;
5use std::ops::Deref;
6use std::os::raw::c_int;
7
8use glutin_glx_sys::glx::types::GLXContext;
9use glutin_glx_sys::{glx, glx_extra};
10
11use crate::config::GetGlConfig;
12use crate::context::{
13 self, AsRawContext, ContextApi, ContextAttributes, GlProfile, RawContext, ReleaseBehavior,
14 Robustness, Version,
15};
16use crate::display::{DisplayFeatures, GetGlDisplay};
17use crate::error::{ErrorKind, Result};
18use crate::prelude::*;
19use crate::private::Sealed;
20use crate::surface::SurfaceTypeTrait;
21
22use super::config::Config;
23use super::display::Display;
24use super::surface::Surface;
25
26impl Display {
27 pub(crate) unsafe fn create_context(
28 &self,
29 config: &Config,
30 context_attributes: &ContextAttributes,
31 ) -> Result<NotCurrentContext> {
32 let shared_context = if let Some(shared_context) =
33 context_attributes.shared_context.as_ref()
34 {
35 match shared_context {
36 RawContext::Glx(shared_context) => *shared_context,
37 #[allow(unreachable_patterns)]
38 _ => return Err(ErrorKind::NotSupported("incompatible context was passed").into()),
39 }
40 } else {
41 std::ptr::null()
42 };
43
44 let context = if self.inner.client_extensions.contains("GLX_ARB_create_context")
45 && self.inner.glx_extra.is_some()
46 {
47 self.create_context_arb(config, context_attributes, shared_context)?
48 } else {
49 self.create_context_legacy(config, shared_context)?
50 };
51
52 // Failed to create the context.
53 if context.is_null() {
54 return Err(ErrorKind::BadContext.into());
55 }
56
57 let config = config.clone();
58 let is_gles = matches!(context_attributes.api, Some(ContextApi::Gles(_)));
59 let inner =
60 ContextInner { display: self.clone(), config, raw: GlxContext(context), is_gles };
61
62 Ok(NotCurrentContext::new(inner))
63 }
64
65 fn create_context_arb(
66 &self,
67 config: &Config,
68 context_attributes: &ContextAttributes,
69 shared_context: GLXContext,
70 ) -> Result<GLXContext> {
71 let extra = self.inner.glx_extra.as_ref().unwrap();
72 let mut attrs = Vec::<c_int>::with_capacity(16);
73
74 // Check whether the ES context creation is supported.
75 let supports_es = self.inner.features.contains(DisplayFeatures::CREATE_ES_CONTEXT);
76
77 let (profile, version) = match context_attributes.api {
78 api @ Some(ContextApi::OpenGl(_)) | api @ None => {
79 let version = api.and_then(|api| api.version());
80 let (profile, version) = context::pick_profile(context_attributes.profile, version);
81 let profile = match profile {
82 GlProfile::Core => glx_extra::CONTEXT_CORE_PROFILE_BIT_ARB,
83 GlProfile::Compatibility => glx_extra::CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
84 };
85
86 (Some(profile), Some(version))
87 },
88 Some(ContextApi::Gles(version)) if supports_es => (
89 Some(glx_extra::CONTEXT_ES2_PROFILE_BIT_EXT),
90 Some(version.unwrap_or(Version::new(2, 0))),
91 ),
92 _ => {
93 return Err(ErrorKind::NotSupported(
94 "extension to create ES context with glx is not present.",
95 )
96 .into())
97 },
98 };
99
100 // Set the profile.
101 if let Some(profile) = profile {
102 attrs.push(glx_extra::CONTEXT_PROFILE_MASK_ARB as c_int);
103 attrs.push(profile as c_int);
104 }
105
106 // Add version.
107 if let Some(version) = version {
108 attrs.push(glx_extra::CONTEXT_MAJOR_VERSION_ARB as c_int);
109 attrs.push(version.major as c_int);
110 attrs.push(glx_extra::CONTEXT_MINOR_VERSION_ARB as c_int);
111 attrs.push(version.minor as c_int);
112 }
113
114 if let Some(profile) = context_attributes.profile {
115 let profile = match profile {
116 GlProfile::Core => glx_extra::CONTEXT_CORE_PROFILE_BIT_ARB,
117 GlProfile::Compatibility => glx_extra::CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
118 };
119
120 attrs.push(glx_extra::CONTEXT_PROFILE_MASK_ARB as c_int);
121 attrs.push(profile as c_int);
122 }
123
124 let mut flags: c_int = 0;
125 let mut requested_no_error = false;
126 if self.inner.features.contains(DisplayFeatures::CONTEXT_ROBUSTNESS) {
127 match context_attributes.robustness {
128 Robustness::NotRobust => (),
129 Robustness::RobustNoResetNotification => {
130 attrs.push(glx_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB as c_int);
131 attrs.push(glx_extra::NO_RESET_NOTIFICATION_ARB as c_int);
132 flags |= glx_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB as c_int;
133 },
134 Robustness::RobustLoseContextOnReset => {
135 attrs.push(glx_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB as c_int);
136 attrs.push(glx_extra::LOSE_CONTEXT_ON_RESET_ARB as c_int);
137 flags |= glx_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB as c_int;
138 },
139 Robustness::NoError => {
140 if !self.inner.features.contains(DisplayFeatures::CONTEXT_NO_ERROR) {
141 return Err(ErrorKind::NotSupported(
142 "GLX_ARB_create_context_no_error not supported",
143 )
144 .into());
145 }
146
147 attrs.push(glx_extra::CONTEXT_OPENGL_NO_ERROR_ARB as c_int);
148 attrs.push(1);
149 requested_no_error = true;
150 },
151 }
152 } else if context_attributes.robustness != Robustness::NotRobust {
153 return Err(ErrorKind::NotSupported(
154 "GLX_ARB_create_context_robustness is not supported",
155 )
156 .into());
157 }
158
159 // Debug flag.
160 if context_attributes.debug && !requested_no_error {
161 flags |= glx_extra::CONTEXT_DEBUG_BIT_ARB as c_int;
162 }
163
164 if flags != 0 {
165 attrs.push(glx_extra::CONTEXT_FLAGS_ARB as c_int);
166 attrs.push(flags as c_int);
167 }
168
169 // Flush control.
170 if self.inner.features.contains(DisplayFeatures::CONTEXT_RELEASE_BEHAVIOR) {
171 match context_attributes.release_behavior {
172 // This is the default behavior in specification.
173 //
174 // XXX passing it explicitly causing issues with older mesa versions.
175 ReleaseBehavior::Flush => (),
176 ReleaseBehavior::None => {
177 attrs.push(glx_extra::CONTEXT_RELEASE_BEHAVIOR_ARB as c_int);
178 attrs.push(glx_extra::CONTEXT_RELEASE_BEHAVIOR_NONE_ARB as c_int);
179 },
180 }
181 } else if context_attributes.release_behavior != ReleaseBehavior::Flush {
182 return Err(ErrorKind::NotSupported(
183 "flush control behavior GLX_ARB_context_flush_control",
184 )
185 .into());
186 }
187
188 // Terminate list with zero.
189 attrs.push(0);
190
191 super::last_glx_error(|| unsafe {
192 extra.CreateContextAttribsARB(
193 self.inner.raw.cast(),
194 *config.inner.raw,
195 shared_context,
196 // Direct context
197 1,
198 attrs.as_ptr(),
199 )
200 })
201 }
202
203 fn create_context_legacy(
204 &self,
205 config: &Config,
206 shared_context: GLXContext,
207 ) -> Result<GLXContext> {
208 let render_type =
209 if config.float_pixels() { glx_extra::RGBA_FLOAT_TYPE_ARB } else { glx::RGBA_TYPE };
210
211 super::last_glx_error(|| unsafe {
212 self.inner.glx.CreateNewContext(
213 self.inner.raw.cast(),
214 *config.inner.raw,
215 render_type as c_int,
216 shared_context,
217 // Direct context.
218 1,
219 )
220 })
221 }
222}
223
224/// A wrapper around `GLXContext` that is known to be not current.
225#[derive(Debug)]
226pub struct NotCurrentContext {
227 inner: ContextInner,
228}
229
230impl NotCurrentContext {
231 fn new(inner: ContextInner) -> Self {
232 Self { inner }
233 }
234}
235
236impl NotCurrentGlContext for NotCurrentContext {
237 type PossiblyCurrentContext = PossiblyCurrentContext;
238 type Surface<T: SurfaceTypeTrait> = Surface<T>;
239
240 fn treat_as_possibly_current(self) -> PossiblyCurrentContext {
241 PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData }
242 }
243
244 fn make_current<T: SurfaceTypeTrait>(
245 self,
246 surface: &Self::Surface<T>,
247 ) -> Result<Self::PossiblyCurrentContext> {
248 self.inner.make_current_draw_read(surface, surface)?;
249 Ok(PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData })
250 }
251
252 fn make_current_draw_read<T: SurfaceTypeTrait>(
253 self,
254 surface_draw: &Self::Surface<T>,
255 surface_read: &Self::Surface<T>,
256 ) -> Result<Self::PossiblyCurrentContext> {
257 self.inner.make_current_draw_read(surface_draw, surface_read)?;
258 Ok(PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData })
259 }
260}
261
262impl GlContext for NotCurrentContext {
263 fn context_api(&self) -> ContextApi {
264 self.inner.context_api()
265 }
266}
267
268impl GetGlConfig for NotCurrentContext {
269 type Target = Config;
270
271 fn config(&self) -> Self::Target {
272 self.inner.config.clone()
273 }
274}
275
276impl GetGlDisplay for NotCurrentContext {
277 type Target = Display;
278
279 fn display(&self) -> Self::Target {
280 self.inner.display.clone()
281 }
282}
283
284impl AsRawContext for NotCurrentContext {
285 fn raw_context(&self) -> RawContext {
286 RawContext::Glx(*self.inner.raw)
287 }
288}
289
290impl Sealed for NotCurrentContext {}
291
292/// A wrapper around `GLXContext` that could be current for the current thread.
293#[derive(Debug)]
294pub struct PossiblyCurrentContext {
295 inner: ContextInner,
296 // The context could be current only on the one thread.
297 _nosendsync: PhantomData<GLXContext>,
298}
299
300impl PossiblyCurrentGlContext for PossiblyCurrentContext {
301 type NotCurrentContext = NotCurrentContext;
302 type Surface<T: SurfaceTypeTrait> = Surface<T>;
303
304 fn make_not_current(self) -> Result<Self::NotCurrentContext> {
305 self.inner.make_not_current()?;
306 Ok(NotCurrentContext::new(self.inner))
307 }
308
309 fn is_current(&self) -> bool {
310 unsafe { self.inner.display.inner.glx.GetCurrentContext() == *self.inner.raw }
311 }
312
313 fn make_current<T: SurfaceTypeTrait>(&self, surface: &Self::Surface<T>) -> Result<()> {
314 self.inner.make_current_draw_read(surface, surface)
315 }
316
317 fn make_current_draw_read<T: SurfaceTypeTrait>(
318 &self,
319 surface_draw: &Self::Surface<T>,
320 surface_read: &Self::Surface<T>,
321 ) -> Result<()> {
322 self.inner.make_current_draw_read(surface_draw, surface_read)
323 }
324}
325
326impl GlContext for PossiblyCurrentContext {
327 fn context_api(&self) -> ContextApi {
328 self.inner.context_api()
329 }
330}
331
332impl GetGlConfig for PossiblyCurrentContext {
333 type Target = Config;
334
335 fn config(&self) -> Self::Target {
336 self.inner.config.clone()
337 }
338}
339
340impl GetGlDisplay for PossiblyCurrentContext {
341 type Target = Display;
342
343 fn display(&self) -> Self::Target {
344 self.inner.display.clone()
345 }
346}
347
348impl AsRawContext for PossiblyCurrentContext {
349 fn raw_context(&self) -> RawContext {
350 RawContext::Glx(*self.inner.raw)
351 }
352}
353
354impl Sealed for PossiblyCurrentContext {}
355
356struct ContextInner {
357 display: Display,
358 config: Config,
359 raw: GlxContext,
360 is_gles: bool,
361}
362
363impl ContextInner {
364 fn make_current_draw_read<T: SurfaceTypeTrait>(
365 &self,
366 surface_draw: &Surface<T>,
367 surface_read: &Surface<T>,
368 ) -> Result<()> {
369 super::last_glx_error(|| unsafe {
370 self.display.inner.glx.MakeContextCurrent(
371 self.display.inner.raw.cast(),
372 surface_draw.raw,
373 surface_read.raw,
374 *self.raw,
375 );
376 })
377 }
378
379 fn make_not_current(&self) -> Result<()> {
380 super::last_glx_error(|| unsafe {
381 self.display.inner.glx.MakeContextCurrent(
382 self.display.inner.raw.cast(),
383 0,
384 0,
385 std::ptr::null(),
386 );
387 })
388 }
389
390 fn context_api(&self) -> ContextApi {
391 if self.is_gles {
392 ContextApi::Gles(None)
393 } else {
394 ContextApi::OpenGl(None)
395 }
396 }
397}
398
399impl Drop for ContextInner {
400 fn drop(&mut self) {
401 let _ = super::last_glx_error(|| unsafe {
402 self.display.inner.glx.DestroyContext(self.display.inner.raw.cast(), *self.raw);
403 });
404 }
405}
406
407impl fmt::Debug for ContextInner {
408 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
409 f&mut DebugStruct<'_, '_>.debug_struct("Context")
410 .field("display", &self.display.inner.raw)
411 .field("config", &self.config.inner.raw)
412 .field(name:"raw", &self.raw)
413 .finish()
414 }
415}
416
417#[derive(Debug)]
418struct GlxContext(GLXContext);
419
420unsafe impl Send for GlxContext {}
421
422impl Deref for GlxContext {
423 type Target = GLXContext;
424
425 fn deref(&self) -> &Self::Target {
426 &self.0
427 }
428}
429