glutin/api/glx/
mod.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
//! GLX platform Api.
#![allow(clippy::unnecessary_cast)] // needed for 32bit & 64bit support

use std::ffi::{self, CStr, CString};
use std::ops::{Deref, DerefMut};
use std::sync::atomic::{AtomicBool, AtomicI32, Ordering};
use std::sync::Mutex;

use libloading::Library;
use once_cell::sync::Lazy;
use x11_dl::xlib::{self, XErrorEvent};

use glutin_glx_sys::{glx, glx_extra};

use crate::error::{Error, ErrorKind, Result};
use crate::lib_loading::{SymLoading, SymWrapper};
use crate::platform::x11::XLIB;

pub mod config;
pub mod context;
pub mod display;
pub mod surface;

/// When using Xlib we need to get errors from it somehow, however creating
/// inner `XDisplay` to handle that or change the error hook is unsafe in
/// multithreaded applications, given that error hook is per process and not
/// connection.
///
/// The hook registrar must call to the function inside xlib error
/// [`handler`].
///
/// The `bool` value returned by that hook tells whether the error was handled
/// by it or not. So when it returns `true` it means that your error handling
/// routine shouldn't handle the error as it was handled by the hook already.
///
/// [`handler`]: https://tronche.com/gui/x/xlib/event-handling/protocol-errors/XSetErrorHandler.html
pub type XlibErrorHookRegistrar =
    Box<dyn Fn(Box<dyn Fn(*mut ffi::c_void, *mut ffi::c_void) -> bool + Send + Sync>)>;

/// The base used for GLX errors.
static GLX_BASE_ERROR: AtomicI32 = AtomicI32::new(0);

/// The last error arrived from GLX normalized by `GLX_BASE_ERROR`.
static LAST_GLX_ERROR: Lazy<Mutex<Option<Error>>> = Lazy::new(|| Mutex::new(None));

/// Whether we're in the process of getting GLX error. Otherwise we may handle
/// the winit's error.
static SYNCING_GLX_ERROR: AtomicBool = AtomicBool::new(false);

static GLX: Lazy<Option<Glx>> = Lazy::new(|| {
    let paths = ["libGL.so.1", "libGL.so"];

    unsafe { SymWrapper::new(&paths).map(Glx).ok() }
});

static GLX_EXTRA: Lazy<Option<GlxExtra>> = Lazy::new(|| {
    let glx = GLX.as_ref()?;
    Some(GlxExtra::new(glx))
});

/// GLX interface.
#[allow(missing_debug_implementations)]
pub struct Glx(pub SymWrapper<glx::Glx>);

unsafe impl Sync for Glx {}
unsafe impl Send for Glx {}

impl SymLoading for glx::Glx {
    unsafe fn load_with(lib: &Library) -> Self {
        Self::load_with(|sym| unsafe {
            lib.get(CString::new(sym.as_bytes()).unwrap().as_bytes_with_nul())
                .map(|sym| *sym)
                .unwrap_or(std::ptr::null_mut())
        })
    }
}

impl Deref for Glx {
    type Target = glx::Glx;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl DerefMut for Glx {
    #[inline]
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}

pub(crate) struct GlxExtra(glx_extra::Glx);

unsafe impl Sync for GlxExtra {}
unsafe impl Send for GlxExtra {}

impl GlxExtra {
    #[inline]
    pub fn new(glx: &Glx) -> Self {
        GlxExtra(glx_extra::Glx::load_with(|proc_name| {
            let c_str = CString::new(proc_name).unwrap();
            unsafe { glx.GetProcAddress(c_str.as_ptr() as *const u8) as *const _ }
        }))
    }
}

impl Deref for GlxExtra {
    type Target = glx_extra::Glx;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl DerefMut for GlxExtra {
    #[inline]
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}
/// Store the last error received from the GLX.
fn glx_error_hook(_display: *mut ffi::c_void, xerror_event: *mut ffi::c_void) -> bool {
    // In case we've not forced the sync, ignore the error.
    if !SYNCING_GLX_ERROR.load(Ordering::Relaxed) {
        return false;
    }

    let xerror = xerror_event as *mut XErrorEvent;
    unsafe {
        let code = (*xerror).error_code;
        let glx_code = code as i32 - GLX_BASE_ERROR.load(Ordering::Relaxed);

        // Get the kind of the error.
        let kind = match code as u8 {
            xlib::BadValue => ErrorKind::BadAttribute,
            xlib::BadMatch => ErrorKind::BadMatch,
            xlib::BadWindow => ErrorKind::BadNativeWindow,
            xlib::BadAlloc => ErrorKind::OutOfMemory,
            xlib::BadPixmap => ErrorKind::BadPixmap,
            xlib::BadAccess => ErrorKind::BadAccess,
            _ if glx_code >= 0 => match glx_code as glx::types::GLenum {
                glx::PROTO_BAD_CONTEXT => ErrorKind::BadContext,
                glx::PROTO_BAD_CONTEXT_STATE => ErrorKind::BadContext,
                glx::PROTO_BAD_CURRENT_DRAWABLE => ErrorKind::BadCurrentSurface,
                glx::PROTO_BAD_CURRENT_WINDOW => ErrorKind::BadCurrentSurface,
                glx::PROTO_BAD_FBCONFIG => ErrorKind::BadConfig,
                glx::PROTO_BAD_PBUFFER => ErrorKind::BadPbuffer,
                glx::PROTO_BAD_PIXMAP => ErrorKind::BadPixmap,
                glx::PROTO_UNSUPPORTED_PRIVATE_REQUEST => ErrorKind::Misc,
                glx::PROTO_BAD_DRAWABLE => ErrorKind::BadSurface,
                glx::PROTO_BAD_WINDOW => ErrorKind::BadSurface,
                glx::PROTO_BAD_CONTEXT_TAG => ErrorKind::Misc,
                glx::PROTO_BAD_RENDER_REQUEST => ErrorKind::Misc,
                glx::PROTO_BAD_LARGE_REQUEST => ErrorKind::Misc,
                _ => return false,
            },
            _ => return false,
        };

        // Get the string from X11 error.
        let mut buf = vec![0u8; 1024];
        (XLIB.as_ref().unwrap().XGetErrorText)(
            _display as *mut _,
            (*xerror).error_code as _,
            buf.as_mut_ptr() as *mut _,
            buf.len() as _,
        );
        let description = CStr::from_ptr(buf.as_ptr() as *const _).to_string_lossy().to_string();

        *LAST_GLX_ERROR.lock().unwrap() =
            Some(Error::new(Some(code as _), Some(description), kind));

        true
    }
}

/// Prevent error being overwritten when accessing the handler from the multiple
/// threads.
static ERROR_SECTION_LOCK: Mutex<()> = Mutex::new(());

/// Get the error from the X11.
///
/// XXX mesa and I'd guess other GLX implementations, send the error, by taking
/// the Xlib Error handling hook, getting the current hook, and calling back to
/// the user, meaning that no `XSync` should be done.
fn last_glx_error<T, F: FnOnce() -> T>(callback: F) -> Result<T> {
    let _guard = ERROR_SECTION_LOCK.lock().unwrap();

    // Mark that we're syncing the error.
    SYNCING_GLX_ERROR.store(true, Ordering::Relaxed);

    // Execute the user routine that may produce GLX error.
    let result = callback();

    // XXX We might want to XSync here in addition, because what mesa is doing might
    // not be common, but I'd assume that what mesa doing is common.

    // Reset and report last error.
    let result = match LAST_GLX_ERROR.lock().unwrap().take() {
        Some(error) => Err(error),
        None => Ok(result),
    };

    // Release the mark.
    SYNCING_GLX_ERROR.store(false, Ordering::Relaxed);

    result
}