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