#![allow(unreachable_patterns)]
use std::ffi;
use raw_window_handle::RawWindowHandle;
use crate::config::{Config, GetGlConfig};
use crate::display::{Display, GetGlDisplay};
use crate::error::Result;
use crate::private::{gl_api_dispatch, Sealed};
use crate::surface::{GlSurface, Surface, SurfaceTypeTrait};
#[cfg(cgl_backend)]
use crate::api::cgl::context::{
NotCurrentContext as NotCurrentCglContext, PossiblyCurrentContext as PossiblyCurrentCglContext,
};
#[cfg(egl_backend)]
use crate::api::egl::context::{
NotCurrentContext as NotCurrentEglContext, PossiblyCurrentContext as PossiblyCurrentEglContext,
};
#[cfg(glx_backend)]
use crate::api::glx::context::{
NotCurrentContext as NotCurrentGlxContext, PossiblyCurrentContext as PossiblyCurrentGlxContext,
};
#[cfg(wgl_backend)]
use crate::api::wgl::context::{
NotCurrentContext as NotCurrentWglContext, PossiblyCurrentContext as PossiblyCurrentWglContext,
};
pub trait GlContext: Sealed {
fn context_api(&self) -> ContextApi;
}
pub trait NotCurrentGlContext: Sealed {
type PossiblyCurrentContext: PossiblyCurrentGlContext;
fn treat_as_possibly_current(self) -> Self::PossiblyCurrentContext;
}
pub trait NotCurrentGlContextSurfaceAccessor<T: SurfaceTypeTrait>: Sealed {
type Surface: GlSurface<T>;
type PossiblyCurrentContext: PossiblyCurrentGlContext;
fn make_current(self, surface: &Self::Surface) -> Result<Self::PossiblyCurrentContext>;
fn make_current_draw_read(
self,
surface_draw: &Self::Surface,
surface_read: &Self::Surface,
) -> Result<Self::PossiblyCurrentContext>;
}
pub trait PossiblyCurrentGlContext: Sealed {
type NotCurrentContext: NotCurrentGlContext;
fn is_current(&self) -> bool;
fn make_not_current(self) -> Result<Self::NotCurrentContext>;
}
pub trait PossiblyCurrentContextGlSurfaceAccessor<T: SurfaceTypeTrait>: Sealed {
type Surface: GlSurface<T>;
fn make_current(&self, surface: &Self::Surface) -> Result<()>;
fn make_current_draw_read(
&self,
surface_draw: &Self::Surface,
surface_read: &Self::Surface,
) -> Result<()>;
}
pub trait AsRawContext {
fn raw_context(&self) -> RawContext;
}
#[derive(Default, Debug, Clone)]
pub struct ContextAttributesBuilder {
attributes: ContextAttributes,
}
impl ContextAttributesBuilder {
pub fn new() -> Self {
Default::default()
}
pub fn with_debug(mut self, debug: bool) -> Self {
self.attributes.debug = debug;
self
}
pub fn with_sharing(mut self, context: &impl AsRawContext) -> Self {
self.attributes.shared_context = Some(context.raw_context());
self
}
pub fn with_robustness(mut self, robustness: Robustness) -> Self {
self.attributes.robustness = robustness;
self
}
pub fn with_release_behavior(mut self, release_behavior: ReleaseBehavior) -> Self {
self.attributes.release_behavior = release_behavior;
self
}
pub fn with_profile(mut self, profile: GlProfile) -> Self {
self.attributes.profile = Some(profile);
self
}
pub fn with_context_api(mut self, api: ContextApi) -> Self {
self.attributes.api = Some(api);
self
}
pub fn build(mut self, raw_window_handle: Option<RawWindowHandle>) -> ContextAttributes {
self.attributes.raw_window_handle = raw_window_handle;
self.attributes
}
}
#[derive(Default, Debug, Clone)]
pub struct ContextAttributes {
pub(crate) release_behavior: ReleaseBehavior,
pub(crate) debug: bool,
pub(crate) robustness: Robustness,
pub(crate) profile: Option<GlProfile>,
pub(crate) api: Option<ContextApi>,
pub(crate) shared_context: Option<RawContext>,
pub(crate) raw_window_handle: Option<RawWindowHandle>,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Robustness {
NotRobust,
NoError,
RobustNoResetNotification,
RobustLoseContextOnReset,
}
impl Default for Robustness {
#[inline]
fn default() -> Self {
Robustness::NotRobust
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GlProfile {
Core,
Compatibility,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ContextApi {
OpenGl(Option<Version>),
Gles(Option<Version>),
}
#[cfg(any(egl_backend, glx_backend, wgl_backend))]
impl ContextApi {
pub(crate) fn version(&self) -> Option<Version> {
match self {
Self::OpenGl(version) => *version,
Self::Gles(version) => *version,
_ => None,
}
}
}
impl Default for ContextApi {
fn default() -> Self {
Self::OpenGl(None)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct Version {
pub major: u8,
pub minor: u8,
}
impl Version {
pub const fn new(major: u8, minor: u8) -> Self {
Self { major, minor }
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum ReleaseBehavior {
None,
Flush,
}
impl Default for ReleaseBehavior {
#[inline]
fn default() -> Self {
ReleaseBehavior::Flush
}
}
#[derive(Debug)]
pub enum NotCurrentContext {
#[cfg(egl_backend)]
Egl(NotCurrentEglContext),
#[cfg(glx_backend)]
Glx(NotCurrentGlxContext),
#[cfg(wgl_backend)]
Wgl(NotCurrentWglContext),
#[cfg(cgl_backend)]
Cgl(NotCurrentCglContext),
}
impl NotCurrentGlContext for NotCurrentContext {
type PossiblyCurrentContext = PossiblyCurrentContext;
fn treat_as_possibly_current(self) -> Self::PossiblyCurrentContext {
gl_api_dispatch!(self; Self(context) => context.treat_as_possibly_current(); as PossiblyCurrentContext)
}
}
impl<T: SurfaceTypeTrait> NotCurrentGlContextSurfaceAccessor<T> for NotCurrentContext {
type PossiblyCurrentContext = PossiblyCurrentContext;
type Surface = Surface<T>;
fn make_current(self, surface: &Self::Surface) -> Result<Self::PossiblyCurrentContext> {
match (self, surface) {
#[cfg(egl_backend)]
(Self::Egl(context), Surface::Egl(surface)) => {
Ok(PossiblyCurrentContext::Egl(context.make_current(surface)?))
},
#[cfg(glx_backend)]
(Self::Glx(context), Surface::Glx(surface)) => {
Ok(PossiblyCurrentContext::Glx(context.make_current(surface)?))
},
#[cfg(wgl_backend)]
(Self::Wgl(context), Surface::Wgl(surface)) => {
Ok(PossiblyCurrentContext::Wgl(context.make_current(surface)?))
},
#[cfg(cgl_backend)]
(Self::Cgl(context), Surface::Cgl(surface)) => {
Ok(PossiblyCurrentContext::Cgl(context.make_current(surface)?))
},
_ => unreachable!(),
}
}
fn make_current_draw_read(
self,
surface_draw: &Self::Surface,
surface_read: &Self::Surface,
) -> Result<Self::PossiblyCurrentContext> {
match (self, surface_draw, surface_read) {
#[cfg(egl_backend)]
(Self::Egl(context), Surface::Egl(draw), Surface::Egl(read)) => {
Ok(PossiblyCurrentContext::Egl(context.make_current_draw_read(draw, read)?))
},
#[cfg(glx_backend)]
(Self::Glx(context), Surface::Glx(draw), Surface::Glx(read)) => {
Ok(PossiblyCurrentContext::Glx(context.make_current_draw_read(draw, read)?))
},
#[cfg(wgl_backend)]
(Self::Wgl(context), Surface::Wgl(draw), Surface::Wgl(read)) => {
Ok(PossiblyCurrentContext::Wgl(context.make_current_draw_read(draw, read)?))
},
#[cfg(cgl_backend)]
(Self::Cgl(context), Surface::Cgl(draw), Surface::Cgl(read)) => {
Ok(PossiblyCurrentContext::Cgl(context.make_current_draw_read(draw, read)?))
},
_ => unreachable!(),
}
}
}
impl GlContext for NotCurrentContext {
fn context_api(&self) -> ContextApi {
gl_api_dispatch!(self; Self(context) => context.context_api())
}
}
impl GetGlConfig for NotCurrentContext {
type Target = Config;
fn config(&self) -> Self::Target {
gl_api_dispatch!(self; Self(context) => context.config(); as Config)
}
}
impl GetGlDisplay for NotCurrentContext {
type Target = Display;
fn display(&self) -> Self::Target {
gl_api_dispatch!(self; Self(context) => context.display(); as Display)
}
}
impl AsRawContext for NotCurrentContext {
fn raw_context(&self) -> RawContext {
gl_api_dispatch!(self; Self(context) => context.raw_context())
}
}
impl Sealed for NotCurrentContext {}
#[derive(Debug)]
pub enum PossiblyCurrentContext {
#[cfg(egl_backend)]
Egl(PossiblyCurrentEglContext),
#[cfg(glx_backend)]
Glx(PossiblyCurrentGlxContext),
#[cfg(wgl_backend)]
Wgl(PossiblyCurrentWglContext),
#[cfg(cgl_backend)]
Cgl(PossiblyCurrentCglContext),
}
impl PossiblyCurrentGlContext for PossiblyCurrentContext {
type NotCurrentContext = NotCurrentContext;
fn is_current(&self) -> bool {
gl_api_dispatch!(self; Self(context) => context.is_current())
}
fn make_not_current(self) -> Result<Self::NotCurrentContext> {
Ok(
gl_api_dispatch!(self; Self(context) => context.make_not_current()?; as NotCurrentContext),
)
}
}
impl<T: SurfaceTypeTrait> PossiblyCurrentContextGlSurfaceAccessor<T> for PossiblyCurrentContext {
type Surface = Surface<T>;
fn make_current(&self, surface: &Self::Surface) -> Result<()> {
match (self, surface) {
#[cfg(egl_backend)]
(Self::Egl(context), Surface::Egl(surface)) => context.make_current(surface),
#[cfg(glx_backend)]
(Self::Glx(context), Surface::Glx(surface)) => context.make_current(surface),
#[cfg(wgl_backend)]
(Self::Wgl(context), Surface::Wgl(surface)) => context.make_current(surface),
#[cfg(cgl_backend)]
(Self::Cgl(context), Surface::Cgl(surface)) => context.make_current(surface),
_ => unreachable!(),
}
}
fn make_current_draw_read(
&self,
surface_draw: &Self::Surface,
surface_read: &Self::Surface,
) -> Result<()> {
match (self, surface_draw, surface_read) {
#[cfg(egl_backend)]
(Self::Egl(context), Surface::Egl(draw), Surface::Egl(read)) => {
context.make_current_draw_read(draw, read)
},
#[cfg(glx_backend)]
(Self::Glx(context), Surface::Glx(draw), Surface::Glx(read)) => {
context.make_current_draw_read(draw, read)
},
#[cfg(wgl_backend)]
(Self::Wgl(context), Surface::Wgl(draw), Surface::Wgl(read)) => {
context.make_current_draw_read(draw, read)
},
#[cfg(cgl_backend)]
(Self::Cgl(context), Surface::Cgl(draw), Surface::Cgl(read)) => {
context.make_current_draw_read(draw, read)
},
_ => unreachable!(),
}
}
}
impl GlContext for PossiblyCurrentContext {
fn context_api(&self) -> ContextApi {
gl_api_dispatch!(self; Self(context) => context.context_api())
}
}
impl GetGlConfig for PossiblyCurrentContext {
type Target = Config;
fn config(&self) -> Self::Target {
gl_api_dispatch!(self; Self(context) => context.config(); as Config)
}
}
impl GetGlDisplay for PossiblyCurrentContext {
type Target = Display;
fn display(&self) -> Self::Target {
gl_api_dispatch!(self; Self(context) => context.display(); as Display)
}
}
impl AsRawContext for PossiblyCurrentContext {
fn raw_context(&self) -> RawContext {
gl_api_dispatch!(self; Self(context) => context.raw_context())
}
}
impl Sealed for PossiblyCurrentContext {}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RawContext {
#[cfg(egl_backend)]
Egl(*const ffi::c_void),
#[cfg(glx_backend)]
Glx(*const ffi::c_void),
#[cfg(wgl_backend)]
Wgl(*const ffi::c_void),
#[cfg(cgl_backend)]
Cgl(*const ffi::c_void),
}
#[cfg(any(egl_backend, glx_backend, wgl_backend))]
pub(crate) fn pick_profile(
profile: Option<GlProfile>,
version: Option<Version>,
) -> (GlProfile, Version) {
match (profile, version) {
(Some(GlProfile::Core), Some(version)) => (GlProfile::Core, version),
(Some(GlProfile::Compatibility), Some(version)) => (GlProfile::Compatibility, version),
(None, Some(version)) if version >= Version::new(3, 3) => (GlProfile::Core, version),
(None, Some(version)) => (GlProfile::Compatibility, version),
(Some(GlProfile::Core), None) => (GlProfile::Core, Version::new(3, 3)),
(Some(GlProfile::Compatibility), None) => (GlProfile::Compatibility, Version::new(2, 1)),
(None, None) => (GlProfile::Core, Version::new(3, 3)),
}
}