1use 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, Priority, RawContext,
14 ReleaseBehavior, 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, supports_surfaceless) =
45 if self.inner.client_extensions.contains("GLX_ARB_create_context")
46 && self.inner.glx_extra.is_some()
47 {
48 self.create_context_arb(config, context_attributes, shared_context)?
49 } else {
50 (self.create_context_legacy(config, shared_context)?, false)
51 };
52
53 if context.is_null() {
55 return Err(ErrorKind::BadContext.into());
56 }
57
58 let config = config.clone();
59 let is_gles = matches!(context_attributes.api, Some(ContextApi::Gles(_)));
60 let inner = ContextInner {
61 display: self.clone(),
62 config,
63 raw: GlxContext(context),
64 is_gles,
65 supports_surfaceless,
66 };
67
68 Ok(NotCurrentContext::new(inner))
69 }
70
71 fn create_context_arb(
72 &self,
73 config: &Config,
74 context_attributes: &ContextAttributes,
75 shared_context: GLXContext,
76 ) -> Result<(GLXContext, bool)> {
77 let extra = self.inner.glx_extra.as_ref().unwrap();
78 let mut attrs = Vec::<c_int>::with_capacity(16);
79
80 let supports_es = self.inner.features.contains(DisplayFeatures::CREATE_ES_CONTEXT);
82
83 let (profile, version, supports_surfaceless) = match context_attributes.api {
84 api @ Some(ContextApi::OpenGl(_)) | api @ None => {
85 let version = api.and_then(|api| api.version());
86 let (profile, version) = context::pick_profile(context_attributes.profile, version);
87 let profile = match profile {
88 GlProfile::Core => glx_extra::CONTEXT_CORE_PROFILE_BIT_ARB,
89 GlProfile::Compatibility => glx_extra::CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
90 };
91
92 let supports_surfaceless = version >= Version::new(3, 0);
95
96 (Some(profile), Some(version), supports_surfaceless)
97 },
98 Some(ContextApi::Gles(version)) if supports_es => (
99 Some(glx_extra::CONTEXT_ES2_PROFILE_BIT_EXT),
100 Some(version.unwrap_or(Version::new(2, 0))),
101 false,
102 ),
103 _ => {
104 return Err(ErrorKind::NotSupported(
105 "extension to create ES context with glx is not present.",
106 )
107 .into())
108 },
109 };
110
111 if let Some(profile) = profile {
113 attrs.push(glx_extra::CONTEXT_PROFILE_MASK_ARB as c_int);
114 attrs.push(profile as c_int);
115 }
116
117 if let Some(version) = version {
119 attrs.push(glx_extra::CONTEXT_MAJOR_VERSION_ARB as c_int);
120 attrs.push(version.major as c_int);
121 attrs.push(glx_extra::CONTEXT_MINOR_VERSION_ARB as c_int);
122 attrs.push(version.minor as c_int);
123 }
124
125 if let Some(profile) = context_attributes.profile {
126 let profile = match profile {
127 GlProfile::Core => glx_extra::CONTEXT_CORE_PROFILE_BIT_ARB,
128 GlProfile::Compatibility => glx_extra::CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
129 };
130
131 attrs.push(glx_extra::CONTEXT_PROFILE_MASK_ARB as c_int);
132 attrs.push(profile as c_int);
133 }
134
135 let mut flags: c_int = 0;
136 let mut requested_no_error = false;
137 if self.inner.features.contains(DisplayFeatures::CONTEXT_ROBUSTNESS) {
138 match context_attributes.robustness {
139 Robustness::NotRobust => (),
140 Robustness::RobustNoResetNotification => {
141 attrs.push(glx_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB as c_int);
142 attrs.push(glx_extra::NO_RESET_NOTIFICATION_ARB as c_int);
143 flags |= glx_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB as c_int;
144 },
145 Robustness::RobustLoseContextOnReset => {
146 attrs.push(glx_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB as c_int);
147 attrs.push(glx_extra::LOSE_CONTEXT_ON_RESET_ARB as c_int);
148 flags |= glx_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB as c_int;
149 },
150 Robustness::NoError => {
151 if !self.inner.features.contains(DisplayFeatures::CONTEXT_NO_ERROR) {
152 return Err(ErrorKind::NotSupported(
153 "GLX_ARB_create_context_no_error not supported",
154 )
155 .into());
156 }
157
158 attrs.push(glx_extra::CONTEXT_OPENGL_NO_ERROR_ARB as c_int);
159 attrs.push(1);
160 requested_no_error = true;
161 },
162 }
163 } else if context_attributes.robustness != Robustness::NotRobust {
164 return Err(ErrorKind::NotSupported(
165 "GLX_ARB_create_context_robustness is not supported",
166 )
167 .into());
168 }
169
170 if context_attributes.debug && !requested_no_error {
172 flags |= glx_extra::CONTEXT_DEBUG_BIT_ARB as c_int;
173 }
174
175 if flags != 0 {
176 attrs.push(glx_extra::CONTEXT_FLAGS_ARB as c_int);
177 attrs.push(flags as c_int);
178 }
179
180 if self.inner.features.contains(DisplayFeatures::CONTEXT_RELEASE_BEHAVIOR) {
182 match context_attributes.release_behavior {
183 ReleaseBehavior::Flush => (),
187 ReleaseBehavior::None => {
188 attrs.push(glx_extra::CONTEXT_RELEASE_BEHAVIOR_ARB as c_int);
189 attrs.push(glx_extra::CONTEXT_RELEASE_BEHAVIOR_NONE_ARB as c_int);
190 },
191 }
192 } else if context_attributes.release_behavior != ReleaseBehavior::Flush {
193 return Err(ErrorKind::NotSupported(
194 "flush control behavior GLX_ARB_context_flush_control",
195 )
196 .into());
197 }
198
199 attrs.push(0);
201
202 let context = super::last_glx_error(|| unsafe {
203 extra.CreateContextAttribsARB(
204 self.inner.raw.cast(),
205 *config.inner.raw,
206 shared_context,
207 1,
209 attrs.as_ptr(),
210 )
211 })?;
212
213 Ok((context, supports_surfaceless))
214 }
215
216 fn create_context_legacy(
217 &self,
218 config: &Config,
219 shared_context: GLXContext,
220 ) -> Result<GLXContext> {
221 let render_type =
222 if config.float_pixels() { glx_extra::RGBA_FLOAT_TYPE_ARB } else { glx::RGBA_TYPE };
223
224 super::last_glx_error(|| unsafe {
225 self.inner.glx.CreateNewContext(
226 self.inner.raw.cast(),
227 *config.inner.raw,
228 render_type as c_int,
229 shared_context,
230 1,
232 )
233 })
234 }
235}
236
237#[derive(Debug)]
239pub struct NotCurrentContext {
240 inner: ContextInner,
241}
242
243impl NotCurrentContext {
244 pub fn make_current_surfaceless(self) -> Result<PossiblyCurrentContext> {
249 self.inner.make_current_surfaceless()?;
250 Ok(PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData })
251 }
252
253 fn new(inner: ContextInner) -> Self {
254 Self { inner }
255 }
256}
257
258impl NotCurrentGlContext for NotCurrentContext {
259 type PossiblyCurrentContext = PossiblyCurrentContext;
260 type Surface<T: SurfaceTypeTrait> = Surface<T>;
261
262 fn treat_as_possibly_current(self) -> PossiblyCurrentContext {
263 PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData }
264 }
265
266 fn make_current<T: SurfaceTypeTrait>(
267 self,
268 surface: &Self::Surface<T>,
269 ) -> Result<Self::PossiblyCurrentContext> {
270 self.inner.make_current_draw_read(surface, surface)?;
271 Ok(PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData })
272 }
273
274 fn make_current_draw_read<T: SurfaceTypeTrait>(
275 self,
276 surface_draw: &Self::Surface<T>,
277 surface_read: &Self::Surface<T>,
278 ) -> Result<Self::PossiblyCurrentContext> {
279 self.inner.make_current_draw_read(surface_draw, surface_read)?;
280 Ok(PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData })
281 }
282}
283
284impl GlContext for NotCurrentContext {
285 fn context_api(&self) -> ContextApi {
286 self.inner.context_api()
287 }
288
289 fn priority(&self) -> Priority {
290 Priority::Medium
291 }
292}
293
294impl GetGlConfig for NotCurrentContext {
295 type Target = Config;
296
297 fn config(&self) -> Self::Target {
298 self.inner.config.clone()
299 }
300}
301
302impl GetGlDisplay for NotCurrentContext {
303 type Target = Display;
304
305 fn display(&self) -> Self::Target {
306 self.inner.display.clone()
307 }
308}
309
310impl AsRawContext for NotCurrentContext {
311 fn raw_context(&self) -> RawContext {
312 RawContext::Glx(*self.inner.raw)
313 }
314}
315
316impl Sealed for NotCurrentContext {}
317
318#[derive(Debug)]
320pub struct PossiblyCurrentContext {
321 inner: ContextInner,
322 _nosendsync: PhantomData<GLXContext>,
324}
325
326impl PossiblyCurrentContext {
327 pub fn make_current_surfaceless(&self) -> Result<()> {
331 self.inner.make_current_surfaceless()
332 }
333}
334
335impl PossiblyCurrentGlContext for PossiblyCurrentContext {
336 type NotCurrentContext = NotCurrentContext;
337 type Surface<T: SurfaceTypeTrait> = Surface<T>;
338
339 fn make_not_current(self) -> Result<Self::NotCurrentContext> {
340 self.make_not_current_in_place()?;
341 Ok(NotCurrentContext::new(self.inner))
342 }
343
344 fn make_not_current_in_place(&self) -> Result<()> {
345 self.inner.make_not_current()
346 }
347
348 fn is_current(&self) -> bool {
349 unsafe { self.inner.display.inner.glx.GetCurrentContext() == *self.inner.raw }
350 }
351
352 fn make_current<T: SurfaceTypeTrait>(&self, surface: &Self::Surface<T>) -> Result<()> {
353 self.inner.make_current_draw_read(surface, surface)
354 }
355
356 fn make_current_draw_read<T: SurfaceTypeTrait>(
357 &self,
358 surface_draw: &Self::Surface<T>,
359 surface_read: &Self::Surface<T>,
360 ) -> Result<()> {
361 self.inner.make_current_draw_read(surface_draw, surface_read)
362 }
363}
364
365impl GlContext for PossiblyCurrentContext {
366 fn context_api(&self) -> ContextApi {
367 self.inner.context_api()
368 }
369
370 fn priority(&self) -> Priority {
371 Priority::Medium
372 }
373}
374
375impl GetGlConfig for PossiblyCurrentContext {
376 type Target = Config;
377
378 fn config(&self) -> Self::Target {
379 self.inner.config.clone()
380 }
381}
382
383impl GetGlDisplay for PossiblyCurrentContext {
384 type Target = Display;
385
386 fn display(&self) -> Self::Target {
387 self.inner.display.clone()
388 }
389}
390
391impl AsRawContext for PossiblyCurrentContext {
392 fn raw_context(&self) -> RawContext {
393 RawContext::Glx(*self.inner.raw)
394 }
395}
396
397impl Sealed for PossiblyCurrentContext {}
398
399struct ContextInner {
400 display: Display,
401 config: Config,
402 raw: GlxContext,
403 is_gles: bool,
404 supports_surfaceless: bool,
405}
406
407impl ContextInner {
408 fn make_current_surfaceless(&self) -> Result<()> {
409 if !self.supports_surfaceless {
410 return Err(
411 ErrorKind::NotSupported("the surfaceless context Api isn't supported").into()
412 );
413 }
414
415 super::last_glx_error(|| unsafe {
418 self.display.inner.glx.MakeContextCurrent(
419 self.display.inner.raw.cast(),
420 0,
421 0,
422 *self.raw,
423 );
424 })
425 }
426
427 fn make_current_draw_read<T: SurfaceTypeTrait>(
428 &self,
429 surface_draw: &Surface<T>,
430 surface_read: &Surface<T>,
431 ) -> Result<()> {
432 super::last_glx_error(|| unsafe {
433 self.display.inner.glx.MakeContextCurrent(
434 self.display.inner.raw.cast(),
435 surface_draw.raw,
436 surface_read.raw,
437 *self.raw,
438 );
439 })
440 }
441
442 fn make_not_current(&self) -> Result<()> {
443 super::last_glx_error(|| unsafe {
444 self.display.inner.glx.MakeContextCurrent(
445 self.display.inner.raw.cast(),
446 0,
447 0,
448 std::ptr::null(),
449 );
450 })
451 }
452
453 fn context_api(&self) -> ContextApi {
454 if self.is_gles {
455 ContextApi::Gles(None)
456 } else {
457 ContextApi::OpenGl(None)
458 }
459 }
460}
461
462impl Drop for ContextInner {
463 fn drop(&mut self) {
464 let _ = super::last_glx_error(|| unsafe {
465 self.display.inner.glx.DestroyContext(self.display.inner.raw.cast(), *self.raw);
466 });
467 }
468}
469
470impl fmt::Debug for ContextInner {
471 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
472 f.debug_struct("Context")
473 .field("display", &self.display.inner.raw)
474 .field("config", &self.config.inner.raw)
475 .field("raw", &self.raw)
476 .finish()
477 }
478}
479
480#[derive(Debug)]
481struct GlxContext(GLXContext);
482
483unsafe impl Send for GlxContext {}
484
485impl Deref for GlxContext {
486 type Target = GLXContext;
487
488 fn deref(&self) -> &Self::Target {
489 &self.0
490 }
491}