|
| 1 | +use super::*; |
| 2 | + |
| 3 | +use std::marker::PhantomData; |
| 4 | +use winit::event_loop::EventLoop; |
| 5 | +use winit::window::{Window, WindowBuilder}; |
| 6 | + |
| 7 | +/// Represents an OpenGL [`Context`] and the [`Window`] with which it is |
| 8 | +/// associated. |
| 9 | +/// |
| 10 | +/// Please see [`ContextWrapper<T, Window>`]. |
| 11 | +/// |
| 12 | +/// # Example |
| 13 | +/// |
| 14 | +/// ```no_run |
| 15 | +/// # fn main() { |
| 16 | +/// let mut el = glutin::event_loop::EventLoop::new(); |
| 17 | +/// let wb = glutin::window::WindowBuilder::new(); |
| 18 | +/// let windowed_context = glutin::ContextBuilder::new() |
| 19 | +/// .build_windowed(wb, &el) |
| 20 | +/// .unwrap(); |
| 21 | +/// |
| 22 | +/// let windowed_context = unsafe { windowed_context.make_current().unwrap() }; |
| 23 | +/// # } |
| 24 | +/// ``` |
| 25 | +/// |
| 26 | +/// [`ContextWrapper<T, Window>`]: struct.ContextWrapper.html |
| 27 | +/// [`Window`]: struct.Window.html |
| 28 | +/// [`Context`]: struct.Context.html |
| 29 | +pub type WindowedContext<T> = ContextWrapper<T, Window>; |
| 30 | + |
| 31 | +/// Represents an OpenGL [`Context`] which has an underlying window that is |
| 32 | +/// stored separately. |
| 33 | +/// |
| 34 | +/// This type can only be created via one of three ways: |
| 35 | +/// |
| 36 | +/// * [`platform::unix::RawContextExt`] |
| 37 | +/// * [`platform::windows::RawContextExt`] |
| 38 | +/// * [`WindowedContext<T>::split`] |
| 39 | +/// |
| 40 | +/// Please see [`ContextWrapper<T, ()>`]. |
| 41 | +/// |
| 42 | +/// [`ContextWrapper<T, ()>`]: struct.ContextWrapper.html |
| 43 | +/// [`WindowedContext<T>::split`]: type.WindowedContext.html#method.split |
| 44 | +/// [`Context`]: struct.Context.html |
| 45 | +#[cfg_attr( |
| 46 | + target_os = "windows", |
| 47 | + doc = "\ |
| 48 | +[`platform::windows::RawContextExt`]: os/windows/enum.RawHandle.html |
| 49 | +" |
| 50 | +)] |
| 51 | +#[cfg_attr( |
| 52 | + not(target_os = "windows",), |
| 53 | + doc = "\ |
| 54 | +[`platform::windows::RawContextExt`]: os/index.html |
| 55 | +" |
| 56 | +)] |
| 57 | +#[cfg_attr( |
| 58 | + not(any( |
| 59 | + target_os = "linux", |
| 60 | + target_os = "dragonfly", |
| 61 | + target_os = "freebsd", |
| 62 | + target_os = "netbsd", |
| 63 | + target_os = "openbsd", |
| 64 | + )), |
| 65 | + doc = "\ |
| 66 | +[`platform::unix::RawContextExt`]: os/index.html |
| 67 | +" |
| 68 | +)] |
| 69 | +#[cfg_attr( |
| 70 | + any( |
| 71 | + target_os = "linux", |
| 72 | + target_os = "dragonfly", |
| 73 | + target_os = "freebsd", |
| 74 | + target_os = "netbsd", |
| 75 | + target_os = "openbsd", |
| 76 | + ), |
| 77 | + doc = "\ |
| 78 | +[`platform::unix::RawContextExt`]: os/unix/enum.RawHandle.html |
| 79 | +" |
| 80 | +)] |
| 81 | +pub type RawContext<T> = ContextWrapper<T, ()>; |
| 82 | + |
| 83 | +/// A context which has an underlying window, which may or may not be stored |
| 84 | +/// separately. |
| 85 | +/// |
| 86 | +/// If the window is stored separately, it is a [`RawContext<T>`]. Otherwise, |
| 87 | +/// it is a [`WindowedContext<T>`]. |
| 88 | +/// |
| 89 | +/// [`WindowedContext<T>`]: type.WindowedContext.html |
| 90 | +/// [`RawContext<T>`]: type.RawContext.html |
| 91 | +/// [`Context`]: struct.Context.html |
| 92 | +#[derive(Debug)] |
| 93 | +pub struct ContextWrapper<T: ContextCurrentState, W> { |
| 94 | + pub(crate) context: Context<T>, |
| 95 | + pub(crate) window: W, |
| 96 | +} |
| 97 | + |
| 98 | +impl<T: ContextCurrentState> WindowedContext<T> { |
| 99 | + /// Borrow the inner `W`. |
| 100 | + pub fn window(&self) -> &Window { |
| 101 | + &self.window |
| 102 | + } |
| 103 | + |
| 104 | + /// Split the [`Window`] apart from the OpenGL [`Context`]. Should only be |
| 105 | + /// used when intending to transfer the [`RawContext<T>`] to another |
| 106 | + /// thread. |
| 107 | + /// |
| 108 | + /// Unsaftey: |
| 109 | + /// - The OpenGL [`Context`] must be dropped before the [`Window`]. |
| 110 | + /// |
| 111 | + /// [`RawContext<T>`]: type.RawContext.html |
| 112 | + /// [`Window`]: struct.Window.html |
| 113 | + /// [`Context`]: struct.Context.html |
| 114 | + pub unsafe fn split(self) -> (RawContext<T>, Window) { |
| 115 | + ( |
| 116 | + RawContext { |
| 117 | + context: self.context, |
| 118 | + window: (), |
| 119 | + }, |
| 120 | + self.window, |
| 121 | + ) |
| 122 | + } |
| 123 | +} |
| 124 | + |
| 125 | +impl<W> ContextWrapper<PossiblyCurrent, W> { |
| 126 | + /// Swaps the buffers in case of double or triple buffering. |
| 127 | + /// |
| 128 | + /// You should call this function every time you have finished rendering, or |
| 129 | + /// the image may not be displayed on the screen. |
| 130 | + /// |
| 131 | + /// **Warning**: if you enabled vsync, this function will block until the |
| 132 | + /// next time the screen is refreshed. However drivers can choose to |
| 133 | + /// override your vsync settings, which means that you can't know in |
| 134 | + /// advance whether `swap_buffers` will block or not. |
| 135 | + pub fn swap_buffers(&self) -> Result<(), ContextError> { |
| 136 | + self.context.context.swap_buffers() |
| 137 | + } |
| 138 | + |
| 139 | + /// Returns the pixel format of the main framebuffer of the context. |
| 140 | + pub fn get_pixel_format(&self) -> PixelFormat { |
| 141 | + self.context.context.get_pixel_format() |
| 142 | + } |
| 143 | + |
| 144 | + /// Resize the context. |
| 145 | + /// |
| 146 | + /// Some platforms (macOS, Wayland) require being manually updated when |
| 147 | + /// their window or surface is resized. |
| 148 | + /// |
| 149 | + /// The easiest way of doing this is to take every [`Resized`] window event |
| 150 | + /// that is received with a [`LogicalSize`] and convert it to a |
| 151 | + /// [`PhysicalSize`] and pass it into this function. |
| 152 | + /// |
| 153 | + /// [`LogicalSize`]: dpi/struct.LogicalSize.html |
| 154 | + /// [`PhysicalSize`]: dpi/struct.PhysicalSize.html |
| 155 | + /// [`Resized`]: event/enum.WindowEvent.html#variant.Resized |
| 156 | + pub fn resize(&self, size: dpi::PhysicalSize) { |
| 157 | + let (width, height) = size.into(); |
| 158 | + self.context.context.resize(width, height); |
| 159 | + } |
| 160 | +} |
| 161 | + |
| 162 | +impl<T: ContextCurrentState, W> ContextWrapper<T, W> { |
| 163 | + /// Borrow the inner GL [`Context`]. |
| 164 | + /// |
| 165 | + /// [`Context`]: struct.Context.html |
| 166 | + pub fn context(&self) -> &Context<T> { |
| 167 | + &self.context |
| 168 | + } |
| 169 | + |
| 170 | + /// Sets this context as the current context. The previously current context |
| 171 | + /// (if any) is no longer current. |
| 172 | + /// |
| 173 | + /// A failed call to `make_current` might make this, or no context |
| 174 | + /// current. It could also keep the previous context current. What happens |
| 175 | + /// varies by platform and error. |
| 176 | + /// |
| 177 | + /// To attempt to recover and get back into a know state, either: |
| 178 | + /// |
| 179 | + /// * attempt to use [`is_current`] to find the new current context; or |
| 180 | + /// * call [`make_not_current`] on both the previously |
| 181 | + /// current context and this context. |
| 182 | + /// |
| 183 | + /// # An higher level overview. |
| 184 | + /// |
| 185 | + /// In OpenGl, only a single context can be current in a thread at a time. |
| 186 | + /// Making a new context current will make the old one not current. |
| 187 | + /// Contexts can only be sent to different threads if they are not current. |
| 188 | + /// |
| 189 | + /// If you call `make_current` on some context, you should call |
| 190 | + /// [`treat_as_not_current`] as soon as possible on the previously current |
| 191 | + /// context. |
| 192 | + /// |
| 193 | + /// If you wish to move a currently current context to a different thread, |
| 194 | + /// you should do one of two options: |
| 195 | + /// |
| 196 | + /// * Call `make_current` on another context, then call |
| 197 | + /// [`treat_as_not_current`] on this context. |
| 198 | + /// * Call [`make_not_current`] on this context. |
| 199 | + /// |
| 200 | + /// If you are aware of what context you intend to make current next, it is |
| 201 | + /// preferable for performance reasons to call `make_current` on that |
| 202 | + /// context, then [`treat_as_not_current`] on this context. |
| 203 | + /// |
| 204 | + /// If you are not aware of what context you intend to make current next, |
| 205 | + /// consider waiting until you do. If you need this context not current |
| 206 | + /// immediately (e.g. to transfer it to another thread), then call |
| 207 | + /// [`make_not_current`] on this context. |
| 208 | + /// |
| 209 | + /// Please avoid calling [`make_not_current`] on one context only to call |
| 210 | + /// `make_current` on another context before and/or after. This hurts |
| 211 | + /// performance by requiring glutin to: |
| 212 | + /// |
| 213 | + /// * Check if this context is current; then |
| 214 | + /// * If it is, change the current context from this context to none; then |
| 215 | + /// * Change the current context from none to the new context. |
| 216 | + /// |
| 217 | + /// Instead prefer the method we mentioned above with `make_current` and |
| 218 | + /// [`treat_as_not_current`]. |
| 219 | + /// |
| 220 | + /// [`make_not_current`]: struct.ContextWrapper.html#method.make_not_current |
| 221 | + /// [`treat_as_not_current`]: |
| 222 | + /// struct.ContextWrapper.html#method.treat_as_not_current |
| 223 | + /// [`is_current`]: struct.ContextWrapper.html#method.is_current |
| 224 | + pub unsafe fn make_current( |
| 225 | + self, |
| 226 | + ) -> Result<ContextWrapper<PossiblyCurrent, W>, (Self, ContextError)> { |
| 227 | + let window = self.window; |
| 228 | + match self.context.make_current() { |
| 229 | + Ok(context) => Ok(ContextWrapper { window, context }), |
| 230 | + Err((context, err)) => { |
| 231 | + Err((ContextWrapper { window, context }, err)) |
| 232 | + } |
| 233 | + } |
| 234 | + } |
| 235 | + |
| 236 | + /// If this context is current, makes this context not current. If this |
| 237 | + /// context is not current however, this function does nothing. |
| 238 | + /// |
| 239 | + /// Please see [`make_current`]. |
| 240 | + /// |
| 241 | + /// [`make_current`]: struct.ContextWrapper.html#method.make_current |
| 242 | + pub unsafe fn make_not_current( |
| 243 | + self, |
| 244 | + ) -> Result<ContextWrapper<NotCurrent, W>, (Self, ContextError)> { |
| 245 | + let window = self.window; |
| 246 | + match self.context.make_not_current() { |
| 247 | + Ok(context) => Ok(ContextWrapper { window, context }), |
| 248 | + Err((context, err)) => { |
| 249 | + Err((ContextWrapper { window, context }, err)) |
| 250 | + } |
| 251 | + } |
| 252 | + } |
| 253 | + |
| 254 | + /// Treats this context as not current, even if it is current. We do no |
| 255 | + /// checks to confirm that this is actually case. |
| 256 | + /// |
| 257 | + /// If unsure whether or not this context is current, please use |
| 258 | + /// [`make_not_current`] which will do nothing if this context is not |
| 259 | + /// current. |
| 260 | + /// |
| 261 | + /// Please see [`make_current`]. |
| 262 | + /// |
| 263 | + /// [`make_not_current`]: struct.ContextWrapper.html#method.make_not_current |
| 264 | + /// [`make_current`]: struct.ContextWrapper.html#method.make_current |
| 265 | + pub unsafe fn treat_as_not_current(self) -> ContextWrapper<NotCurrent, W> { |
| 266 | + ContextWrapper { |
| 267 | + context: self.context.treat_as_not_current(), |
| 268 | + window: self.window, |
| 269 | + } |
| 270 | + } |
| 271 | + |
| 272 | + /// Treats this context as current, even if it is not current. We do no |
| 273 | + /// checks to confirm that this is actually case. |
| 274 | + /// |
| 275 | + /// This function should only be used if you intend to track context |
| 276 | + /// currency without the limited aid of glutin, and you wish to store |
| 277 | + /// all the [`Context`]s as [`NotCurrent`]. |
| 278 | + /// |
| 279 | + /// Please see [`make_current`] for the prefered method of handling context |
| 280 | + /// currency. |
| 281 | + /// |
| 282 | + /// [`make_current`]: struct.ContextWrapper.html#method.make_current |
| 283 | + /// [`NotCurrent`]: enum.NotCurrent.html |
| 284 | + /// [`Context`]: struct.Context.html |
| 285 | + pub unsafe fn treat_as_current(self) -> ContextWrapper<PossiblyCurrent, W> { |
| 286 | + ContextWrapper { |
| 287 | + context: self.context.treat_as_current(), |
| 288 | + window: self.window, |
| 289 | + } |
| 290 | + } |
| 291 | + |
| 292 | + /// Returns true if this context is the current one in this thread. |
| 293 | + pub fn is_current(&self) -> bool { |
| 294 | + self.context.is_current() |
| 295 | + } |
| 296 | + |
| 297 | + /// Returns the OpenGL API being used. |
| 298 | + pub fn get_api(&self) -> Api { |
| 299 | + self.context.get_api() |
| 300 | + } |
| 301 | +} |
| 302 | + |
| 303 | +impl<W> ContextWrapper<PossiblyCurrent, W> { |
| 304 | + /// Returns the address of an OpenGL function. |
| 305 | + pub fn get_proc_address(&self, addr: &str) -> *const () { |
| 306 | + self.context.get_proc_address(addr) |
| 307 | + } |
| 308 | +} |
| 309 | + |
| 310 | +impl<T: ContextCurrentState, W> std::ops::Deref for ContextWrapper<T, W> { |
| 311 | + type Target = Context<T>; |
| 312 | + fn deref(&self) -> &Self::Target { |
| 313 | + &self.context |
| 314 | + } |
| 315 | +} |
| 316 | + |
| 317 | +impl<'a, T: ContextCurrentState> ContextBuilder<'a, T> { |
| 318 | + /// Builds the given window along with the associated GL context, returning |
| 319 | + /// the pair as a [`WindowedContext<T>`]. |
| 320 | + /// |
| 321 | + /// Errors can occur in two scenarios: |
| 322 | + /// - If the window could not be created (via permission denied, |
| 323 | + /// incompatible system, out of memory, etc.). This should be very rare. |
| 324 | + /// - If the OpenGL [`Context`] could not be created. This generally |
| 325 | + /// happens |
| 326 | + /// because the underlying platform doesn't support a requested feature. |
| 327 | + /// |
| 328 | + /// [`WindowedContext<T>`]: type.WindowedContext.html |
| 329 | + /// [`Context`]: struct.Context.html |
| 330 | + pub fn build_windowed<TE>( |
| 331 | + self, |
| 332 | + wb: WindowBuilder, |
| 333 | + el: &EventLoop<TE>, |
| 334 | + ) -> Result<WindowedContext<NotCurrent>, CreationError> { |
| 335 | + let ContextBuilder { pf_reqs, gl_attr } = self; |
| 336 | + let gl_attr = gl_attr.map_sharing(|ctx| &ctx.context); |
| 337 | + platform_impl::Context::new_windowed(wb, el, &pf_reqs, &gl_attr).map( |
| 338 | + |(window, context)| WindowedContext { |
| 339 | + window, |
| 340 | + context: Context { |
| 341 | + context, |
| 342 | + phantom: PhantomData, |
| 343 | + }, |
| 344 | + }, |
| 345 | + ) |
| 346 | + } |
| 347 | +} |
0 commit comments